Migrations seguras em produção
O mesmo ALTER TABLE que roda instantâneo no seu banco de teste pode travar uma tabela real por minutos.
Numa tabela de teste com 200 linhas, ALTER TABLE usuarios ADD COLUMN documento VARCHAR(20) NOT NULL roda em milissegundos. Na mesma tabela em produção, com 5 milhões de linhas e tráfego real acontecendo ao mesmo tempo, o mesmo comando pode segurar um lock que trava leituras e escritas por minutos — e minutos de indisponibilidade em produção custam caro.
Por que NOT NULL numa tabela grande é arriscado
Adicionar uma coluna nova, sozinha e sem obrigatoriedade, costuma ser rápido. O problema aparece quando você exige que toda linha já existente respeite uma regra nova — como NOT NULL — o banco precisa varrer a tabela inteira, linha por linha, pra confirmar que nenhuma viola a regra antes de aceitar a constraint. Numa tabela pequena isso é instantâneo; numa tabela de milhões de linhas, é uma varredura completa segurando um lock enquanto roda.
Estratégia em etapas — a forma segura
ALTER TABLE usuarios ADD COLUMN documento VARCHAR(20);
-- sem NOT NULL: essa etapa é rápida mesmo numa tabela grandeUPDATE usuarios
SET documento = 'PENDENTE'
WHERE documento IS NULL AND id BETWEEN 1 AND 50000;
-- repete por faixas de id até cobrir a tabela inteira — em produção,
-- isso roda em background, em pedaços pequenos, nunca numa transação
-- gigante que trava a tabela do início ao fimALTER TABLE usuarios ALTER COLUMN documento SET NOT NULL;
-- agora essa validação é rápida: já não existe nenhuma linha com
-- documento vazio pra encontrar no meio da varreduraCada etapa é pequena e reversível separadamente — se algo der errado no backfill, dá pra pausar sem afetar a aplicação, já que a coluna ainda é opcional. Uma migration "tudo de uma vez" (criar a coluna já NOT NULL) não te dá esse meio-termo: ou funciona de primeira, ou trava a tabela tentando.
NOT VALID / VALIDATE CONSTRAINT — a mesma ideia pra CHECK
ALTER TABLE usuarios
ADD CONSTRAINT documento_nao_vazio CHECK (documento <> '') NOT VALID;
-- a constraint já vale pra todo INSERT/UPDATE novo a partir de agora,
-- sem varrer o dado existente nesse momento
ALTER TABLE usuarios VALIDATE CONSTRAINT documento_nao_vazio;
-- essa segunda etapa varre o dado já existente, mas com um lock mais
-- leve do que adicionar a constraint validada de uma vez sóPensando em rollback
Migrations em etapas pequenas facilitam reverter: tirar um NOT NULL recém-aplicado é rápido; apagar uma coluna nullable que ainda não é usada por nenhum código não quebra nada. O risco cresce quando uma migration remove ou renomeia algo que o código em produção ainda depende — o código antigo (rodando enquanto o deploy novo ainda não terminou) não pode quebrar por causa da migration mais nova.
Exercício
Você precisa tornar obrigatória (NOT NULL) a coluna telefone numa tabela usuarios com 5 milhões de linhas, em produção, sem causar indisponibilidade. Escreva o SQL essencial de cada etapa.