Skip to content

Software

Why we settle the data model before the UI

Published
min read
2
Author
NEMO

Every custom software project has one decisive moment, and it is usually week two: the point where you decide how data is stored. Get it right and adding a feature six months later is a day’s work. Get it wrong and the same feature takes two weeks and breaks something else each time.

Why the data model is settled before the UI

The natural temptation is to start with screens, because screens are what you can show a client. But a screen reflects data, not the other way round. Start from the UI and you end up deriving the data model from those screens — and the entities that have no screen, like “price change history” or “the intermediate state of an order”, never get built at all.

Then someone asks what this product cost three months ago, and there is no answer, because that data was never stored.

Three questions we ask before the first table

What needs a history? Anything someone might ask about the past should be versioned rather than overwritten: prices, statuses, balances, assignments. Overwriting is cheap until the day it isn’t.

Where are the module boundaries? Inventory should work without knowing what “sales” is. If it can’t, you won’t be able to replace inventory alone later.

What is genuinely unique? Is a product code unique company-wide, or per branch? That answer is a simple index today and a painful migration next year.

Example: why status isn’t a column

Say an order has a status: placed, confirmed, shipped, delivered. The simplest thing is a status column. But the next question is almost always: when was it confirmed, who confirmed it, how long between confirmation and shipping?

# instead of this
class Order(models.Model):
    status = models.CharField(max_length=20)

# this
class OrderEvent(models.Model):
    order = models.ForeignKey(Order, related_name="events")
    status = models.CharField(max_length=20)
    actor = models.ForeignKey(User, null=True)
    at = models.DateTimeField(auto_now_add=True)

Current status becomes the latest event. The cost is one extra table; the benefit is that every question about timing and responsibility has an answer.

In short

Changing a table in week two is close to free. The same change in month six means data migration, rewritten reports and risk. That’s why we settle the data architecture before designing screens — not out of principle, but because that is the cheapest place to be wrong.

TagsDjangoArchitecture