Zpět do magazínu
Databáze10.04.2026
Databáze

Migrace databáze bez paniky: jak měnit schéma na produkci.

Změna schématu na běžící produkci nahání hrůzu. Ukážu ti postup, díky kterému migrace proběhne bez výpadku a bez ztráty dat.

PB
Petr Bláha
Autor
10. duben 2026
Publikováno
8 min
Čtení

Měnit databázi, na které visí živá data a platící uživatelé, je jedna z nejvíc stresujících věcí ve vývoji. Jeden špatný příkaz a smažeš sloupec, který nešel vrátit. Sám jsem si pár takových momentů zažil, a tak ti dám postup, díky kterému migrace zvládneš v klidu, bez výpadku a bez ztrát.

Co je migrace a proč ji neobejdeš

Migrace je řízená změna struktury databáze: přidání tabulky, nového sloupce, indexu nebo úprava typu. Jakmile máš aplikaci v produkci, nemůžeš jen tak otevřít databázi a něco v ní ručně překlikat. Potřebuješ změny dělat předvídatelně, opakovatelně a tak, aby je bylo možné zopakovat na všech prostředích úplně stejně, od tvého počítače po produkci.

Zlaté pravidlo: vždy záloha

Než spustíš cokoli, co mění strukturu nebo data, udělej zálohu. Supabase i většina hostovaných databází zálohuje automaticky, ale spolehnout se jen na to je hazard. Před větší migrací si zálohu udělej ručně a hlavně ověř, že jde obnovit. Záloha, kterou jsi nikdy nezkusil obnovit, není záloha, je to jen falešný pocit bezpečí.

Piš migrace idempotentně

Idempotentní znamená, že migraci můžeš spustit vícekrát po sobě a nic se nerozbije. To je v praxi k nezaplacení, protože nikdy přesně nevíš, jestli předchozí pokus prošel celý, nebo spadl v půlce kvůli výpadku spojení.

-- špatně: druhé spuštění spadne, sloupec už existuje
ALTER TABLE users ADD COLUMN phone TEXT;

-- dobře: bezpečné spustit kolikrát chceš
ALTER TABLE users ADD COLUMN IF NOT EXISTS phone TEXT;
-- stejný princip u tabulek a indexů
CREATE TABLE IF NOT EXISTS notifications (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID NOT NULL REFERENCES users(id)
);

CREATE INDEX IF NOT EXISTS idx_notifications_user
  ON notifications(user_id);

Bezpečné versus nebezpečné změny

Ne všechny migrace jsou stejně riskantní. Některé jsou v podstatě neškodné, jiné mohou zlikvidovat data nebo shodit aplikaci, protože starý kód najednou hledá sloupec, který už není. Tady je rozdělení, podle kterého se orientuju.

Typ změnyRizikoNa co dát pozor
Přidání sloupce (nullable)NízkéSkoro vždy bezpečné
Přidání tabulky nebo indexuNízkéVelký index může na chvíli zatížit databázi
Přejmenování sloupceVysokéStarý kód přestane fungovat, dělej ve dvou krocích
Smazání sloupceVysokéNevratné, nejdřív přestaň používat v kódu
Změna typu sloupceVysokéData se mohou nevejít, otestuj na kopii

Postup, který tě nezklame

Když dělám migraci na produkci, držím se vždycky stejných kroků. Nuda je tady ctnost, kreativita patří jinam.

  1. Otestuj na kopii. Spusť migraci nejdřív na vývojové databázi se stejnou strukturou jako produkce.
  2. Udělej zálohu. A ověř, že jde obnovit.
  3. Spusť migraci v transakci. Když cokoli selže, databáze se vrátí do původního stavu, jako by se nic nestalo.
  4. Ověř výsledek. Zkontroluj, že data sedí a aplikace běží, ideálně automatickou kontrolou přímo v migraci.
BEGIN;

ALTER TABLE orders
  ADD COLUMN IF NOT EXISTS discount NUMERIC(10,2) DEFAULT 0;

-- kontrola, že to dopadlo podle očekávání
DO $do$
BEGIN
  IF NOT EXISTS (
    SELECT 1 FROM information_schema.columns
    WHERE table_name = 'orders' AND column_name = 'discount'
  ) THEN
    RAISE EXCEPTION 'sloupec discount se nepridal';
  END IF;
END $do$;

COMMIT;

Riskantní změny dělej ve dvou krocích

Přejmenování nebo smazání sloupce neřeš jedním tahem. Aplikace a databáze se nemění ve stejnou vteřinu, takže by ti chvíli něco nesedělo a uživatelé by viděli chyby. Místo toho postupuj postupně a obě verze nech chvíli žít vedle sebe.

  • Místo přejmenování: přidej nový sloupec, nasaď kód, který píše do obou, data dosypej, pak teprve starý odeber.
  • Místo rovnou smazání: nejdřív přestaň sloupec používat v kódu, počkej pár dní, jestli něco nespadne, a teprve potom ho odeber.
  • U velkých tabulek počítej s tím, že některé operace tabulku na chvíli zamknou. Dělej je v době nízkého provozu, ideálně v noci.

Tenhle opatrný přístup souvisí s tím, jak máš databázi navrženou. Když máš dobré databázové schéma, riskantních migrací budeš potřebovat mnohem méně, protože většinu věcí předvídáš dopředu.

Měj připravený plán návratu

I když všechno otestuješ, počítej s tím, že něco může jít jinak, než čekáš. Před každou větší migrací si dopředu rozmysli, jak ji vrátíš zpět, kdyby se ukázalo, že způsobila problém. U přidání sloupce je návrat snadný, prostě ho zase odebereš. U změny dat budeš potřebovat zálohu nebo opačnou migraci.

Sepsání návratového postupu ještě před spuštěním tě donutí domyslet rizika. Často při tom zjistíš, že migrace, kterou jsi považoval za neškodnou, vlastně jde vrátit jen těžko, a raději ji rozdělíš na bezpečnější kroky. Pár minut přemýšlení dopředu ti ušetří hodiny panického hašení požáru, až se na produkci něco pokazí. A věř mi, dřív nebo později se to stane každému, kdo migrace dělá dost dlouho, takže lepší být připravený než překvapený uprostřed noci.

Časté dotazy

Co když migrace spadne v půlce?

Přesně proto ji obaluješ do transakce s BEGIN a COMMIT. Když cokoli selže před COMMIT, databáze provede rollback a vrátí se do stavu před migrací. Nedojde k tomu, že polovina změn projde a polovina ne a ty zůstaneš v nedefinovaném stavu.

Jak verzovat migrace?

Ukládej je jako číslované soubory ve formátu jako 001_create_users.sql, 002_add_phone.sql a drž je v gitu. Díky tomu víš, co a v jakém pořadí se na databázi spustilo, a kolega nebo budoucí ty to zopakuje úplně stejně.

Můžu migrovat bez výpadku?

U většiny změn ano, pokud je děláš zpětně kompatibilně a po krocích. Přidání nullable sloupce nebo nové tabulky aplikaci neshodí. Problém dělají až destruktivní změny dělané jedním tahem v jednu chvíli.

Migrace na produkci jsou přesně ta chvíle, kdy se vyplatí mít vedle sebe někoho, kdo to dělal stokrát a ví, kde číhají miny. Pokud chceš měnit schéma s klidem a bez výpadků, ozvi se mi přes kontakt a projdeme to bezpečně.