Engineering

Your data model is the product, everything else is paint.

Teams obsess over the interface and treat the data model as plumbing. It is backwards. You can repaint a UI in a day, but a flawed schema constrains every feature you build for years. Here is why we spend our hardest thinking on the model underneath.

Early in my career I shipped a feature I was proud of. The interface was clean, the interactions felt right, and we put it in front of customers in under three weeks. Eighteen months later that same feature was the reason we could not build three other things customers were begging for. The UI had been fine. The data model underneath it was wrong, and a wrong data model does not stay quietly broken. It taxes every feature you try to build on top of it, compounding the way debt compounds, until one day you realize the whole product is bending itself around a decision someone made in an afternoon and never wrote down.

That experience changed how I think about where the real product lives. We spend the most ceremony on interfaces because interfaces are what we see, and the data model is treated as plumbing, the boring part you get to once the screens are agreed. It is backwards. You can repaint a UI in a day. You can redesign a flow in a sprint. But the schema is the set of promises your software makes about what is true, how things relate, and what can change, and those promises reach into every line of code you will ever write against them. The interface is paint. The data model is the building.

Why the schema outlives everything else

The reason a data model is so expensive to get wrong is that it is the most shared thing in your system. A button is touched by one screen. A table is touched by every feature that reads or writes it, every report, every export, every integration, every migration, and eventually every customer who has years of data shaped by it. When you change a button, you change a button. When you change a fundamental relationship in your schema, you are negotiating with the entire history of your application at once.

This is why database design is not a phase that ends. It is a constraint you live inside. Get the core entities and their relationships right and most features become small, because the model already expresses what they need. Get them wrong and every feature carries a tax: a join that should not exist, a field overloaded to mean three things, a status column that has quietly become a state machine nobody designed. None of that shows up in a demo. All of it shows up two years later as the reason simple requests take a quarter.

Example: a team I worked with modeled a customer and a billing account as the same record because, on day one, every customer had exactly one account. It demoed perfectly. The moment a single customer needed two accounts, that decision touched authentication, invoicing, permissions, and the audit log, because all of them had assumed the identity. The fix was not a feature. It was a six month migration that produced no new customer value, only the right to keep building.

The model is the product, even when users never see it

Customers do not look at your schema, so it is tempting to think they do not care about it. They care about it constantly, just indirectly. Every capability they love and every limitation they curse traces back to what the model can and cannot represent. When a customer says your tool is flexible, they are describing a data model that anticipated their case. When they say it is rigid, they are describing one that did not. The system architecture is the product, fully and literally, because the architecture is the set of things that are possible.

You can see this in any mature platform. The features that feel effortless to add are the ones the model already supports, and the features that feel impossible are the ones that would require rewriting what the data means. This is why two competing products with similar interfaces can have wildly different ceilings. The one with the better model can keep saying yes for years. The other one starts saying no, and the no comes dressed as a roadmap excuse, but the real reason is a schema that ran out of room.

When we designed the model behind Atlas, we knew tasks, projects, contracts, CRM records, and inbox items would all need to relate to one another, because real work does not respect product boundaries. A contract is attached to a deal which is attached to a company which has tasks against it. If we had modeled each surface as its own island, every cross-feature request would have become a special case. Modeling the relationships up front was the hardest thinking we did, and it is the reason features that look like they should require a rewrite usually require an afternoon.

How we actually spend the hard thinking

So what does it mean to invest in the model in practice. It is not over-engineering, and it is not abstracting everything into a generic blob that can represent anything and therefore expresses nothing. The discipline is the opposite of that. It is being precise about what things are and how they relate before you write the code that depends on it. Among our engineering practices, the data model is the one design conversation we refuse to rush.

A few principles guide that conversation. We name things for what they are, not for how the first feature uses them, because the feature changes and the name lingers. We make illegal states unrepresentable wherever we can, so the schema enforces truth rather than relying on every future developer to remember a rule. We treat a status field that has grown more than a handful of values as a design smell, usually a hidden state machine asking to be made explicit. And we write down the meaning of the model, not just its shape, because the relationships carry intent that a column list cannot convey.

We also accept that some model decisions are genuinely hard to reverse, and we slow down precisely for those. Software design is mostly the art of making changes cheap, but a small number of choices are expensive no matter what, and the core data model holds most of them. For those, an extra week of argument is the best money you will ever spend, because the alternative is the six month migration that ships nothing. I would rather lose a week now than a quarter in two years.

What this means for how you build

The practical takeaway is a sequencing one. Before you design the screen, design what is true. Get the entities, the relationships, and the constraints right, write down what they mean, and stress them against the cases you can foresee and a few you hope you never see. Then build the interface on top, fast and freely, because now you can change it without fear. Paint is cheap when the building is sound.

None of this argues against shipping quickly. It argues for spending your speed where it is recoverable and your caution where it is not. Move fast on the paint, the layouts, the flows, the copy, all the things you can change next week. Move slowly and deliberately on the model, because that is the part you cannot. The teams that confuse these two pay for it later, always in the same currency: a long, expensive migration that produces nothing a customer can see, undertaken only to win back the right to keep building. Get the data model right and you spend your years adding value. Get it wrong and you spend them apologizing to it.

F

Farhan

Farhan is the solo builder of wrxstack. He designs, writes, and ships Atlas and Portfolio on his own, and writes here about product, engineering, careers, and the craft of building software as one person.