I remember the exact moment I realized I had lost control of my own code. It wasn’t a bug or a crash. It was a mobile developer in another time zone who had built an entire feature on top of a temporary field in a JSON response.
I wanted to rename that field to something more sensible. It would have taken seconds. But the moment that response left my server and reached a device I didn’t own, it stopped being my code. It became a promise. And in software, breaking a promise is far more expensive than refactoring a function.
The Illusion of Agility
In the early stages of a system, speed feels like the only thing that matters. You move quickly, reshape your database without hesitation, and adjust responses as if nothing depends on them. This works because nothing really does. The frontend and backend move together, often in the same repository, deployed at the same time. You can afford to be clever.
That illusion fades as soon as your system escapes your control.
A mobile app might sit on a user’s phone for months without an update. A partner might integrate your API into a dashboard you never see. At that point, your backend is no longer just code you can change freely. It becomes infrastructure that other people rely on.
This is where many experienced engineers still make a critical mistake. They treat APIs like private functions. A private function can change whenever you want. An API cannot. Its shape is no longer an implementation detail. It is a contract.
Design for the No
Good API design is not about flexibility. It is about constraint. Every piece of flexibility you expose becomes something you are responsible for supporting indefinitely.
A common example is the temptation to include a generic metadata or settings field that accepts arbitrary JSON: