Noções Básicas de Migrações
7 exemplos para os fundamentos de migração de esquema - 5 básicos e 2 intermediários. SQL versionado no git é a única fonte de verdade para o que a produção se parece.
Pré-requisitos
mkdir -p db/migrations
git init # migrações vivem ao lado do código da aplicação- Uma tabela de histórico ordenada (
flyway_schema_history,liquibase.databasechangelogou customizada). - Nunca aplique DDL editado manualmente em produção sem o mesmo SQL em um arquivo de migração mesclado.
Exemplos Básicos
1. Primeiro Arquivo de Migração
-- db/migrations/V001__init_schema.sql
CREATE SCHEMA app AUTHORIZATION app_owner;
CREATE TABLE app.users (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
email text NOT NULL UNIQUE,
created_at timestamptz NOT NULL DEFAULT now()
);- O nome do arquivo carrega a ordem (
V001, prefixo de timestamp ou convenção da ferramenta). - Padrões idempotentes (
IF NOT EXISTS) são específicos da ferramenta - migrações versionadas do Flyway não são reexecutadas. - Revise migrações como código de aplicação em PRs.
2. Tabela de Histórico de Esquema
CREATE TABLE IF NOT EXISTS app.schema_migrations (
version text PRIMARY KEY,
applied_at timestamptz NOT NULL DEFAULT now(),
checksum text
);- O runner registra as versões aplicadas após o sucesso.
- O checksum detecta adulteração em arquivos já aplicados.
- Nunca delete linhas do histórico para "corrigir" um deploy - corrija para frente com uma nova migração.
Relacionado: Flyway & Liquibase - runners corporativos
3. Role de Migração Separada
CREATE ROLE app_owner NOLOGIN;
CREATE ROLE app_migrator LOGIN PASSWORD 'vault';
GRANT app_owner TO app_migrator;
-- Sessão de migração
SET ROLE app_owner;
CREATE TABLE app.orders (id bigint PRIMARY KEY);
RESET ROLE;- O
app_apiem tempo de execução não deve possuir tabelas ou ter direitos de DDL. - A CI de migração usa a mesma role que o deploy de produção para concessões idênticas.
- Linhas
SET ROLEpertencem ao wrapper de migração ou inicialização de conexão apenas para o migrador.
4. DDL Transacional (Quando Seguro)
BEGIN;
CREATE TABLE app.feature_flags (
key text PRIMARY KEY,
enabled boolean NOT NULL DEFAULT false
);
INSERT INTO app.feature_flags (key) VALUES ('new_checkout');
COMMIT;- A maioria do DDL no PostgreSQL é transacional - migrações falhas são revertidas.
- Exceções:
CREATE INDEX CONCURRENTLY,DROP INDEX CONCURRENTLY,VACUUM- não podem ser executadas dentro de um bloco de transação. - Divida instruções inseguras em arquivos de migração separados com notas operacionais.
5. Concessão na Mesma Migração
CREATE TABLE app.products (id bigint PRIMARY KEY, sku text NOT NULL);
GRANT SELECT ON app.products TO app_readers;
GRANT SELECT, INSERT, UPDATE, DELETE ON app.products TO app_writers;- Ou confie em
ALTER DEFAULT PRIVILEGESda migração de bootstrap. - Concessões ausentes causam sucesso no deploy, mas erros de permissão em tempo de execução.
- Teste a migração contra um banco de dados recém-criado na CI, não apenas em laptops de desenvolvimento.
Exemplos Intermediários
6. Expandir/Contrair Prévia
-- Expandir: adicionar coluna anulável (deploy seguro)
ALTER TABLE app.orders ADD COLUMN discount_cents integer;
-- App v2 escreve a coluna; um job de preenchimento preenche os valores
-- Contrair (migração posterior): impor NOT NULL após o preenchimento
ALTER TABLE app.orders ALTER COLUMN discount_cents SET NOT NULL;- Entregue mudanças aditivas antes das destrutivas.
- Deploy em duas fases: expansão do esquema, deploy da aplicação, preenchimento de dados, contração do esquema.
- Nunca remova uma coluna até que nenhum código a leia.
Relacionado: Padrão Expandir/Contrair - fluxo completo
7. Aplicar CI em Banco de Dados Efêmero
# trecho do pipeline
services:
postgres:
image: postgres:18.4
steps:
- run: flyway migrate -url=jdbc:postgresql://postgres:5432/test
- run: psql $TEST_URL -f ci/assert_schema.sql- Cada PR aplica a cadeia completa de migração em um banco de dados vazio.
- Captura bugs de ordenação e concessões ausentes antes do merge.
- Corresponda a versão principal do PostgreSQL na CI com a versão principal de destino da produção.
Versões da Stack: Esta página foi escrita para PostgreSQL 18.4 (estável 18, manutenção 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x e PostGIS 3.5+.