Practical API Versioning and Deprecation for Indie Developer Tools
A lightweight process for solo founders to version small APIs and libraries, communicate changes, and remove old behavior without breaking users or creating endless…
Developer tools and APIs live longer than most founders expect. A small library or REST endpoint that worked for the first ten users can still be called by those users two years later. When you need to change a response shape, rename a field, or drop an endpoint, the cost of getting the communication wrong is usually higher than the cost of the code change itself.
This is a practical operating guide for solo founders and small teams. It assumes you do not have a dedicated developer-relations person or a full versioning platform. The goal is a simple set of rules you can follow so that changes are predictable, users have time to adapt, and you can eventually delete the old path.
Choose a versioning style and stick to it
Most indie tools use one of three approaches. Pick the one that matches how your users consume the product and document it once.
- Semantic versioning for libraries and SDKs. Major.minor.patch. A major bump means a breaking change. Users who pin to a major version (or use a lockfile) stay safe until they choose to upgrade. This works well for npm, PyPI, Cargo, and similar package managers.
- URL or header versioning for HTTP APIs.
/v1/resourceor anAccept/API-Versionheader. New behavior lives under a new version identifier while the old one continues to work. This is common when clients are not updated through a package manager. - Additive changes only. Prefer adding fields or optional parameters over changing or removing existing ones. This avoids a formal version bump for many improvements and is often the lowest-friction option for early products.
Whatever you choose, state it in the README or API docs in one sentence. “This library follows semantic versioning. Major versions may contain breaking changes. We will announce deprecations at least 90 days before a major release.” Clarity here reduces support questions later.
What counts as a breaking change
Not every modification is breaking. The following usually are:
- Removing a field, endpoint, method, or parameter that existing clients rely on.
- Changing the type or meaning of an existing field (a string becoming an object, a success code changing meaning).
- Making a previously optional parameter required.
- Changing authentication requirements or error formats in a way that breaks existing error handling.
- Altering rate-limit behavior or pagination defaults so that previously working clients now fail or receive incomplete data.
Adding an optional field, adding a new endpoint, or tightening validation in a way that rejects previously invalid input is usually not breaking. When in doubt, treat it as breaking and communicate it. The cost of an extra announcement is lower than the cost of a silent breakage.
A minimal deprecation process
You do not need a formal deprecation committee. A short, repeatable sequence is enough.
- Mark the old behavior as deprecated in code and docs. For libraries this can be a comment, a
@deprecatedannotation, or a runtime warning that logs once. For APIs it can be a response header (Deprecation: trueandSunset: <date>) or a note in the OpenAPI spec. The warning should appear in the place developers actually look (logs, response headers, or the docs page for that endpoint). - Announce the change. Update the changelog, post a short note in your changelog or status page, and send a one-time email to users who have the relevant endpoint or package in their recent activity if you have that data. Keep the message factual: what is changing, why, the timeline, and what they should do instead. Link to a migration guide if the change is non-obvious.
- Give a concrete timeline. Ninety days is a reasonable default for most indie tools. Shorter is acceptable for rarely used endpoints if you can confirm low traffic. Longer is better when the change requires significant work on the client side. Put the sunset date in the announcement and in the deprecation warning.
- Monitor usage of the old path. Even a simple log line or metric that counts calls to the deprecated endpoint is useful. If usage drops to near zero before the deadline, you can remove it earlier. If it remains high, you may need to extend the timeline or reach out to the remaining users.
- Remove the old behavior. Delete the code, update the docs, and note the removal in the changelog under the new major version or the date the versioned endpoint was retired. Leaving dead code “just in case” creates more maintenance than it saves.
The entire process can live in a short internal checklist and a public changelog. No additional tooling is required beyond what you already use for releases.
Changelog and communication habits that scale
A single CHANGELOG.md or a /changelog page is usually sufficient. Keep entries short and grouped by version or date. Use consistent labels such as “Breaking”, “Deprecated”, “Added”, and “Fixed”. Link each breaking or deprecation entry to the relevant docs or migration note.
Other low-effort channels:
- A pinned discussion or issue in the repository for major changes.
- A short section in release notes that email subscribers already receive.
- Response headers or a docs banner for the affected endpoints while the deprecation window is open.
Avoid burying the notice only in a Slack channel or a social post that disappears. Users who integrate once and check back months later should still be able to find the information from the repository or docs.
Practical checklist before a breaking change
- The change is necessary (security, reliability, or a clear product improvement) and not just a preference.
- Old and new behavior are documented side by side during the transition.
- Deprecation warning or header is in place and has been tested.
- Changelog entry and any migration guide are written.
- Announcement is scheduled (email, changelog, docs banner).
- Sunset date is explicit and at least 90 days out unless usage data justifies a shorter window.
- You have a way to see whether the old path is still being called.
- Plan for removal is noted so the old code does not linger indefinitely.
- Tests cover both the deprecated path (while it exists) and the new path.
Common pitfalls
- Shipping a breaking change in a minor or patch release because “almost no one uses that field.” Even low usage can include important customers who integrated months ago.
- Deprecating something in code but never announcing it. Developers do not regularly read source comments.
- Setting a sunset date and then forgetting to remove the old code. The dual path becomes permanent and increases the chance of subtle bugs.
- Changing error formats or status codes without warning. Clients often branch on specific strings or codes.
- Offering a new version but leaving the old documentation as the default landing page so new users copy the soon-to-be-removed examples.
When the simple process is no longer enough
Consider more structure when:
- You have multiple products or SDKs in different languages that must stay in sync.
- Enterprise customers require formal change-management notices or specific notice periods in a contract.
- Traffic on deprecated endpoints stays high for months and you need better targeting or phased rollouts.
- You want automated checks that fail a build if a public API surface changes without a major version bump.
Until then, consistent semantic or URL versioning, a short deprecation window with clear communication, and the habit of actually removing old behavior will cover most solo-founder needs. The system should make changes predictable for users and sustainable for you.
Versioning is mostly a communication problem. When the rules are written down, the timeline is realistic, and the old path is eventually deleted, users can upgrade on their schedule and you avoid maintaining two implementations forever. Start with the smallest version of this process on the next change that would otherwise surprise someone, and refine it based on the questions you actually receive.
Sponsor this article · $29 / 30 days
More in Developer Tools
Practical Configuration and Credential Handling for Indie Developer Tools
Practical configuration and credential-handling patterns for solo maintainers of CLIs, SDKs, and small developer tools.
Practical Opt-In Telemetry for Indie Developer Tools
A lightweight, privacy-conscious approach for solo founders who maintain CLIs, SDKs, or small libraries: what to meas...