Changing a live schema without downtime
Add before you remove, backfill in the background, and never lock a table the running application still depends on. Single-tenant means doing this once per client, not once.

The short answer
- A schema change avoids downtime by being additive first. New columns, tables or indexes are added without touching anything the running application still reads or writes.
- Destructive changes, dropping or renaming something still in use, happen only after the application code has stopped depending on the old shape. Never in the same step as the addition.
- Backfilling existing rows into a new column happens in the background, in batches. It never holds a lock on a large table long enough to block normal reads and writes.
- Single-tenant architecture changes the scale of the problem. A schema change has to roll out safely across every client's separate Postgres project, not once against one shared database.
Why a schema change can take a database offline if done carelessly
A schema change that renames a column, drops a table, or runs a blocking operation on a large table can take an application offline in two ways. The first is a hard failure. Application code still expects a column that a migration just renamed or removed, so every query touching it starts erroring at once. The second is a soft failure. Some schema operations, such as adding a column with a default on a very large table in older Postgres versions, hold a lock that blocks reads and writes for the duration. Nothing errors, but it is an outage all the same.
Both failures are avoidable. The technique is decades old and well understood in database operations. It is worth naming because it gets skipped often enough to still cause real outages: never make a change in one step that both the application code and the database schema must update at the same moment.
The additive-first pattern in practice
A schema change that renames a column is really three changes, not one. First, add the new column beside the old one. Both exist, and the running application keeps working unmodified. Second, deploy application code that writes to both columns and reads from whichever one is populated. During this dual-write period, a background job backfills the new column for rows that only have the old one. Third, once the backfill is complete and the application no longer reads the old column, drop it. That is safe, because nothing depends on it anymore.
The backfill needs its own care. Updating every row in a large table in one transaction holds a lock for as long as the transaction runs, which causes the soft-failure downtime described above. Backfilling in small batches, with pauses between them, keeps each lock short enough that normal traffic is never blocked. The backfill takes longer in wall-clock time. For a table anyone is actively using, that is almost always the right trade.
What changes when the database is not shared
All of the above describes migrating one database safely. Single-tenant architecture, one Postgres project per client, adds another dimension. A schema change is not deployed once. It has to reach every client's separate project, correctly, without any instance ending up on a schema version the running code does not expect. A migration that is safe in principle can still cause an incident if it succeeds on most projects and silently fails, or partly applies, on one. That client's instance is then in a state nothing was tested against.
So a single-tenant product needs migration tooling that tracks the schema version of every client project independently. It applies changes with the same additive-first discipline, and it detects and alerts on an instance that falls out of sync, rather than assuming every rollout succeeded uniformly. That is a much larger operational surface than migrating one shared database. It is part of the honest cost of the single-tenant trade. The isolation and export guarantees a dedicated database per client buys come with a rollout discipline that has to scale with the number of clients.
Questions
- Why not only take the database offline briefly for a schema change?
- For a product people are actively using, any downtime window has a real cost, and it does not scale. A five-minute outage that is tolerable once a quarter becomes untenable when migrations happen weekly. Additive, backward-compatible migrations avoid needing that window at all.
- What makes a migration 'additive' rather than destructive?
- An additive migration adds a new column, table or index without removing or renaming anything the running application still depends on. A destructive one drops or renames a column the old code still reads. It breaks anything still running the previous version the moment it executes.
- Does single-tenant architecture make schema changes harder in every way?
- It makes rollout harder. A change has to reach every client's separate project reliably, instead of one shared database once. The migration technique itself is the same: additive-first design and background backfills apply the same way, repeated per project rather than done once.