For years, a team spent hours designing API contracts by hand. We’d open a JSON file, type out opening and closing braces, and start making up field names and sample values. One endpoint, two endpoints, sometimes five or six for a complex feature. We’d debate field names, data shapes, and whether an amount should have two decimal places or four. Then we’d get together as a pair or a small group and poke holes in the design before anyone wrote any C# or TypeScript.

It was slow. In hindsight, it was also one of the most important steps of our process.

The API Is the Boundary

The API is the contract between the frontend and the backend. It sits at the boundary between two teams, two codebases, two sets of concerns. Once you understand that, the question becomes: when should you define that contract?

The answer that has worked for us is to do it before writing implementation code. Before you write a controller. Before you write a handler. Before you set up an Angular service. The contract comes first, because everything downstream depends on it.

When both sides are building against the same agreed-upon shape, you can work in parallel without stepping on each other. The frontend developer doesn’t need to wait for the backend to be running. The backend developer doesn’t need to interpret vague requirements from the UI. The contract is the source of truth, and it exists in a format both sides can read before a single line of production code is written.

The mechanism that makes that parallel work practical is stub endpoints — real endpoints that return the agreed-upon shape before any business logic is wired. I wrote about how that works in Stubbed Endpoints: How We Build UI Before the Backend Exists.

The Contract Is Driven by the UI’s Needs

The API shape should be driven by what the UI needs, not by what the database stores.

If a customer’s name is stored as five separate fields in the database (prefix, first name, middle initial, last name, suffix), that’s the database’s problem. If the screen only needs to display “Last, First,” then the API returns exactly that. The frontend doesn’t need to know about the storage structure. It needs the data in a shape that best serves the experience.

The same logic applies to amounts, dates, calculated fields, and anything else. We design the JSON response to serve the user interface, not to mirror the data model. The backend figures out how to get there.

What We Actually Design

For each endpoint, the design captures several things:

  • Where does it live? The URL structure.

  • What permission does a user need to access it?

  • What does the request body look like, if there is one?

  • What does the response look like?

  • And critically, what does the sample data look like — not random strings and numbers, but values that actually resemble the domain. If the feature involves produce items, the sample data has potatoes, not Ferraris.

The sample data matters more than it might seem. When you’re looking at a design that says itemName: "Honeycrisp Apples" and quantity: 100, you can reason about whether the shape makes sense in context. When it says itemName: "asdf", you can’t.

We also design the error cases. What does the API say back when someone submits a form with invalid data? What fields does it call out? What message does it give? That conversation between the UI and the backend, when something goes wrong, is part of the contract too. And it is designed to serve the people who use the system, enabling them to accomplish their task.

Now With AI

This whole process used to take hours. Complex features might take half a day just for the design step, before any real implementation.

These days, we have a skill that takes a user story and any UI mockups we’ve prepared and generates the full endpoint design, including the JSON shapes with domain-appropriate sample data, following the conventions we’ve established over the years. If the mockup has a “+” button that we didn’t explicitly call out, the skill finds it and designs an endpoint for that action. The design is done in minutes.

What hasn’t changed is the review step. Someone still looks at what came out and asks: does this make sense? Is there a field here that the UI doesn’t actually need? Is there something missing that we’ll regret later? That conversation happens over a JSON file, not over C# or TypeScript, which means corrections are fast and cheap.

The discipline of designing the contract before writing code hasn’t gone away. It’s just much faster to get to the review.

Cheap to Change, Expensive to Skip

A JSON file is easy to change. You edit a field name or add a property and you’re done. A controller, a handler, a service, a component — each of those takes real effort to revise.

Skipping the design step doesn’t save time. It just moves the cost to a place where it’s much more expensive. Every rework I’ve seen from a poorly thought-out API shape traces back to that skipped design step. The half hour upfront is worth several hours of untangling later.

Leave a Reply

Trending

Discover more from Claudio Lassala's Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading