Good APIs Age Slowly | Yusuf Aytas
I have noticed that APIs are a bit like abstractions in general. APIs that impress people quickly are very often the ones that cause the most trouble later. I do not mean this as some grand law. Perhaps, I am being slightly unfair, but I have seen enough beautiful APIs turn into maintenance issues to say that.
What I trust more is time. A good API might not be the one that looks the cleanest in a code review. It is the one that still makes sense after other teams start using it, after requirements shift, after the underlying implementation changes, and after somebody does something with it that the original author definitely did not have in mind. Good APIs age slowly. That is probably a much better test than elegance.
Good APIs Age Slowly
The First Version Gets too Much Credit
I think people often like the first version of an API too much because they judge it when everything is still simple. The person who made it knows exactly how it should work, the people using it usually think in the same way, and the system around it has not changed yet. In that situation, almost any API can look good.
Then real life starts. A different team comes along and uses the API from a batch processing. Somebody assumes a field is stable because it has always been there, even though nobody explicitly promised it. Somebody else relies on response ordering because that is how it happened to work in the current implementation. Six months later, when the original team wants to change something inside the system, they realise it is not just an internal change anymore. Other people are already depending on it, even if that was never the plan. That is usually where the bullshit begins because software has a nasty habit of turning observed behavior into dependency whether you meant it or not.
Most API Problems are Boundary Problems
When I think more about API design, I feel the main problem is not beauty, names, or how nice the code example looks. I think the real problem is usually about boundaries. Teams often do not decide early enough what should be part of the public contract and what should stay private inside the system.This seems easy in theory, but in real work people mix these things up all the time. Usually this happens because adding one more field or showing one more piece of state seems safe at the time.
Unfortunately, it rarely stays harmless. Once something is visible, consumers start building around it. From that point on, it does not really matter whether you meant to promise it or not. They saw it, it was useful, and now it is part of their mental model. This is what makes boundary mistakes so irritating. They work just enough for other people to start relying on them. Then later, when you want to change something inside, you realize those internal details have become part of the API.
My gut instinct at this point is to expose as little as possible. I know it’s conservative and had fought some fights on it. Happy to do more because adding later is relatively easy. Removing things, once other people have started relying on them, is where the real pain begins. That is when normal technical choices become difficult talks, migration work, and team politics. Hence, a lot of API stability comes from being careful about the boundary. Teams need to think more carefully about what other people really need to see and what should stay inside.
Convenience Has a Cost
Many APIs are made to feel easy in the current way people use them. At first, that looks good. But many times, easy only means the API has many hidden assumptions. It may assume calls happen in a certain order. It may assume the same type of user. It may assume the same timing. It may assume the caller thinks like the person who made it. At the beginning, this can look like good design.
Then a new use case shows up and the API suddenly feels stiff and awkward. I am not saying every API needs to spell out every little thing because that would be dumb too. Nevertheless, when an API tries too hard to be helpful and guesses too much, it is usually moving the complexity. Later that turns into debugging pain, migration pain, or just teams wasting time trying to figure out what the hell is going on.
That is why boring APIs often last longer. They may not feel as nice at first but at least they are honest. They show the boundaries more clearly. They make the state more visible. They leave less magic hidden in the background. And less magic usually means less bullshit later when the system changes.
Your API Is Not Your Frontend
Another thing that seems harmless until it is not is designing APIs around the current frontend shape. I understand why people do it. A page needs some payload in a certain structure, the backend returns exactly that structure, and everybody moves on. It feels practical. Sometimes it probably is practical.
But a screen is not a domain model, and tying your API too closely to a temporary UI decision...