← The 16-day journey
Chapter 04 · Database, with Claude Code

The memory that must never lie

Your backend from Chapter 3 thinks, but forgets everything on restart. This chapter gives it permanent memory — and teaches you the one part of the system where mistakes cannot be shrugged off. Code bugs get fixed. Lost data is lost.

A calm stacked-cylinder vault with neat rows of blocks stored in visible layers, one block being placed by a thin beam.
Order, permanence, safekeeping. The database does one job and must do it perfectly.

01Why this part is different

Everything you have built so far can be thrown away and rebuilt. Delete the frontend — rebuild it tomorrow from the docs. Delete the backend — same. Delete the database and ten years of users, orders and records are gone forever. That asymmetry is why this chapter has a different tone. Frontend mistakes look bad. Backend mistakes behave badly. Database mistakes are permanent.

It is also why, of all the chapters, this is the one where you review Claude’s work most carefully. The AI will happily write a migration that drops a table. Your job is to be the person who reads it first.

02What database work is actually made of

  • Designing the schemadeciding what tables exist, what columns they have, and how they relate — the floor plan of the memory.
  • Queriesasking the memory questions and giving it updates — the SQL you saw in college, finally with a purpose.
  • Migrationschanging the floor plan of a building people already live in — safely, step by step, with a record.
  • Indexesthe difference between finding a name in a phone book and reading every page of it.
  • Safetyinjection attacks, least privilege, and the one-door rule from Chapter 3.
  • Backupsthe undo button for reality. Not optional. Ever.

03Tables and relationships, in plain words

A table is a strict Excel sheet: columns are decided in advance, every row follows them. The notice board needs two: users and notices. Every row gets an id — a unique number plate no other row shares.

Now the idea the whole relational world is built on. A notice must remember who wrote it. We do not copy the author’s name into the notice — names change, and copies drift apart. Instead the notice stores the author’s id: author_id = 7, meaning “written by user number 7”. That pointing column is a foreign key, and the arrangement is a relationship: one user, many notices. Store a fact in exactly one place, point at it from everywhere else — that single principle is most of database design.

Diagram · two tables, one relationship
usersidunique №nametextemailtext · uniquepassword_hashtextrolestudent | adminnoticesidunique №titletext · not emptybodytextauthor_idforeign keycreated_attimestamphiddentrue | false“written by user № …”one user → many notices · the fact is stored once, pointed at from everywhere
author_id points, never copies. Names can change in one place because they live in one place.

04Choosing a database: secondary, again

Same speech, third time, still true. We use PostgreSQL (“Postgres”) — free, battle- tested, and the most demanded relational database in job postings. You already know of MongoDB, which stores flexible documents instead of strict tables; it fits some problems well. But learn tables and relationships first: they force you to think about your data, and that thinking transfers everywhere — including to Mongo. For practice on your laptop, SQLite — a whole database living in a single file — is a perfectly honest starting point, and the concepts are identical.

05The data model doc — the spec, again

By now you know the move: before Claude touches anything, the thinking goes in a file. For the database it is docs/data-model.md — written in plain language first, not SQL:

docs/data-model.md
# Data model — notice board

## What must be remembered
- Users: name, college email (unique), hashed password, role
  (student | admin), when they joined.
- Notices: title, body, who wrote it, when, and whether it is
  hidden by an admin.

## Relationships
- One user writes many notices. A notice has exactly one author.
- Deleting a user does NOT delete their notices (college records
  stay); the notice keeps pointing at the removed user's id.

## Rules the database itself should enforce
- Emails unique. Titles never empty. Role only student or admin.

Then: “Read docs/data-model.md and draft the schema as a migration.” Claude turns your plain words into SQL — and you review the translation, not the typing. Check three things: do the column names match the words you used, are the rules you wrote enforced (unique, not-null), and is anything there you never asked for.

06Migrations — renovating an occupied building

Here is the problem migrations solve. Month two, you need a category column on notices. But the database is running, full of real rows. You cannot delete it and start again — that is the “lost forever” scenario. You have to renovate the building while people live in it.

