惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
N
News and Events Feed by Topic
D
DataBreaches.Net
MongoDB | Blog
MongoDB | Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Engineering at Meta
Engineering at Meta
T
Tailwind CSS Blog
博客园_首页
Microsoft Azure Blog
Microsoft Azure Blog
Y
Y Combinator Blog
博客园 - Franky
Hugging Face - Blog
Hugging Face - Blog
月光博客
月光博客
A
About on SuperTechFans
I
InfoQ
S
Securelist
Last Week in AI
Last Week in AI
S
Schneier on Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
Hacker News: Ask HN
Hacker News: Ask HN
Schneier on Security
Schneier on Security
Know Your Adversary
Know Your Adversary
腾讯CDC
大猫的无限游戏
大猫的无限游戏
S
Security @ Cisco Blogs
博客园 - 三生石上(FineUI控件)
Simon Willison's Weblog
Simon Willison's Weblog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
Tor Project blog
美团技术团队
aimingoo的专栏
aimingoo的专栏
G
Google Developers Blog
罗磊的独立博客
Vercel News
Vercel News
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
The Cloudflare Blog
S
Secure Thoughts
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Latest news
Latest news
Recent Announcements
Recent Announcements
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
L
LINUX DO - 热门话题
Security Latest
Security Latest
TaoSecurity Blog
TaoSecurity Blog
Cyberwarzone
Cyberwarzone
有赞技术团队
有赞技术团队

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
API Versioning: Current Approaches and Choices in the Ecosystem
Mustafa ERBA · 2026-05-17 · via DEV Community

The moment you launch an API, one of the biggest nightmares associated with it is versioning decisions. Especially when you consider that this API will be used by different clients or is expected to have a long lifespan, things become even more complex. Looking back at the trouble I got myself into by underestimating this topic in the past, my pragmatic approach today is rooted in those painful experiences.

API versioning is one of the key ways an application can evolve over time while ensuring that existing clients are not disrupted. In a production ERP system I worked on, the painful lesson of 20 different operator screens suddenly throwing errors due to a minor change in a JSON field name taught me how critical this issue is. In this post, I will discuss API versioning approaches, the experiences I've gained in my projects, and the trade-offs these choices bring.

Why is API Versioning Necessary? My First Wrong Choices

When developing an API, everything initially goes through a single version, and it's easy. However, over time, business requirements change, new features are added, and old features are updated or removed. These changes directly affect the applications consuming the API (mobile apps, web frontends, other services) and are called breaking changes. A breaking change causes an application using the API to stop working without code modifications.

In my early projects, I overlooked this. While developing the backend for a financial calculator side project, I didn't implement versioning with the thought, "I'm the only one using it, so it's not necessary." A few months later, while adding a new feature, I had to change the data format my existing mobile app received from the backend. The result: I had to deploy both the backend and the mobile app simultaneously and wait for users to update their applications. This was unacceptable for a system with zero downtime expectations.

⚠️ Proven by Experience: Versioning is Essential

Versioning is critical not only when your API is exposed externally but also in internal inter-service communication. If one service uses another service's API, a breaking change can cause the other service to break, leading to hours of downtime in the production environment.

These early mistakes taught me that versioning is not just a "technical detail" but also a matter of product strategy and operational flexibility. It is essential to define a versioning strategy from the outset to extend the life of your API and avoid inconveniencing your clients. Otherwise, we face a significant regression risk with every change.

Main API Versioning Approaches: Pros and Cons

There are three main approaches at the core of API versioning: URL Path, Query Parameter, and Header (Content Negotiation). Each has its own advantages and disadvantages, and choosing the right one depends on the project's needs. I've also experienced different approaches in various projects and seen when each works and when it causes headaches.

Approach Pros Cons When I Preferred It
URL Path - Simple, understandable, discoverable - Can lead to URL bloat - Public APIs, simple projects (my own side projects)
Query Parameter - Keeps URLs relatively clean - Open to misuse, low discoverability - Rarely, usually as a "fallback"
Header (Custom) - Keeps URLs cleanest - Low discoverability, requires client support - Internal APIs, places with strict control (ERP)
Header (Accept) - Compliant with HTTP, Content Negotiation - Client implementation more complex - When different output formats are required

As a general rule, for external and public APIs, I've usually preferred the URL Path method because its simplicity and understandability provide the least friction for external developers. For internal APIs, I can opt for Header-based approaches that are more flexible and keep URLs clean.

URL Path Versioning: Direct and Understandable

URL Path versioning is perhaps the most common and understandable method. You include the API's version number directly in the URL path, for example, /v1/users or /api/v2/products. This method can be easily tested even in a browser, and its documentation is straightforward.

