Migrações de base de dados sem noites em claro
O código faz-se voltar atrás com um git revert. O esquema da base, não. É a assimetria que explica todas as regras que se seguem.
1. Repor a imagem anterior não repõe o esquema
Num deploy típico, as migrações correm no arranque e o código novo entra a seguir. Se algo correr mal e a imagem for revertida, fica código antigo sobre esquema novo. Se a migração era destrutiva, o código antigo procura uma coluna que já não existe.
É a primeira coisa a saber sobre a sua própria pipeline: o rollback é da imagem, não da base.
2. Aditivo primeiro, destrutivo muito depois
Acrescentar não parte nada: uma coluna nova que aceita nulos é invisível para o código antigo. Apagar e renomear é que partem. Daí a renomeação em três deploys:
-- Deploy 1: acrescentar, sem tocar no que existe
ALTER TABLE utilizadores ADD COLUMN email_novo VARCHAR(200);
-- (o código passa a escrever nos dois e a ler do antigo)
-- Deploy 2: copiar o que falta, e ler do novo
UPDATE utilizadores SET email_novo = email WHERE email_novo IS NULL;
-- Deploy 3: só quando nada mais lê a antiga
ALTER TABLE utilizadores DROP COLUMN email;É mais trabalhoso do que um RENAME, e é a diferença entre um deploy aborrecido e um minuto de erros 500 para toda a gente — porque durante um deploy há sempre um instante com as duas versões do código a correr ao mesmo tempo.
3. Migrações escrevem-se, não se geram e esquecem
As ferramentas geram o SQL a partir da diferença entre esquemas, e fazem-no bem — para acrescentar. Numa renomeação, o gerador não sabe que é uma renomeação: escreve um DROP e um CREATE, e leva os dados com ele.
Ler o SQL gerado antes de o aplicar é o hábito que separa quem já perdeu uma tabela de quem ainda não.
4. O ensaio
Correr a migração numa cópia dos dados de produção, e não numa base vazia. É com dados reais que aparecem os nulos que não deviam existir e os duplicados que impedem um índice único.
Contar antes e depois:
SELECT count(*)nas tabelas tocadas.Guardar à parte o que a migração vai destruir — nem que seja num ficheiro JSON — antes de a correr.
E, acima de tudo, uma cópia de segurança que já tenha sido restaurada pelo menos uma vez. Uma cópia por testar é uma esperança, não é uma cópia.