When an API upgrade breaks old clients, should you keep the old API?
After a server-side API upgrade, older clients may fail to connect. In most cases, the user is not unwilling to update; instead, the server changed the old interface's request contract, such as adding a required field without a default value, changing return types, or adjusting error code semantics. In 2026 project delivery, the common approach is to keep the formal external API in a compatibility period, with old and new versions running in parallel, and decide the sunset time based on the old-version call traffic. The experience range is usually to keep it for at least one client release cycle, commonly 1-2 months on mobile; or wait until the old-version call share stays below 5% for 7 consecutive days, then arrange the removal. This article only discusses how the server side handles old endpoints; it does not discuss gray traffic switching.
Check the contract first, don't rush users to upgrade
The client and the server can communicate based not on beautiful code but on consistent agreement of message format, field meanings, and value ranges. When upgrading, if you change an optional field into a required one, or change a return field from string to number, old clients cannot detect it in advance and will be rejected by the new logic.
For troubleshooting steps, it is recommended to fix two steps: first find 'version number + request path + response code' in gateway or application logs, then diff old and new interface definitions. Focus on required fields, return value types, enum values, and signing rules; common incompatibilities are almost always hidden in these four categories.
- Adding a request parameter that is required and has no default value: the old request will report a parameter error directly.
- Renaming or deleting the return field: client parsing will get a null value or type error.
- Changing the semantics of an error code: the client will enter an error message branch that no longer matches the original condition.
- Changing the field order or participating set of the signature string: the old client's encryption logic will fail completely.
It is easy to misjudge here: seeing no exception in application monitoring leads to thinking the server is fine. In fact, the server may look normal because old traffic has already been redirected to new logic and then the new contract rejects the old request.
First distinguish clearly: which upgrades can keep no old API
Not every API modification requires keeping the old version. If you can control the requestor version, such as for internal admin backends, service calls maintained by the same team, or pre-production environments that have not been released, you can modify and release the front-end and back-end together, and you don't need to repeatedly maintain legacy logic.
Conversely, for apps in app-store review, mini programs, APIs called by third parties in the long term, and IoT devices that cannot be remotely upgraded, it is recommended to put the compatibility period in the development schedule. The key to whether compatibility can be skipped is one principle: can you force all requesters to upgrade to the new version?
How long to keep old API: use 'observe-switch-sunset' triggers
Deciding how long to keep an old API should not be based on gut feeling. In 2026 project delivery habits, each stage should have a verifiable exit condition; it is not 'we will delete it in a certain month'.
- Observe: after the new API goes online, keep the old API and record the call count, failure rate, and version distribution of old clients. The observation period is recommended to cover a complete business cycle; a common experience range is 1-2 weeks. If there are month-beginning or month-end peaks, cross the peak.
- Switch: when the success rate of the new API stabilizes at an available level (a typical practice range is around 99%) and the old-version call share falls to around 20%, drive higher-version clients to switch to the new API. At this point, stop feature iteration for the old API, but do not delete it.
- Sunset: when the old-version call share is below 5% for 7 consecutive days, or when it is more than 1-2 months after the new version release, delete it during off-peak hours; before deleting, indicate in documentation and response headers that the old version will be unavailable.
If the old-version share keeps not dropping, first find the reason, e.g., users are stuck on an old version and cannot upgrade, or a push task keeps users on the old version. If you delete by calendar at this time, the impact scope may be enlarged.
URL version or Header version: the boundary is different
When old and new interfaces must be online at the same time, the server needs to know which version the request is from. Based on common experience: for external public APIs, use the URL path version; for internal service calls, use the Header version; only consider the version field in the request body when the change scope is very small.
- URL path version (/api/v2/order): the path is clear and visible directly in gateway, log, and monitoring; the downside is that every major version leaves a path.
- Header version (X-Api-Version: 2): does not pollute the URL, suitable for internal services; however, the log and gateway must record the header, otherwise it is difficult to locate the version during troubleshooting.
- Version field in the request body: can only handle small differences; when the structure change is large, many version branches will appear, generally not recommended for the main version.
No matter which method you choose, cache keys, rate-limit rules, and monitoring alarms must carry the version identifier. Otherwise, failures of different versions are mixed together, and it becomes hard to answer 'should this old API be kept?'.
Common pitfalls in keeping old APIs, and one real-world experience
Keeping an old API does not mean that a copy of a controller can simply run. In actual delivery, it is common that the routing remains but the code points to new logic; or only the HTTP layer is retained while the database fields are changed by the new version, and the old API reports an error when executed. There are roughly four common problems.
- The route is still there, but the code runs the new logic and validation also uses the new rules, so old clients always fail.
- Database fields have been removed or default values changed, so the old API execution reports that the field does not exist.
- No separate timeout and retry is configured for the old API; when the new link slows down, the old link is dragged down together.
- Error codes are not isolated by version, and old clients cannot receive error codes they can recognize.
The recommended acceptance action is to run old-version messages in the test environment through the full business flow before launch; the status codes, returned fields, and error messages must be consistent with the old version.
One real delivery experience: an old terminal whose vendor has stopped maintenance cannot change code, but the new API must go live. Our approach was to split traffic at the gateway layer by source IP or device ID: old devices continue to use old logic, and new traffic goes to the new API. The experience range for this compatibility routing is an extra 2-5 person-days of development and joint debugging. The cost is not small, but it can avoid a collective disconnection of old devices on the first online day.
Applicable and non-applicable boundaries
This approach is suitable for scenarios where, after an official release, the requesters cannot be forced to upgrade; it is often seen in apps, desktop clients, third-party open APIs, and IoT devices with slow version updates. If your callers are internal services that you can control, a synchronized upgrade saves more effort; there is no need to pay double maintenance costs for the sake of elegance.
The old API is not a free service. For every extra version kept, security patches, dependency upgrades, and monitoring calibration are added. In common 2026 experience, it is recommended to keep no more than two major versions on external APIs. When too many old versions accumulate, it is more worthwhile to do data-plane convergence than to let compatibility hide the slow-down of upgrades for a long time.
The method in this article is suitable for short-term coexistence of old and new features, not for long-term architecture that treats compatibility as a basis. For old systems that need maintenance across multiple years, it is recommended to use gateway forwarding plus standalone configuration isolation, rather than piling version branches in the main program.
Common questions
How long should an old API be kept in general?
The experience range is: for external clients, keep at least one release cycle, typically 1-2 months on mobile, or until old-version call share is lower than 5% for 7 consecutive days; for internal systems, it can be shortened to about 2 weeks.
An old client cannot connect. What should be checked first?
Look at the version number, request path, and response code in the gateway or logs first, then diff the new interface definition with the message that the old client depends on. Focus on required fields, return fields, enum values, and signature rules.
Is it better to use the path version /v2 or the request header?
For public external APIs, URL path versioning is recommended; for internal service-to-service calls, Header versioning is OK. In both methods, the version must be recorded in logs and monitoring, otherwise locating faults is difficult.
If old-version calls never drop, can I remove the old API directly?
Direct deletion is not recommended. First investigate whether users are stuck on an old version or whether forced blocking is in place. If you do need to keep it, set boundaries: no new features, no frequent changes, and continue monitoring the call volume.
Before publishing a new API, answer first: 'how many requests still stay on the old contract?' If the answer is not clear, do not rush to delete old code. Use the 'observe-switch-sunset' framework to set exit thresholds at each stage, and make the decision when data arrives.
-
Upload folders ship with code and images are lost—should files be stored locally or in object storage?
Date: Sep 13, 2026 Read: 2
-
Saved Just Now but the Detail Page Still Shows the Old Record — Does Read-Write Splitting Mean Every Read Has to Go to the Primary?
Date: Sep 12, 2026 Read: 5
-
Scheduled Jobs Run Fine on One Machine but Duplicate on Multiple Servers — Where Should You Stop Them?
Date: Sep 11, 2026 Read: 11
-
Auto-increment primary keys are convenient when a table first goes live — how much trouble is it to change them on the day you actually shard?
Date: Sep 10, 2026 Read: 15
-
Why did APIs get slower after increasing the database connection pool, and what is the appropriate connection count?
Date: Sep 9, 2026 Read: 18




