Lesson 3.1 · 11 min read
Every product you've ever used remembers things. Your account, your posts, your orders, your messages, your settings, the fact that you liked that photo three years ago. All of it persists, survives you closing the app, and comes back exactly as you left it. That memory lives in a database, and the database is the most consequential and least visible part of any product.
Here's the claim this post is going to make, and it's one that should genuinely interest you as a designer. How you structure your data is a design decision, not merely a technical one. It determines which features are cheap to build and which are nearly impossible. It determines what questions your product can answer quickly and which ones it can't answer at all. Products are constrained by their data model in ways their teams often don't consciously realize, and by the time anyone notices, changing it is expensive and painful.
So this isn't a post about storage. It's about the invisible architecture of your product's memory, and about making it deliberately rather than by accident.
Start with the naive question, because the answer reveals what a database actually is.
You could just write your data to a file. Save all the users into a document, read it back when you need it. For about five minutes and one user, this works fine. Then reality arrives.
Two people sign up at the same instant, and both write to the file simultaneously, and one of them overwrites the other. A user is gone, forever, with no error. Your file grows to a million users, and to find one person's account you have to read the entire thing from top to bottom, taking seconds instead of milliseconds. The power cuts out mid-write and now the file is half-written garbage and your entire product's memory is corrupted. You want everyone's orders from last Tuesday over fifty dollars, and there's no way to ask that question except reading everything and checking each one by hand.
A database is a system built to solve exactly these problems and a hundred more you haven't thought of. It handles many people reading and writing at once without corrupting anything. It finds one record among millions almost instantly. It survives crashes without losing or mangling your data. It answers precise questions about your data quickly. It enforces rules about what's allowed to exist.
That's what you're getting: not a place to put things, but a system that guarantees your data stays correct, stays findable, and stays available under conditions that would destroy a file. Every one of those guarantees is hard-won and genuinely difficult to build, which is why almost nobody builds their own. You use a database, and you get decades of solved problems for free.
The most common kind of database, and the one worth understanding first and deepest, organizes data into tables, and if you've ever used a spreadsheet, you already know the shape.
A table holds one kind of thing. A users table holds users. A products table holds products. An orders table holds orders. Each table is dedicated to one type of entity in your product, which is your first structural decision: what things does my product have?
Within a table, each row is one individual instance of that thing. One row is one user. Another row is another user. A million rows, a million users. Rows are the actual records, the individual facts.
And each column is one property that every row has. The users table might have columns for id, name, email, and created date. Every user has each of those. The columns define the shape of a user in your system, what a user consists of, and this is your second structural decision: what does each thing consist of?
So a table is a grid: columns defining what properties exist, rows holding the actual instances. It's a spreadsheet with rules. And notice that you already met this shape without knowing it, back in the JSON post: a labeled collection of properties (a row) and a list of them (a table). The same structure, in a different costume.
One column deserves special attention, because it's the quiet backbone of everything: the id, also called the primary key. Every row gets a unique id, a value that belongs to that row and no other, forever. It's how you refer to one specific record with absolute precision. Two users can share a name. They cannot share an id. When your app fetches /api/orders/42, that 42 is an id, pointing at exactly one row in the orders table. The id is the true name of a record, and everything about how tables relate to each other, which is the subject of a later post, is built on ids.
Here's where this stops being storage and starts being design.
Before you store anything, you define the schema: the structure of your tables. What tables exist, what columns each has, what type of data each column holds (text, number, date, true-or-false), and what rules apply (must this be filled in? must it be unique?). The schema is the blueprint of your product's memory.
And the database enforces it. If your schema says every user must have an email, and it must be unique, then the database will physically refuse to store a user with no email, or a second user with an email that already exists. It will reject it, loudly. This is enormously valuable and worth pausing on. Your schema isn't a suggestion or a convention that everyone tries to follow. It's a guarantee, held by the database itself, that your data will always have the shape you declared. Bad data cannot get in. Compare that to storing data in a file, where anything at all can be written and nothing checks.
Think of a form you'd design. You decide what fields exist, which are required, what kind of input each accepts. Then the form enforces it: you cannot submit without filling in the required fields, you cannot type letters into the phone number. A schema is that, applied to your product's entire memory, enforced at the deepest level. Every piece of data that exists must conform. You are designing the constraints, and then something reliable holds them.
That word "in advance" is doing real work, though. You define the schema before the data exists, and changing it later, when a million rows already live in that shape, is a real operation with real cost and real risk. Which is exactly why the structure of your data is a decision worth making thoughtfully, and exactly why the next section matters more than almost anything else in this module.
Now the central argument. The way you model your data determines what your product can and cannot easily do. Let me make this concrete, because in the abstract it sounds like a platitude and in the specific it's a revelation.
Suppose you're building a product where users write posts. You decide, sensibly, that each post has an author, so you add a column to the posts table called author_name, and you store the person's name there. It works. Posts show their author. Ship it.
Now product asks for some perfectly reasonable things. Show me all posts by one author, on their profile page. That's awkward already, because you're matching on a name, and two people can have the same name, so you can't reliably tell them apart. Let the author change their display name. Now every one of their old posts still shows the old name, because you copied the name into each post rather than pointing at the person. To fix it you'd have to find and rewrite every post they ever wrote. Show the author's profile picture next to each post. You can't, because you stored their name and nothing else, so you have no way to get from a post back to the actual person.
Every one of those problems traces to a single early structural choice: storing a copy of the name instead of storing a reference to the user's id. Had you stored author_id instead, pointing at the user's row, every one of those features would be straightforward. The user changes their name once, in one place, and every post reflects it, because posts don't hold the name, they point at the person who holds it. Profile pictures, filtering by exact author, showing their bio, all trivially available, because there's a path from the post back to the user.
Same product, same features requested, and one early decision made them easy or made them expensive. That's the whole argument. The data model doesn't just store your product's features. It permits them. Features that fit the model are cheap. Features that fight the model are costly, and sometimes so costly they quietly get dropped, which means your data structure silently shaped what your product became.
You've seen this exact phenomenon in design. A component built with the right structure absorbs new requirements gracefully. One built with a hasty structure has to be torn apart the first time something changes. Naming and structure decisions made early, when they seem harmless, ripple through everything after. Data modeling is that same discipline, applied to the layer where the cost of getting it wrong is highest, because unlike a component, you can't just rebuild it, there are a million rows of real user data living inside it.
So how do you actually make these decisions well? The good news is the method is one you already have. You do research, you identify the real entities, you understand how they relate, and you design a structure that fits the truth of the domain rather than the convenience of the moment.
Ask what things genuinely exist in your product. Not screens, not features, but things: users, orders, products, messages, teams, comments. Each becomes a table. Getting this list right, identifying the real nouns of your domain, is most of the work.
Then ask what each thing genuinely consists of. What properties does every one of them have? Which are required to exist at all? Which must be unique? These become your columns and your rules, and answering them forces a clarity about your product that's valuable entirely on its own. You will discover, doing this, that you have unanswered questions about your own product. That's not a distraction. That's the exercise doing its job.
Then ask how they relate. Does a user have many orders? Does an order contain many products? Does a comment belong to exactly one post and one user? These relationships are the connective structure of your data, they're where the ids from earlier become essential, and they get an entire post of their own next, because they're where data modeling becomes genuinely interesting.
And through all of it, ask the question that separates a thoughtful model from a naive one: what will I want to ask of this data later? Not just what do I need to store, but what questions will I need answered. Show me a user's order history. Show me every product never ordered. Show me which posts got the most comments this week. If you can imagine the questions, you can check whether your structure can answer them, and you can fix the structure now, when it's free, rather than later, when it isn't.
When you ask an AI to build a feature, it will design a data model for you, quickly and without much deliberation, and it will look perfectly reasonable. It will work for what you asked. The trouble is that the data model is the one part of your application that is genuinely expensive to change later, and it's being decided in a moment, casually, based on a single feature request rather than on the shape of your product.
This is where you earn your title. You're the one who knows what the product is becoming, what features are coming, what questions will get asked. So you're the one who should look at the proposed tables and columns and ask the real questions. Is this storing a copy of something that should be a reference? Are these two things that should be separate tables actually crammed into one? Is there an id where there should be an id? When this feature I know is coming next quarter arrives, will this structure welcome it or fight it?
You can, and should, direct the data model deliberately: "make posts reference the user by id rather than storing the author's name," "these should be two separate tables with a relationship, not repeated columns," "every table needs a unique id and a created timestamp." That's not micromanaging the AI. That's making the single most consequential architectural decision in your application, on purpose, with knowledge of where the product is going, which is precisely the thing no AI can know and precisely the thing a design engineer should own.
The database is your product's memory. Its structure is the shape of what your product is able to remember, and therefore of what it's able to become. That's worth designing.
Do this before the next post
Pick a product you know well, ideally one you use daily, and design its data model on paper. Don't write code. Just draw boxes.
Start by listing the real things that exist in it. For a task app: users, tasks, projects, maybe teams and comments. Each of those is a table, so draw a box for each. Now fill in each box with the columns, what does a task actually consist of? An id, a title, a description, whether it's done, when it was created, a due date. Do this for every box, and force yourself to be specific about what's required and what's optional.
Then look at what you've drawn and ask the hard question. How does a task know which project it belongs to? How does a project know which team owns it? How does a comment know which task it's on, and who wrote it? Try to draw those connections, and notice that you'll naturally reach for ids to do it, one box pointing at another. You've just discovered relationships on your own, which is exactly where the next post begins.
Sit with your sketch and ask the questions your product would ask. Show me all of one user's incomplete tasks. Show me every task in this project, with the name of whoever created it. Can your structure answer them? If one is awkward, change the structure until it isn't. What you're doing right now, on paper, in twenty minutes, is the most important and least reversible design work in any application. Feel how different it is from designing a screen, and how much it decides.