A migration is a small numbered file describing one change: “001 create users and notices”, “002 add category to notices”. They live in git, run in order, and each runs exactly once — so every copy of the database (yours, your teammate’s, production) reaches the same shape by the same recorded steps. The iron rule that follows: nobody changes a database by hand — every change is a migration file. The AI era makes this stricter, not looser: Claude writes the migration, you read it, git remembers it.

migrations/ — the renovation diary
migrations/
  001_create_users_and_notices.sql
  002_add_category_to_notices.sql
  003_add_index_on_notices_created_at.sql

# each file: one change, runs once, in order, on every copy.
# reviewed like any diff — especially anything that DROPs or ALTERs.

07Indexes — why apps get slow

When the notices table has 200 rows, everything is fast no matter what you do. At two lakh rows, “show the newest notices” suddenly takes seconds — because without help, the database reads every row to answer. That full read has a name (a table scan) and a cure: an index — a sorted side-structure the database maintains so it can jump straight to the answer, like the index pages of a textbook.

The trade: indexes make reads fast and writes slightly slower, so you index the columns you search and sort by — not everything. And the professional move you can already do: when something is slow, ask the database to explain itself (Postgres literally has an EXPLAIN command) and ask Claude to interpret the output. “The app is slow” usually ends in a missing index — now you know the shape of the story.

08Safety — injection and the one-door rule

The most famous database attack is embarrassingly simple. If your backend builds a query by gluing user text into SQL, a user can type SQL as their input — a search box entry that ends with “; DELETE everything” — and the database will obey. That is SQL injection, and it has emptied real companies’ real tables.

The defence is one habit: user text is never glued into a query. It is passed separately (called a parameterised query) so the database treats it as plain data, never as a command. Put it in CLAUDE.md as a rule and check it in review. Combine with Chapter 3’s one-door rule — only src/db touches the database — and safety becomes something you can actually audit: one folder to read, one rule to check.

09Claude and your database — where the line is

How this fits the loop you know. The schema, migrations and seed data (fake rows for development) all live in the repo — so Claude reads and writes them like any code, and git remembers every change. For inspecting a running database there are MCP servers (a Postgres MCP, for instance) that let Claude look at real tables and run read-only queries — genuinely useful when debugging.

And one line you never cross, which you already believe in from Chapter 1: the AI never gets a door to production data. Claude works on dev, with fake rows, always. Not because the AI is malicious — because accidents in dev cost nothing and accidents in production are the one category of mistake this chapter exists to prevent. The same applies to you, by the way. Professionals with ten years of experience do not hand-run queries on production either.

10Backups — the undo button for reality

Migrations protect the shape of the data. Backups protect the data itself: a copy of the whole database, taken automatically on a schedule, stored somewhere else. Every serious system has them, and there is one lesson about them that people learn either from a mentor or from a disaster — I would rather you get it from a mentor: a backup you have never restored is a rumour, not a backup. Practising the restore once, on dev, is what turns it into an actual safety net.

11What you must still learn by hand, database edition

Learn to read SQL comfortably — SELECT, INSERT, UPDATE, JOIN — because every schema and migration Claude writes is a diff you must judge, and this chapter told you which diffs are permanent. Practise drawing a data model on paper from a plain-English description; “design the tables for X” is a guaranteed interview question and it is exactly section 05 without the AI. And treat anything containing DROP or DELETE as a stop-and-read-twice moment, forever.

12Do this today

Finish the build. Write docs/data-model.md for the notice board in your own words. Have Claude draft migration 001 and read every line of it. Wire src/db to a real database — SQLite is fine — and re-run your Chapter 3 tests until they pass again. Then the moment this was all for: post a notice from your frontend, restart the backend, and refresh. The notice is still there. Frontend asks, backend thinks, database remembers — the whole sentence from Chapter 1 is now something you have built, end to end, with your own reviewed code at every layer.

That is the entire classical system. What comes next in the journey is the new part — giving this system intelligence — and you now have every foundation the 16 days assume.