← Blog
engineeringarchitecturetypescriptbackend

The Interface Is the Contract

21 March 2026

The Interface Is the Contract

The most expensive line of code you'll ever change isn't in the implementation. It's in the type signature.

You can rename a variable, swap an algorithm, rewrite a query — as long as the interface stays the same, nothing breaks. The interface is the promise. The implementation is how you keep it.

Implicit and explicit contracts

Every function has a contract: given these inputs, produce this output, under these conditions. In untyped JavaScript, that contract lives in your head, in comments, maybe in documentation nobody reads. It can drift. It can be wrong. It can simply not exist.

TypeScript makes contracts first-class. When you define a function signature, you're not just annotating — you're publishing a promise that the compiler will enforce. The biggest benefit isn't catching type errors at the call site. It's that you can't casually change a contract without the compiler showing you everywhere that change matters.

Design the interface first

The discipline I've found most useful: before writing the implementation, write the type signature. What does this function receive? What does it return? What are the invariants?

This sounds like design-up-front, which has a bad reputation for good reason. But a type signature isn't a spec — it's one line. Writing it first forces you to think about how the code will be used before you think about how it will work. Those are different questions, and the first is usually more important.

When I built the booking logic for Barba Studio, I wrote the function signatures for the scheduling layer before touching the database. Not because I was being rigorous — because I needed to understand what the UI actually needed to call. The implementation changed several times. The interface barely changed at all.

The stability gradient

Not all interfaces are equally expensive to change. There's a natural gradient:

Public APIs — what external clients or other services call. Most expensive. Changing these is a breaking change, and breaking changes need versioning, migration paths, and communication.

Module boundaries — what your internal services or packages call each other. Expensive. Changing these breaks other parts of your own system.

Internal function signatures — what your own code within a module calls. Cheap. Refactor freely.

Implementation details — variables, algorithms, data structures. Free. Nobody outside the function cares.

The mistake I see most often is treating these as equivalent — over-engineering internal details as if they were public APIs, or casually breaking module boundaries as if they were implementation details.

When the interface tells you something

There's a specific feeling when a type signature gets complicated. Parameters multiply. The return type becomes a union of five things. You need an options object because the argument list is unmanageable.

This is the interface telling you something — the same way untestable code is telling you something. A complicated interface usually means the function is doing too many things, or the wrong abstraction is sitting at the boundary.

The right response isn't to push through. It's to stop and ask what the interface should look like — then work backward to the implementation that satisfies it.

TypeScript as living documentation

What I value most about typed interfaces isn't bug prevention. It's communication. When I return to a module I haven't touched in six months, the types tell me what it expects and what it promises. They can't be wrong the way a comment can be wrong, because wrong types don't compile.

This matters especially when you're the only developer. There's nobody to ask. The interface is the documentation.

Change carefully

Treat interfaces as contracts, not suggestions. Change implementations freely — that's what implementations are for. Change interfaces carefully, and when you do, treat it as a decision worth recording. Because everyone who depends on that contract will need to know why it changed.