There's an old urban legend in New York about Chinese restaurants — that thousands of locations across the city are all sourced from a single kitchen. The same food, the same packaging, just hundreds of different storefronts. It wasn't really true, but it's a useful mental model for what a well-abstracted API architecture can achieve.
With a truly flexible, extendable API layer, you can spin up a new single-page application and reuse most of the same backend components. When I'm explaining this to stakeholders, I'll often show a diagram of the backend services involved in delivering a new product, then highlight how much of that backend is just recomposing several existing services. The "new" product is really just a new storefront for capabilities that already exist.
The Promise of Immutable Interfaces
To make this work, APIs need to be broken down into the proper level of abstraction. Once an API is created, it shouldn't need to significantly change — or at minimum, it shouldn't need breaking changes. APIs should become immutable interfaces between systems.
This is what makes integration easy. Teams and products can connect to your APIs and gain value quickly. But if those APIs become a liability — if every team that integrated now needs to change their integration — that efficiency disappears fast. The very thing that was supposed to accelerate delivery becomes a coordination nightmare.
Scar Tissue and Forward Thinking
Designing APIs in this environment requires forward thinking. But even more, it requires scar tissue — hard-won experience about the kinds of hasty API decisions that come back to bite you.
Should this property be a string? An object? An array of strings? An array of objects?
These aren't trivial questions. The strategy pattern, when applied to API property design, is a powerful drug. In essence: make a property an array of objects, each with a type property, with the intention that consumers push each object through a case statement to decide how to resolve it.
This approach is hugely powerful. If you want to create a timeline, put a type on each object in that timeline, and now you can define a different structure for each entry. It's infinitely extendable.
But it has a cost. Quickly, you reach a point where what was built for flexibility has now forced every consumer to develop for all potential cases. The flexibility you gave yourself becomes complexity you've imposed on everyone else.
On the other hand, if you make that property just an array of strings, you lose the ability to provide any fidelity to the structure of that data. You've traded flexibility for simplicity, but perhaps too much.
Canonical Models and Release Valves
My approach is to ensure canonical models are well designed, socialised, and tested in a variety of scenarios before adoption. A canonical model should represent the stable, agreed-upon structure of your core business entities.
But I also make sure there's a release valve — something like a custom fields key store — that can cover the one-in-a-thousand exceptions. This is far better than baking every exception into the canonical model itself.
If a hard-and-fast canonical model isn't possible for a particular entity or endpoint, that's a strong indicator. It means that endpoint should be peeled off and become solution-specific rather than trying to serve everyone poorly.
Beyond Entity CRUDs: Process Endpoints
One pattern I've found particularly valuable is designing around pinch points in process flows — places where minimal data needs to move from one system to another. By finding these points, you can create highly reusable process endpoints, not just entity CRUDs.
I've designed and built systems intended to set off a stateless process: two or three canonical records come in to trigger a process, and the results of that process flow out to a third system. The API doesn't know or care what happens before or after — it just executes its piece of the workflow.
The key is resisting any opportunity to introduce requirements that would make the API specific to a particular use case. When you hold that line, something remarkable happens: six months later, when a similar solution is needed by another system, you can offer an integration with virtually no real work. A whole new demand is fulfilled because you designed for reuse rather than for the immediate ask.
The Discipline of Abstraction
API-first thinking isn't just a technical architecture decision. It's a discipline that shapes how you respond to business requests.
When someone asks for a feature, the API-first mindset asks: what's the general capability here? How do we build this so the next three teams who need something similar can reuse what we've built?
It requires saying no to shortcuts that would make an API too specific. It requires investing time upfront in proper abstraction. And it requires the scar tissue to know which design decisions seem harmless today but will haunt you in eighteen months.
The payoff is an organisation that can move faster over time — where each new product is a recombination of proven capabilities rather than a greenfield build. One kitchen, many storefronts.