Normalization
Method
A design method that splits data across several tables and links them by key so that no fact is written down in more than one place.
Why it exists
Back when everything went into one wide table, every order row carried the customer's company name, billing address, and contact email alongside the order itself. Eight thousand orders meant the same company name was typed out eight thousand times.
The trouble showed up the day that company changed its name. All eight thousand rows had to be found and edited, and missing a single one left the same customer sitting under two different names. Pull revenue by company after that and one account comes back split across two lines.
The leak ran the other way too. A company that had never placed an order had nowhere to live in the table at all, and cancelling a company's last order took its address and contact details down with it.
Pagedeck hit exactly this. Asked for a list of enterprise accounts, Theo went through the billing table and found one customer scattered across three spellings: "Pagedeck Inc.", "Pagedeck, Inc", and "Pagedeck". A table is the storage unit that holds data in rows and columns, and the company name had been typed straight into one of those columns. Theo merged the three by hand and sent the list, then had to do the same work again the following month.
Normalization is the design that stops this from happening in the first place instead of cleaning it up afterward. Write a fact in exactly one place and there is no second copy to drift out of sync.
How to run it
Lay out the tables you already have and work through five steps in order. Skipping a step leaves you without the information the next one needs.
- Write down what determines what. Knowing the order number fixes the order date; knowing the company number fixes the company name. That list is the raw material for everything that follows.
- Leave one value per cell. If a tags column has held "docs,collab,sharing" as a comma-separated string, move it to a separate table with one row per tag. While several values share a cell, even a question as simple as "which documents are tagged collab" turns into a text search.
- Keep only the values that depend on the whole primary key. A primary key is the column that picks out exactly one row. In a table keyed by order number plus product number, the product name is determined by the product number alone, so it depends on half the key. Move it to a product table. This step is called second normal form.
- Pull out columns that depend on a non-key column. If an order table carries both a company number and a company name, the name belongs to the company number rather than to the order. Give it its own table. That is third normal form (3NF), and when people say a schema is normalized they usually mean this far.
- Reconnect the tables you split with a foreign key. A foreign key is a column that points at a row in another table. The order row keeps only the company number, and the database itself checks that the number actually exists on the company side.
After five steps you have more tables than you started with. If showing a single order on screen now means reading four of them together, that is the moment to look at the schema again. A schema is the blueprint recording which tables exist and which columns each one holds.
When it fits and when it doesn't
It fits operational data that changes often and causes real damage when it goes wrong. Members, contracts, payments, and inventory are all tables that get edited rather than only appended to. A relational database is built to read split tables back together, so reassembling them is one SQL statement rather than a procedure someone writes by hand.
There are three limits worth knowing.
- Reads get slower, because the tables have to be matched up on every request. A dashboard that joins seven tables and takes more than five seconds to answer is paying the cost of the split.
- It doesn't suit facts that are already settled. The price on an order is the price at the moment of payment, so pointing that row at the product table means a later price increase silently rewrites old orders. Values like this are copied into the row on purpose.
- Every extra split costs effort. Forms beyond 3NF exist, but they rarely earn their keep in day-to-day product work.
So the order matters. Normalize to 3NF, start there, and reintroduce duplication only at the specific spots that turn out to be slow. Allow duplication from the beginning and you lose track of which copy is the original, which leaves nothing to reverse back toward.
How it sounds at work
"Let's get this to third normal form and start from there."
A proposal to split the tables to the standard level and not allow duplicates yet.
"The company name is sitting right in the orders table. Let's move it out and reference it by ID."
The same value is copied across many rows, so it should live in one place.
"We're denormalizing here. The dashboard is over five seconds."
Duplication is being reintroduced deliberately because a screen got slow.
The whole sequence played out at Pagedeck when the team added enterprise billing. The payments table Theo opened held the company name, business registration number, payment method, and contact email all in one row, and that table was the reason account names came back in three variants every time someone pulled a list.
Theo split out a company table and a contact table and left only the company number on each payment row. When a customer rebranded soon after, Sofia edited one row, and the past invoices and the current month's bill went out under the same name. The billing history screen did get a little slower, since it now reads three tables together, and that particular screen got an index to make up the difference. An index is a lookup written in advance, much like the one at the back of a book, telling the database where to read.
What people get wrong
The most common reading is that more splitting is always better design. The forms are numbered, so a higher number looks like a better state, which is a fair enough assumption. In practice 3NF is where most operational data stops, and going further is worth it only when a specific duplicate is actually causing trouble. Chase the number and you end up with two dozen tables while the data errors you were worried about keep happening at the same rate.
Reading it as a disk-saving technique is the other frequent mistake. Not repeating the same string eight thousand times does use less space, but storage is cheap enough now that the saving rarely changes a decision. What the technique is actually for is correctness. Miss that and you get judgments like "we have plenty of disk, so we can skip it" — and six months later someone is merging three spellings of one company by hand again.
Easily confused with
| Term | What it does | How it differs |
|---|---|---|
| Denormalization | Allows duplication on purpose for speed | Runs the opposite direction. The usual sequence is to normalize first, then reverse it only where reads are slow |
Where to go next
Last updated 2026-09-16