This content originally appeared on HackerNoon and was authored by Maximiliano Contieri
When you break APIs without warning, you break trust
TL;DR: You should version your APIs to prevent breaking existing clients when you make changes.
Problems 😔
- Client applications crashes
- Integration failures
- Least Minimal Surprise Principle violation
- Downtime
- Broken Trust
- Deployment rollbacks needed
- Development time wasted
- User experience degradation
Solutions 😃
- Add semantic versioning
- Implement backward compatibility
- Create deprecation warnings
- Create roadmaps
- Use content negotiation
- Maintain parallel versions
- Communicate changes early
- Deprecate features gradually
- Document breaking changes clearly
- Check deprecated parameters with logging
- Test new versions thoroughly
- Remove deprecated functionality after sunset
Context 💬
When you modify APIs without proper versioning, you create breaking changes that affect all existing clients.
\ You force consumers to update their code immediately or face system failures.
\ You break the implicit contract between API providers and consumers.
\ Modern software relies heavily on API stability, and introducing breaking changes without warning can create cascading failures across dependent systems.
\ This is more important today than ever since many IAs build their solutions using existing API documentation.
\ When you update an API without maintaining backward compatibility, you risk breaking all the applications that depend on it.
\ This creates instability, frustration, and costly fixes for users.
\ Clients often tolerate defects in new functionalities, but never a previously stable behavior broken.
\ Proper versioning ensures smooth transitions and maintains your system's reliability.
Sample Code 📖
Wrong ❌
// user-api-v1.json - Original API response
{
"id": 317,
"name": "Mr Nimbus",
"email": "nimbus@atlantis.com",
"nationalities": "Brazilian,Canadian,Oceanic"
}
// Later changed to this without versioning:
{
"userId": 317,
"fullName": "Mr Nimbus",
"emailAddress": "nimbus@atlantis.com",
"createdAt": "2018-12-09T18:30:00Z",
"nationalities": ["Brazilian", "Canadian", "Oceanic"]
}
fetch('/api/users/317')
.then(response => response.json())
.then(user => {
// This breaks when API changes field names and data types
document.getElementById('name').textContent = user.name;
document.getElementById('email').textContent = user.email;
// This breaks when nationalities changes from string to array
document.getElementById('nationalities').textContent
= user.nationalities;
});
Right 👉
// user-api-v1.json - Version 1 (maintained)
{
"id": 317,
"name": "Mr Nimbus",
"email": "nimbus@atlantis.com",
"nationalities": "Brazilian,Canadian,Oceanic"
}
// user-api-v2.json - Version 2
// (new structure, backward compatible)
{
"id": 317,
"userId": 317,
"name": "Mr Nimbus",
"fullName": "Mr Nimbus",
"email": "nimbus@atlantis.com",
"emailAddress": "nimbus@atlantis.com",
"createdAt": "2018-12-09T18:30:00Z",
"nationalities": "Brazilian,Canadian,Oceanic"
"nationalitiesList": ["Brazilian", "Canadian", "Oceanic"]
}
// user-api-v3.json - Version 3
// (new structure, backward not compatible)
{
"userId": 317,
"fullName": "Mr Nimbus",
"emailAddress": "nimbus@atlantis.com",
"createdAt": "2018-12-09T18:30:00Z",
"nationalitiesList": ["Brazilian", "Canadian", "Oceanic"]
}
// client-code-versioned.js
const API_VERSION = 'v1';
fetch(`/api/${API_VERSION}/users/317`)
.then(response => response.json())
.then(user => {
document.getElementById('name').textContent = user.name;
document.getElementById('email').textContent = user.email;
// V1 handles comma-separated string
document.getElementById('nationalities').textContent
= user.nationalities;
});
// Or with content negotiation
fetch('/api/users/317', {
headers: {
'Accept': 'application/vnd.api+json;version=1'
}
})
.then(response => response.json())
.then(user => {
document.getElementById('name').textContent = user.name;
document.getElementById('email').textContent = user.email;
document.getElementById('nationalities').textContent
= user.nationalities;
});
Detection 🔍
- [x] Semi-Automatic
You can detect this smell when you find APIs that change field names, remove fields, or alter data structures without maintaining backward compatibility.
\ Look for client applications that break after API deployments.
\ Check for missing version headers or URL versioning schemes.
\ Monitor error logs for sudden spikes in client failures after releases.
Tags 🏷️
- APIs
Level 🔋
- [x] Intermediate
Why the Bijection Is Important 🗺️
You must maintain a stable MAPPER between your API contract and client expectations.
\ When you break this Bijection by changing the API without versioning, you violate the fundamental principle that clients can rely on consistent interfaces.
\ You create a mismatch between what clients expect to receive and what your API provides.
\ This breaks the one-to-one correspondence between API promises and API delivery, leading to system failures and lost trust.
\ APIs model real-world services. When you break the mapping between your API and the business logic it represents, clients can't reliably interact with your system.
\ This mismatch leads to defects, downtime, a lack of trust, and a poor user experience.
AI Generation 🤖
AI generators often create this smell when you ask them to "improve" or "update" existing APIs.
\ They focus on making the API "better" without considering backward compatibility.
\ You need to explicitly instruct AI tools to maintain existing field names and add versioning when making changes.
\ They often favor clean design over stability unless explicitly told otherwise.
AI Detection 🧲
AI generators can fix this smell when you provide clear instructions about API versioning strategies.
\ You should ask them to implement semantic versioning, maintain backward compatibility, and create migration paths for deprecated features.
Try Them! 🛠
Remember: AI Assistants make lots of mistakes
Suggested Prompt: Create API versioning to prevent breaking changes
| Without Proper Instructions | With Specific Instructions | |----|----| | ChatGPT | ChatGPT | | Claude | Claude | | Perplexity | Perplexity | | Copilot | Copilot | | Gemini | Gemini | | DeepSeek | DeepSeek | | Meta AI | Meta AI | | Grok | Grok | | Qwen | Qwen |
Conclusion 🏁
You should always version your APIs to prevent breaking changes from impacting client applications.
\ Even from your first version.
\ When you maintain stable contracts through proper versioning, you build trust with API consumers and enable smooth evolution of your systems.
\ Breaking changes are inevitable, but they shouldn't break your clients.
\ Always version your APIs, deprecate carefully, and communicate proactively to avoid unnecessary disruptions.
Relations 👩❤️💋👨
https://hackernoon.com/misusing-http-status-codes-wrecks-your-api-monitoring-and-client-logic
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-iv-7sc3w8n
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xii
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxii
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxxiv
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-xxxv
Disclaimer 📘
Code Smells are my opinion.
Credits 🙏
Photo by Giancarlo Revolledo on Unsplash
APIs are forever, so design them carefully
Martin Fowler
https://hackernoon.com/400-thought-provoking-software-engineering-quotes?embedable=true
This article is part of the CodeSmell Series.
https://hackernoon.com/how-to-find-the-stinky-parts-of-your-code-part-i-xqz3evd?embedable=true
\
This content originally appeared on HackerNoon and was authored by Maximiliano Contieri

Maximiliano Contieri | Sciencx (2025-06-13T15:00:08+00:00) Code Smell 303 – How to Prevent Breaking Existing Clients When You Make Changes. Retrieved from https://www.scien.cx/2025/06/13/code-smell-303-how-to-prevent-breaking-existing-clients-when-you-make-changes/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.