Coupling Has a Time Factor


We were building a screen to create a portfolio. It was basically a form — fill it out, submit it.

Submitting hit an API, which saved to a database. If a field was missing from the payload, the API would default it.

UI submits to the Node.js service, which saves to the database and defaults missing fields

However, we wanted to display the defaults the API was applying in the UI, so the user could see them before submitting. But we also needed those defaults server-side, since the API was reused in other places. There were a lot of fields, and we didn’t want two sources of truth.

The create portfolio form with the API’s default values pre-filled in red

Attempt one: a shared library

We built a TypeScript library containing all the API’s default values. Both our Node.js service and our UI imported it.

This felt great at first. Everything was typed. You could see the defaults right there in your editor, at compile time.

Both the UI and the Node.js service depend on the shared TypeScript library

Then I started wondering: why not use an API instead?

Instead of a shared library, the UI could fetch defaults from a defaults API when the page loaded, and store them in state. No library. The defaults would just live in the service.

The UI depends on the Node.js service directly via a defaults API

The tradeoff was obvious: we’d lose the explicitness of the library. No more seeing the data at compile time. Just a fetch at runtime.

What I realized

The defaults API approach was better. Not because of typing or explicitness — because of coupling.

With the library, the UI knew about the API’s defaults at compile time. When a default changed, the UI had to recompile, rebuild, and redeploy to pick it up.

With the defaults API, the UI only knew about the defaults at runtime. When a default changed, the UI could just refetch it on the next page load. No recompiling, no rebuilding, no redeploying. This approach was more loosely coupled.

That’s when it clicked: coupling isn’t just about what one service knows about another — it’s also about when it knows it. The library and the API exposed the exact same information. The only difference was when the UI learned it.

Takeaway

In general, it’s well known that we should be minimizing coupling between services, so that when one service changes, hopefully the others don’t have to. That’s the lowest-maintenance outcome. But the lesson here is that coupling has a time dimension too. You can reduce coupling not just by changing what two services know about each other, but by changing when they know it.


Note: this example is simplified. We weren’t just creating portfolios — we were creating hundreds of different financial entities (investments, stock purchases, debt, and more). And the data shared with the UI wasn’t just defaults — it included other metadata too, like required fields.