When developing a production ERP system, I used this method for a few integration APIs exposed externally. Our clients could clearly see which version they were calling from the URL. However, when I kept two different versions live simultaneously, I started seeing if version == "v1" blocks in the backend code, which was not a pleasant situation in terms of code quality. We can make this situation a bit more manageable with a reverse proxy like Nginx.

# Nginx config example
server {
    listen 80;
    server_name api.example.com;

    location ~ ^/(v1|v2)/ {
        # Routing for v1 or v2 paths
        proxy_pass http://backend_api_cluster/$request_uri;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location /v1/ {
        # Specific backend or logic for v1
        proxy_pass http://v1_backend_cluster;
        proxy_set_header Host $host;
    }

    location /v2/ {
        # Specific backend or logic for v2
        proxy_pass http://v2_backend_cluster;
        proxy_set_header Host $host;
    }
}

Enter fullscreen mode Exit fullscreen mode

In the Nginx example above, you can route different versions to different backends. This gives me the flexibility to deploy different versions independently and allows me to develop new versions independently while supporting older versions. However, this approach leads to longer URLs and the need to define a new path for each new version. It's inevitable that URLs will "bloat" as they go from v3, v4, and so on.

Header and Content Negotiation Versioning: More Flexible Methods

Header-based versioning specifies the API version via HTTP Request Headers. This keeps URLs clean and ensures that the API's base resource URI does not change. For example, we can use a custom header like X-API-Version: 2.

Versioning using Content Negotiation is a more standard HTTP approach. By using the Accept header, the client indicates that it is requesting a specific media type or API version. For instance, with a value like Accept: application/vnd.myapi.v2+json, the client is saying "I want the 2nd version of myapi in JSON format." I used this approach in a client project where the same endpoint needed to work with different data models.

# FastAPI example: Versioning with Accept header
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse

app = FastAPI()

@app.get("/items")
async def get_items(request: Request):
    accept_header = request.headers.get("Accept")

    if "application/vnd.myapi.v1+json" in accept_header:
        # Return data according to v1 model
        return JSONResponse({"version": "v1", "data": [{"id": 1, "name": "Item A"}]})
    elif "application/vnd.myapi.v2+json" in accept_header:
        # Return data according to v2 model (e.g., with an additional field)
        return JSONResponse({"version": "v2", "data": [{"uuid": "abc", "item_name": "Item A", "price": 100}]})
    else:
        raise HTTPException(status_code=406, detail="Unsupported Accept header")

Enter fullscreen mode Exit fullscreen mode

This approach has been useful, especially in internal service-to-service communication or with tightly controlled clients like mobile applications. When developers correctly set the Accept header, it was clear which version of the API they were using. However, it is less discoverable than URL Path; for someone using the API for the first time, setting the header correctly requires reading a bit more documentation. Also, some client libraries or tools may not automatically support such custom headers or Content Negotiation, which means additional development costs.

My Versioning Strategy Choices in Projects

The choice of API versioning strategy varies depending on the nature of the project and its target audience. In my experience, there is no "single right way"; there are always trade-offs. The decisions I've made in my own side projects or in internal platforms of banks I've consulted for differ from those made in a production ERP system where I gained experience.

For example, for the public API of my financial calculator side project, I preferred URL Path versioning. The reason was simple: to enable external developers to use and integrate with the API easily. A simple URL like /v1/calculate speeds up the onboarding process, compared to a complex header structure. Currently, I maintain both v1 and v2 versions live and ensure that older clients using v1 continue to work without issues. However, this comes with the burden of maintaining the code for both versions in the backend.

On the other hand, for services I developed for a bank's internal platform, I versioned the APIs via the Accept header. On this platform, since the clients were typically enterprise applications, developers had a higher ability to manage HTTP headers correctly. Additionally, keeping the URLs clean provided a more organized view for the bank's security and audit teams. In this choice, the idea that "keeping URLs clean and unchanging simplifies firewall rules and monitoring configurations" was influential. Especially when a service has more than 20 APIs, going with /v1/users, /v2/users, /v3/users can be a more elegant solution than Accept: application/vnd.bank.users.v3+json.

ℹ️ Which Method When?

When making a choice, consider your target audience and the lifespan of your API. Simplicity (URL Path) for public APIs and flexibility (Header) for closed systems or custom integrations generally yield better results. Remember, the ultimate goal of API versioning is to be able to evolve your API without breaking your clients.

In these choices, I always asked myself, "How much flexibility will I need in the future?" and "What will be the operational cost of this approach?" Sometimes I accepted technical debt for the sake of simplicity, and sometimes I opted for a slightly more complex start for long-term sustainability. The important thing is to be aware of these trade-offs and make informed decisions.

Zero Downtime and Deprecation Management During Version Transitions

One of the most challenging parts of API versioning is ensuring a smooth transition from older versions to newer ones. When working with the goal of "zero downtime," both old and new versions need to be live simultaneously for a period. This is usually managed through a process called "graceful deprecation."

During the transition from v1 API to v2 in an ERP system for a manufacturing firm, I observed that the old operator screens continued to use v1, while the newly developed mobile applications started using v2. To manage this transition process, we set a deprecation period of approximately 6 months. During this period, by monitoring calls to the v1 API, we identified which clients were still using the old version. I notified clients by adding a warning header (X-API-Deprecated: true; Deprecation-Date: 2026-12-31) to the v1 endpoints.

HTTP/1.1 200 OK
Content-Type: application/json
X-API-Deprecated: true; Deprecation-Date: 2026-12-31
Link: <https://api.example.com/v2/docs>; rel="sunset"; type="text/html"

{
  "message": "This API version (v1) will be deprecated on December 31, 2026. Please migrate to v2."
  // ... v1 response data
}

Enter fullscreen mode Exit fullscreen mode

This type of deprecation notice gives client-side developers sufficient time and helps them plan the transition. Seeing the number of requests to v1 endpoints decrease over the deprecation period was a key metric indicating that we could safely shut down v1. If I hadn't done this monitoring, I might have shut down v1 and put a customer still using it in a difficult situation. I previously wrote about [related: observability strategies], and this is part of that.

I haven't shied away from making mistakes either. Once, while performing final checks before shutting down v1, I decided to shut it down within an hour with the thought, "no one is using it anyway." However, a reporting tool that was overlooked was still using v1, and that tool's reports suddenly started coming back "empty." This taught me the lesson: "Never assume, always look at the data." In such situations, having rollback mechanisms ready is a lifesaver. My [related: post on CI/CD reliability] also touches upon these topics.

Development and Operational Cost of API Versioning

API versioning brings significant development and operational costs with it. Defining a strategy without considering these costs can lead to major problems down the line. In my experience, these costs are generally categorized under three main headings: documentation, testing, and deployment.

Documentation: Each new API version requires an updated or new set of documentation. While tools like Swagger/OpenAPI simplify this process, maintaining separate documentation for two or more active versions requires continuous effort. Especially in cases where a field in v1 changes or is removed in v2, clear and up-to-date documentation is vital to avoid confusing client developers. I know I spend a few hours a week keeping the API documentation for my side project up to date.

Testing: Each new version means expanding the existing test suite. The tests you wrote for v1 may not be valid for v2, or you may need to add new scenarios specific to v2. In an ERP project, we updated our CI/CD pipeline to test v1 and v2 APIs simultaneously. This increased test time by 30%, but it was necessary to catch regressions. I especially had to write separate integration tests for each version in automated tests.

# Example CI/CD test step (pseudo-code)
# Similar steps can be used in Gitlab CI/CD or Github Actions

test_api_versions:
  stage: test
  script:
    - echo "Running tests for API v1"
    - docker-compose -f docker-compose.v1.yml up -d
    - pytest tests/v1/
    - docker-compose -f docker-compose.v1.yml down

    - echo "Running tests for API v2"
    - docker-compose -f docker-compose.v2.yml up -d
    - pytest tests/v2/
    - docker-compose -f docker-compose.v2.yml down

Enter fullscreen mode Exit fullscreen mode

Deployment and Operations: Keeping multiple API versions live complicates deployment strategies. Having different versions in different codebases (e.g., separate Git branches) or containing conditional logic within the same codebase increases the maintenance burden. In a client project, I ran two different API versions in separate Docker containers and routed traffic using Nginx. This meant allocating separate resources (CPU, RAM) for each version, which increased operational costs. I previously shared my experiences with [related: Nginx reverse proxy configurations].

To reduce these costs, it's important to avoid unnecessary versioning and include non-breaking changes (e.g., adding a new field) in the current version. Furthermore, setting long deprecation periods encourages clients to migrate to newer versions, helping to reduce the maintenance burden of older versions.

Conclusion: Versioning is a Strategy, Not a Feature

API versioning is more of a product and operational strategy than a technical feature. One of the most important lessons I've learned in my 20 years of field experience is not to postpone this topic by saying "we'll handle it later." Before launching an API, having a clear versioning strategy and ensuring your team is in agreement on it will prevent many future headaches.

The right versioning approach varies depending on the nature of your project, your target audience, and your operational capacity. The simplicity of URL Path, the flexibility of Header-based versioning, or the rare use cases of Query Parameter... I've experienced them all and seen that each has its own pros and cons. The important thing is to be aware of these trade-offs and make an informed decision.

Remember, as your API evolves, your clients will also have to evolve. Versioning is one of the most powerful tools we have to make this transition as smooth and transparent as possible. My clear position is: Start early, monitor, communicate, and never assume.