KoderKoder.ai
PreçosEnterpriseEducaçãoPara investidores
EntrarComeçar

Produto

PreçosEnterprisePara investidores

Recursos

Fale conoscoSuporteEducaçãoBlog

Jurídico

Política de privacidadeTermos de usoSegurançaPolítica de uso aceitávelDenunciar abuso

Social

LinkedInTwitter
Koder.ai
Idioma

© 2026 Koder.ai. Todos os direitos reservados.

Início›Blog›Migrações PostgreSQL com Claude Code: prompts para mudanças seguras
05 de dez. de 2025·7 min

Migrações PostgreSQL com Claude Code: prompts para mudanças seguras

Aprenda prompts para migrações PostgreSQL com Claude Code: como fazer mudanças expand-contract seguras, backfills e planos de rollback, e o que verificar em staging antes do release.

Migrações PostgreSQL com Claude Code: prompts para mudanças seguras

O que torna uma mudança de esquema PostgreSQL arriscada\n\nUma mudança de esquema no PostgreSQL parece simples até encontrar tráfego real e dados reais. A parte arriscada geralmente não é o SQL em si. É quando o código do app, o estado do banco e o momento do deploy deixam de coincidir.\n\nA maior parte das falhas é prática e dolorosa: um deploy quebra porque código antigo toca uma nova coluna, uma migração tranca uma tabela quente e os timeouts disparam, ou uma mudança “rápida” silenciosamente apaga ou reescreve dados. Mesmo quando nada trava, você pode enviar bugs sutis como defaults errados, constraints quebradas ou índices que nunca terminaram de ser criados.\n\nMigrações geradas por IA adicionam outra camada de risco. Ferramentas podem produzir SQL válido que ainda é inseguro para sua carga de trabalho, volume de dados ou processo de release. Elas também podem chutar nomes de tabela, ignorar locks de longa duração ou minimizar o rollback porque migrar para baixo é difícil. Se você está usando Claude Code para migrações, precisa de guardrails e contexto concreto.\n\nQuando este post diz que uma mudança é “segura”, significa três coisas:\n\n- Compatível para trás: versões antigas e novas do app podem rodar durante o rollout.\n- Observável: você pode medir o progresso e detectar problemas rapidamente.\n- Reversível: você tem um plano de rollback que consegue executar sob pressão.\n\nO objetivo é que migrações vire trabalho rotineiro: previsível, testável e chato.\n\n## Regras de segurança a seguir antes de escrever qualquer prompt\n\nComece com algumas regras inegociáveis. Elas mantêm o modelo focado e evitam que você envie uma mudança que só funciona no seu laptop.\n\nDivida o trabalho em passos pequenos. Uma mudança de esquema, um backfill de dados, uma mudança no app e um passo de limpeza são riscos diferentes. Agrupar tudo torna mais difícil ver o que quebrou e mais difícil reverter.\n\nPrefira alterações aditivas antes das destrutivas. Adicionar coluna, índice ou tabela costuma ser de baixo risco. Renomear ou dropar objetos é onde ocorrem as quedas. Faça a parte segura primeiro, mova o app, e só remova o antigo quando tiver certeza de que não é mais usado.\n\nFaça o app tolerar as duas formas por um tempo. O código deveria ser capaz de ler tanto a coluna antiga quanto a nova durante o rollout. Isso evita a corrida comum onde alguns servidores executam código novo enquanto o banco ainda está no estado antigo (ou o contrário).\n\nTrate migrações como código de produção, não um script rápido. Mesmo que você esteja construindo em uma plataforma como Koder.ai (Go backend com PostgreSQL, mais clientes React ou Flutter), o banco é compartilhado por tudo. Erros custam caro.\n\nSe quiser um conjunto compacto de regras para colocar no topo de cada pedido de SQL, use algo como:\n\n- Uma mudança por migração: expand, depois backfill, depois trocar o código, depois limpar.\n- Evite locks longos: use criação de índice CONCURRENTLY e lotes pequenos para updates.\n- Requerer um plano de rollback para cada passo, incluindo como parar no meio de um backfill.\n- Requerer queries de verificação e métricas de sucesso (contagens de linhas, taxa de nulls, tempos).\n- Requerer um runbook: como rodar, o que observar e quem acionar.\n\nUm exemplo prático: em vez de renomear uma coluna da qual seu app depende, adicione a nova coluna, faça backfill lentamente, faça deploy do código que lê a nova e cai para a antiga, e só depois remova a coluna antiga.\n\n## O que incluir no seu prompt para que Claude Code fique com pé no chão\n\nClaude consegue gerar SQL decente a partir de um pedido vago, mas migrações seguras precisam de contexto. Trate seu prompt como um mini briefing de design: mostre o que existe, explique o que não pode quebrar e defina o que “seguro” significa para seu rollout.\n\nComece colando apenas os fatos do banco que importam. Inclua a definição da tabela mais indexes e constraints relevantes (primary keys, unique constraints, foreign keys, check constraints, triggers). Se tabelas relacionadas estiverem envolvidas, inclua esses trechos também. Um pequeno trecho preciso impede que o modelo adivinhe nomes ou perca uma constraint importante.\n\nAdicione escala do mundo real. Contagens de linhas, tamanho das tabelas, taxa de escrita e pico de tráfego devem alterar o plano. “200M rows and 1k writes/sec” é uma migração diferente de “20k rows and mostly reads.” Também inclua sua versão do Postgres e como as migrações rodam no seu sistema (transação única vs múltiplos passos).\n\nDescreva como a aplicação usa os dados: leituras importantes, gravações e jobs em background. Exemplos: “API lê por email”, “workers atualizam status”, ou “reports varrem por created_at”. Isso orienta se você precisa de expand/contract, feature flags e quão seguro será um backfill.\n\nFinalmente, seja explícito sobre restrições e entregáveis. Uma estrutura simples funciona bem:\n\n- Trechos do esquema atual e o objetivo\n- Suposições de escala (linhas, writes/sec, janela de manutenção se houver)\n- Dependências do app (queries/endpoints/jobs)\n- Restrições rígidas (sem downtime, evitar locks longos, evitar reescritas completas)\n- Entregáveis: SQL mais um plano em linguagem natural, verificação e rollback\n\nPedir tanto SQL quanto um plano de execução força o modelo a pensar sobre ordenação, risco e o que checar antes de enviar.\n\n## Expand/contract em português simples (e quando usar)\n\nO padrão expand/contract muda um banco PostgreSQL sem quebrar o app enquanto a mudança está em progresso. Em vez de um único switch arriscado, você faz o banco suportar as duas formas — antiga e nova — por um período.\n\nPense assim: adicione coisas novas com segurança (expand), mova tráfego e dados gradualmente e só então remova as peças antigas (contract). Isso é especialmente útil em trabalho assistido por IA porque força a planejar o meio confuso.\n\n### As quatro fases\n\nUm fluxo prático fica assim:\n\n- Expand: adicione uma nova coluna nullable ou tabela, adicione índice se necessário, e acrescente constraints de forma a evitar bloqueios (por exemplo, adicionar constraints como NOT VALID quando apropriado).\n- Compatibility: atualize o app para lidar com ambos os campos. Pode ser dual-write (gravar nos dois) ou read-fallback (ler novo e cair para o antigo).\n- Backfill: copie dados antigos para o novo em pequenos lotes, com checkpoints e modo de retomar.\n- Contract: quando tiver certeza que tudo usa o novo caminho, endureça regras (tornar coluna NOT NULL, validar constraints), então remova a coluna ou tabela antiga.\n\nUse esse padrão sempre que usuários ainda possam estar em código antigo enquanto o banco muda. Isso inclui deploys com múltiplas instâncias, apps móveis que atualizam devagar, ou qualquer release onde uma migração possa levar minutos ou horas.\n\nUma tática útil é planejar dois releases. Release 1 faz expand mais compatibility para que nada quebre se o backfill estiver incompleto. Release 2 faz o contract só depois de confirmar que o novo código e os novos dados estão no lugar.\n\n## Um template seguro de prompt para migrações expand/contract\n\nCopie este template e preencha os colchetes. Ele força o Claude Code a produzir SQL executável, checagens para provar que funcionou e um plano de rollback que você consegue seguir.\n\n\nYou are helping me plan a PostgreSQL expand-contract migration.\n\nContext\n- App: [what the feature does, who uses it]\n- Database: PostgreSQL [version if known]\n- Table sizes: [rough row counts], write rate: [low/medium/high]\n- Zero/near-zero downtime required: [yes/no]\n\nGoal\n- Change: [describe the schema change]\n- Current schema (relevant parts):\n [paste CREATE TABLE or \\d output]\n- How the app will change (expand phase and contract phase):\n - Expand: [new columns/indexes/triggers, dual-write, read preference]\n - Contract: [when/how we stop writing old fields and remove them]\n\nHard safety requirements\n- Prefer lock-safe operations. Avoid full table rewrites on large tables when possible.\n- If any step can block writes, call it out explicitly and suggest alternatives.\n- Use small, reversible steps. No “big bang” changes.\n\nDeliverables\n1) UP migration SQL (expand)\n - Use clear comments.\n - If you propose indexes, tell me if they should be created CONCURRENTLY.\n - If you propose constraints, tell me whether to add them NOT VALID then VALIDATE.\n\n2) Verification queries\n - Queries to confirm the new schema exists.\n - Queries to confirm data is being written to both old and new structures (if dual-write).\n - Queries to estimate whether the change caused bloat/slow queries/locks.\n\n3) Rollback plan (realistic)\n - DOWN migration SQL (only if it is truly safe).\n - If down is not safe, write a rollback runbook:\n - how to stop the app change\n - how to switch reads back\n - what data might be lost or need re-backfill\n\n4) Runbook notes\n - Exact order of operations (including app deploy steps).\n - What to monitor during the run (errors, latency, deadlocks, lock waits).\n - “Stop/continue” checkpoints.\n\nOutput format\n- Separate sections titled: UP.sql, VERIFY.sql, DOWN.sql (or ROLLBACK.md), RUNBOOK.md\n\n\nDuas linhas extras que ajudam na prática:\n\n- Peça para rotular qualquer passo que bloqueie escrita como RISK: blocks writes, além de quando executá-lo (off-peak vs anytime).\n- Force honestidade sobre locks: "If you're not sure whether a statement takes an ACCESS EXCLUSIVE lock, say so and offer a safer option."\n\n## Operações comuns de esquema e como pedir SQL mais seguro\n\nPequenas mudanças de esquema ainda podem prejudicar se pegarem locks longos, reescreverem tabelas grandes ou falharem no meio. Ao usar Claude Code para migrações, peça SQL que evite reescritas e mantenha seu app funcionando enquanto o banco alcança o novo estado.\n\n### Adicionar colunas e defaults (sem locks longos)\n\nAdicionar uma coluna nullable normalmente é seguro. Adicionar uma coluna com default não-nulo pode ser arriscado em versões antigas do Postgres porque pode reescrever a tabela inteira.\n\nUma abordagem mais segura é em dois passos: adicionar a coluna como NULL sem default, backfill em lotes, então definir o default para novas linhas e adicionar NOT NULL quando os dados estiverem limpos.\n\nSe precisar aplicar um default imediatamente, exija uma explicação do comportamento de lock para sua versão do Postgres e um plano de fallback se o tempo de execução for maior que o esperado.\n\n### Índices, FKs, constraints, drops\n\nPara índices em tabelas grandes, solicite CREATE INDEX CONCURRENTLY para que leitura e escrita continuem fluindo. Também exija uma nota de que não pode rodar dentro de um bloco de transação, o que significa que sua ferramenta de migração precisa de um passo não transacional.\n\nPara foreign keys, o caminho mais seguro costuma ser adicionar a constraint como NOT VALID primeiro, depois validar mais tarde. Isso torna a mudança inicial mais rápida ao mesmo tempo que impõe a FK para novos writes.\n\nAo tornar constraints mais rígidas (NOT NULL, UNIQUE, CHECK), peça por “limpar primeiro, impor depois.” A migração deve detectar linhas erradas, corrigi-las e só então ativar a regra mais rígida.\n\nSe quiser um checklist curto para colar em prompts, mantenha-o enxuto:\n\n- Incluir notas sobre locks e tempo de execução esperado.\n- Usar CONCURRENTLY para índices grandes e citar limites de transação.\n- Preferir NOT VALID então VALIDATE para novas FKs.\n- Separar backfill de impor NOT NULL/UNIQUE.\n- Dropar objetos somente após um ciclo completo de release e confirmação de que ninguém os lê.\n\n## Pedindo backfills lentos, constantes e recuperáveis\n\nBackfills são onde a maior parte da dor aparece, não o ALTER TABLE. Pedidos seguros tratam backfills como jobs controlados: mensuráveis, reiniciáveis e gentis com a produção.\n\nComece com checagens de aceitação fáceis de rodar e difíceis de contestar: contagens de linhas esperadas, taxa de null alvo e algumas checagens pontuais (por exemplo, comparar valores antigos vs novos para 20 IDs aleatórios).\n\nDepois peça um plano de batching. Batches mantêm locks curtos e reduzem surpresas. Um bom pedido especifica:\n\n- Como batchar (ranges de PK ou janelas de tempo como created_at)\n- Tamanho de lote alvo (por exemplo, 5.000 a 50.000 linhas)\n- Se deve pausar entre batches em tabelas quentes\n- Que cada batch seja uma transação clara e curta (não uma transação enorme)\n\nExija idempotência porque backfills falham no meio. O SQL deve ser seguro para re-executar sem duplicar ou corromper dados. Padrões típicos são “update apenas onde a nova coluna é NULL” ou uma regra determinística onde a mesma entrada sempre gera a mesma saída.\n\nTambém detalhe como o app se mantém correto enquanto o backfill roda. Se novas escritas continuarem chegando, precisa de uma ponte: dual-write no código do app, um trigger temporário, ou lógica read-fallback (ler novo quando presente, caso contrário o antigo). Diga qual abordagem você pode implantar com segurança.\n\nPor fim, inclua pause e resume no desenho. Peça por tracking de progresso e checkpoints, como uma tabela pequena que armazena o último ID processado e uma query que relata progresso (linhas atualizadas, último ID, hora).\n\nExemplo: você adiciona users.full_name derivado de first_name e last_name. Um backfill seguro atualiza apenas linhas onde full_name IS NULL, roda em ranges de ID, registra o último ID atualizado e mantém novos cadastros corretos via dual-write até o switch-over completo.\n\n## Como pedir planos de rollback que funcionem na prática\n\nUm plano de rollback não é só “escreva um down migration.” São dois problemas: desfazer a mudança de esquema e lidar com quaisquer dados que mudaram enquanto a versão nova estava ativa. Rollback de esquema costuma ser possível. Rollback de dados frequentemente não é, a menos que você tenha planejado.\n\nSeja explícito sobre o que rollback significa para sua mudança. Se você está dropando uma coluna ou reescrevendo valores no lugar, exija uma resposta realista como: “Rollback restaura compatibilidade do app, mas dados originais não podem ser recuperados sem um snapshot.” Essa honestidade é o que mantém você seguro.\n\nPeça gatilhos de rollback claros para que ninguém discuta durante um incidente. Exemplos:\n\n- Taxa de erro ou latência cruza um limite definido por 10 minutos\n- Um plano de query crítico regrediu (ex.: seq scan numa tabela quente)\n- Job de backfill fica atrasado por mais de N horas\n- Checagens de dados falham (nulls onde não devem, duplicatas, linhas faltando)\n- Um passo de migração bloqueia writes por mais de X segundos\n\nExija o pacote completo de rollback, não só SQL: DOWN migration SQL (só se seguro), passos no app para manter compatibilidade, e como parar jobs em background.\n\nEsse padrão de prompt geralmente é suficiente:\n\n\nProduce a rollback plan for this migration.\nInclude: down migration SQL, app config/code switches needed for compatibility, and the exact order of steps.\nState what can be rolled back (schema) vs what cannot (data) and what evidence we need before deciding.\nInclude rollback triggers with thresholds.\n\n\nAntes de enviar, capture um “safety snapshot” leve para comparar antes/depois:\n\n- Contagens de linhas das tabelas afetadas (e subconjuntos chave)\n- Um pequeno conjunto de queries de amostra com resultados esperados\n- Agregados simples (sum, min/max) para colunas tocadas\n- Uma lista curta de IDs para checagens spot antes e depois\n\nTambém seja claro sobre quando não reverter. Se você só adicionou uma coluna nullable e o app faz dual-write, um fix para frente (hotfix no código, pausar o backfill, depois retomar) costuma ser mais seguro que reverter e criar mais divergência.\n\n## Erros comuns a observar com migrações assistidas por IA\n\nIA pode escrever SQL rapidamente, mas não vê seu banco de produção. A maioria das falhas acontece quando o prompt é vago e o modelo preenche lacunas.\n\nUma armadilha comum é pular o esquema atual. Se você não colar a definição da tabela, indexes e constraints, o SQL pode mirar colunas que não existem ou ignorar uma regra de unicidade que torna o backfill lento e bloqueante.\n\nOutro erro é enviar expand, backfill e contract em um único deploy. Isso elimina sua rota de escape. Se o backfill demora ou falha no meio, você fica preso com um app esperando o estado final.\n\nAs falhas que mais aparecem:\n\n- Backfills que não são idempotentes e não têm rastreamento de progresso\n- Adicionar NOT NULL, UNIQUE ou FKs antes de limpar e validar dados\n- Transações longas sem lock timeouts ou statement timeouts\n- Sem queries de verificação, então problemas ficam escondidos até os usuários acharem\n\nUm exemplo concreto: “renomear uma coluna e atualizar o app.” Se o plano gerado renomeia e backfill em uma única transação, um backfill lento pode segurar locks e quebrar o tráfego ao vivo. Um prompt mais seguro força lotes pequenos, timeouts explícitos e queries de verificação antes de remover o caminho antigo.\n\n## O que verificar em staging antes de enviar\n\nStaging é onde você encontra problemas que nunca aparecem num banco dev pequeno: locks longos, nulls surpresa, índices faltando e caminhos de código esquecidos.\n\nPrimeiro, verifique que o esquema bate com o plano após a migração: colunas, tipos, defaults, constraints e índices. Uma olhada rápida não basta. Um índice faltando pode transformar um backfill seguro em um caos lento.\n\nDepois rode a migração contra um dataset realista. Idealmente é uma cópia recente do production com campos sensíveis mascarados. Se não puder, ao menos iguale volume e hotspots de produção (tabelas grandes, linhas largas, tabelas com muitos índices). Registre tempos de cada passo para saber o que esperar em produção.\n\nUm checklist curto para staging:\n\n- Esquema corresponde ao plano (colunas, tipos, constraints, índices)\n- Tempos registrados com volume de dados realista\n- Compatibilidade testada: app antigo com novo esquema e app novo com esquema antigo (quando o plano diz que deve funcionar)\n- Queries de verificação rodadas: taxas de null, contagens de linhas, checagem de órfãos para novas FKs, leituras de amostra\n- Sinais operacionais observados durante a execução: locks, deadlocks, timeouts, queries lentas\n\nPor fim, teste fluxos reais de usuário, não só SQL. Crie, atualize e leia registros tocados pela mudança. Se expand/contract for o plano, confirme que ambas formas funcionam até a limpeza final.\n\n## Um exemplo realista: mudar uma coluna sem quebrar usuários\n\nImagine que você tem users.name que guarda nomes completos como “Ada Lovelace.” Você quer first_name e last_name, mas não pode quebrar cadastros, perfis ou telas administrativas enquanto a mudança é rolada.\n\nComece com um passo de expand que seja seguro mesmo que nenhum código novo seja deployado ainda: adicione colunas nullable, mantenha a coluna antiga e evite locks longos.\n\nsql\nALTER TABLE users ADD COLUMN first_name text;\nALTER TABLE users ADD COLUMN last_name text;\n\n\nDepois atualize o comportamento do app para suportar os dois esquemas. No Release 1, o app deve ler das colunas novas quando presente, recorrer para name quando elas forem null, e gravar em ambos para que novos dados fiquem consistentes.\n\nO próximo passo é o backfill. Rode um job em batches que atualiza um pequeno conjunto de linhas por execução, registra progresso e pode ser pausado com segurança. Por exemplo: atualize users onde first_name é null em ordem de ID ascendente, 1.000 por vez, e registre quantas linhas mudaram.\n\nAntes de endurecer regras, valide em staging:\n\n- Novos cadastros preenchem first_name e last_name e continuam setando name\n- Usuários existentes exibem corretamente mesmo se apenas name estiver presente\n- Backfill pode parar e reiniciar sem duplicar trabalho\n- Não restam nulls inesperados após o fim do backfill\n- Queries básicas em users não ficam visivelmente mais lentas\n\nO Release 2 muda leituras para as colunas novas apenas. Só depois disso aplique constraints (como SET NOT NULL) e remova name, idealmente em um deploy separado posterior.\n\nPara rollback, mantenha simples. O app continua lendo name durante a transição, e o backfill é pausável. Se precisar reverter o Release 2, volte as leituras para name e deixe as colunas novas até estabilizar novamente.\n\n## Próximos passos: transforme seus prompts numa rotina repetível de migração\n\nTrate cada mudança como um pequeno runbook. O objetivo não é um prompt perfeito. É uma rotina que exige os detalhes certos: esquema, constraints, plano de execução e rollback.\n\nPadronize o que todo pedido de migração deve incluir:\n\n- Esquema atual e a mudança exata (tabelas, colunas, índices)\n- Restrições e fatos de tráfego (tamanho da tabela, taxa de escrita, downtime permitido)\n- Sequência de release (expand, deploy do app, backfill, contract)\n- Como você vai observar progresso (queries/métricas, tempo esperado)\n- Passos de rollback (o que reverter primeiro, quais dados podem ficar para trás)\n\nDecida quem é responsável por cada passo antes de alguém rodar SQL. Uma divisão simples evita “todo mundo achou que outra pessoa fez”: desenvolvedores cuidam do prompt e do código de migração, ops cuida do timing e monitoramento em produção, QA verifica comportamento em staging e casos de borda, e uma pessoa decide go/no-go.\n\nSe você está construindo apps via chat, pode ajudar esboçar a sequência antes de gerar qualquer SQL. Para times que usam Koder.ai, o Planning Mode é um lugar natural para escrever essa sequência, e snapshots mais rollback podem reduzir o blast radius se algo inesperado acontecer durante o rollout.\n\nDepois de enviar, agende o cleanup do contract logo enquanto o contexto está fresco, para que colunas antigas e código de compatibilidade temporário não fiquem por meses.

Perguntas frequentes

Why do PostgreSQL schema changes break production even when the SQL looks simple?

Uma mudança de esquema é arriscada quando o código do app, o estado do banco de dados e o momento do deploy deixam de coincidir.\n\nModos comuns de falha:\n\n- Código antigo do app acessa uma nova coluna/constraint e trava\n- Uma migração assume um lock forte numa tabela ocupada e as requisições time-out\n- Uma mudança “pequena” reescreve ou remove dados silenciosamente\n- Trabalho de índice/constraint leva muito mais tempo do que o esperado e causa queries lentas

What’s the safest default way to change a schema without downtime?

Use a abordagem expand/contract:\n\n- Expand: adicione colunas/tabelas/indexes novos e compatíveis (nullable, NOT VALID quando aplicável)\n- Compatibility: faça deploy do app que saiba ler/escrever as duas formas\n- Backfill: copie dados em pequenos lotes com checkpoints\n- Contract: aplique constraints e remova campos antigos apenas depois de um ciclo de release\n\nIsso mantém versões antigas e novas do app funcionando durante o rollout.

What extra risks do AI-generated migrations introduce?

O modelo pode gerar SQL válido, mas que é inseguro para sua carga de trabalho.\n\nRiscos típicos relacionados a IA:\n\n- Adivinhar nomes de tabelas/colunas ou esquecer uma constraint importante\n- Propor uma migração “big bang” que elimina opções de rollback\n- Ignorar comportamento de locks, limites de transação e builds de índice de longa duração\n- Simplificar demais o rollback (especialmente quando dados são transformados ou removidos)\n\nTrate a saída da IA como um rascunho e exija plano de execução, checagens e passos de rollback.

What should I paste into my prompt so Claude Code doesn’t guess?

Inclua apenas os fatos dos quais a migração depende:\n\n- Trechos relevantes de CREATE TABLE (mais indexes, FKs, UNIQUE/CHECK, triggers)\n- Versão do Postgres e como as migrações rodam (transação única vs passos múltiplos)\n- Escala: contagem de linhas, tamanho da tabela, taxa de escrita, pico de tráfego\n- Como o app usa os dados (reads/writes/jobs críticos)\n- Restrições rígidas (sem downtime, evitar reescritas completas, limites de lock)\n- Entregáveis: UP SQL + queries de verificação + plano de rollback + runbook\n\nIsso evita adivinhações e força a ordem correta.

Should I combine schema changes and backfills in one migration?

Regra padrão: separe-os.\n\nDivisão prática:\n\n- Migração 1: expand (novas colunas/tabelas, talvez constraints NOT VALID)\n- Deploy do app: código de compatibilidade (read-fallback ou dual-write)\n- Job de backfill: updates em lotes com rastreamento de progresso\n- Migração 2: contract (VALIDATE, SET NOT NULL, drop old columns)\n\nAgrupar tudo torna falhas mais difíceis de diagnosticar e reverter.

How do I add a new column with a default without causing long locks?

Prefira este padrão:\n\n1) ADD COLUMN ... NULL sem default (rápido)\n2) Backfill em lotes\n3) Definir default para novas linhas\n4) Adicionar NOT NULL só após verificação\n\nAdicionar um default não-nulo pode ser arriscado em algumas versões porque pode reescrever a tabela inteira. Se precisar do default imediato, peça notas sobre locks/tempo de execução e um fallback seguro.

When should I use CREATE INDEX CONCURRENTLY, and what’s the catch?

Peça:\n\n- CREATE INDEX CONCURRENTLY para tabelas grandes/quentes\n- Uma observação de que não pode rodar dentro de um bloco de transação (sua ferramenta precisa suportar isso)\n- Tempo estimado e o que monitorar (espera de locks, latência de queries)\n\nPara verificação, inclua um check simples de que o índice existe e está sendo usado (por exemplo, compare um EXPLAIN antes/depois em staging).

What’s the safest way to add a foreign key on a large table?

Use NOT VALID primeiro, depois valide:\n\n- Adicione a FK como NOT VALID para que o passo inicial seja menos disruptivo\n- Rode VALIDATE CONSTRAINT em um passo separado quando puder observar\n\nIsso ainda garante a FK para novos writes, enquanto controla quando a validação cara acontece.

How do I prompt for a backfill that won’t melt production and can resume?

Um bom backfill é em lotes, idempotente e reiniciável.\n\nRequisitos práticos:\n\n- Fazer batch por ranges de PK ou janelas de tempo\n- Atualizar apenas linhas que ainda precisam (ex.: WHERE new_col IS NULL)\n- Manter batches em transações curtas; opcionalmente pausar entre batches\n- Rastrear progresso (último ID processado, linhas atualizadas, hora de início)\n- Garantir que o app continue correto enquanto o backfill roda (dual-write, trigger temporária ou read-fallback)\n\nIsso torna backfills suportáveis sob tráfego real.

What does a realistic rollback plan look like for schema changes?

Objetivo padrão de rollback: restaurar compatibilidade do app rapidamente, mesmo que os dados não voltem perfeitamente.\n\nUm plano de rollback viável deve incluir:\n\n- Se o DOWN SQL é realmente seguro; se não, um runbook em vez disso\n- A ordem exata: parar/pausar jobs de backfill, deploy do ajuste no app, depois passos de esquema\n- Triggers claros de rollback (taxa de erro, latência, espera de locks, checagens de dados falhando)\n- Declaração do que pode ser revertido (esquema) vs o que não pode (dados)\n\nFrequentemente o rollback mais seguro é voltar a leitura para o campo antigo enquanto deixa as colunas novas no lugar.

Sumário
O que torna uma mudança de esquema PostgreSQL arriscada\n\nUma mudança de esquema no PostgreSQL parece simples até encontrar tráfego real e dados reais. A parte arriscada geralmente não é o SQL em si. É quando o código do app, o estado do banco e o momento do deploy deixam de coincidir.\n\nA maior parte das falhas é prática e dolorosa: um deploy quebra porque código antigo toca uma nova coluna, uma migração tranca uma tabela quente e os timeouts disparam, ou uma mudança “rápida” silenciosamente apaga ou reescreve dados. Mesmo quando nada trava, você pode enviar bugs sutis como defaults errados, constraints quebradas ou índices que nunca terminaram de ser criados.\n\nMigrações geradas por IA adicionam outra camada de risco. Ferramentas podem produzir SQL válido que ainda é inseguro para sua carga de trabalho, volume de dados ou processo de release. Elas também podem chutar nomes de tabela, ignorar locks de longa duração ou minimizar o rollback porque migrar para baixo é difícil. Se você está usando Claude Code para migrações, precisa de guardrails e contexto concreto.\n\nQuando este post diz que uma mudança é “segura”, significa três coisas:\n\n- Compatível para trás: versões antigas e novas do app podem rodar durante o rollout.\n- Observável: você pode medir o progresso e detectar problemas rapidamente.\n- Reversível: você tem um plano de rollback que consegue executar sob pressão.\n\nO objetivo é que migrações vire trabalho rotineiro: previsível, testável e chato.\n\n## Regras de segurança a seguir antes de escrever qualquer prompt\n\nComece com algumas regras inegociáveis. Elas mantêm o modelo focado e evitam que você envie uma mudança que só funciona no seu laptop.\n\nDivida o trabalho em passos pequenos. Uma mudança de esquema, um backfill de dados, uma mudança no app e um passo de limpeza são riscos diferentes. Agrupar tudo torna mais difícil ver o que quebrou e mais difícil reverter.\n\nPrefira alterações aditivas antes das destrutivas. Adicionar coluna, índice ou tabela costuma ser de baixo risco. Renomear ou dropar objetos é onde ocorrem as quedas. Faça a parte segura primeiro, mova o app, e só remova o antigo quando tiver certeza de que não é mais usado.\n\nFaça o app tolerar as duas formas por um tempo. O código deveria ser capaz de ler tanto a coluna antiga quanto a nova durante o rollout. Isso evita a corrida comum onde alguns servidores executam código novo enquanto o banco ainda está no estado antigo (ou o contrário).\n\nTrate migrações como código de produção, não um script rápido. Mesmo que você esteja construindo em uma plataforma como Koder.ai (Go backend com PostgreSQL, mais clientes React ou Flutter), o banco é compartilhado por tudo. Erros custam caro.\n\nSe quiser um conjunto compacto de regras para colocar no topo de cada pedido de SQL, use algo como:\n\n- Uma mudança por migração: expand, depois backfill, depois trocar o código, depois limpar.\n- Evite locks longos: use criação de índice CONCURRENTLY e lotes pequenos para updates.\n- Requerer um plano de rollback para cada passo, incluindo como parar no meio de um backfill.\n- Requerer queries de verificação e métricas de sucesso (contagens de linhas, taxa de nulls, tempos).\n- Requerer um runbook: como rodar, o que observar e quem acionar.\n\nUm exemplo prático: em vez de renomear uma coluna da qual seu app depende, adicione a nova coluna, faça backfill lentamente, faça deploy do código que lê a nova e cai para a antiga, e só depois remova a coluna antiga.\n\n## O que incluir no seu prompt para que Claude Code fique com pé no chão\n\nClaude consegue gerar SQL decente a partir de um pedido vago, mas migrações seguras precisam de contexto. Trate seu prompt como um mini briefing de design: mostre o que existe, explique o que não pode quebrar e defina o que “seguro” significa para seu rollout.\n\nComece colando apenas os fatos do banco que importam. Inclua a definição da tabela mais indexes e constraints relevantes (primary keys, unique constraints, foreign keys, check constraints, triggers). Se tabelas relacionadas estiverem envolvidas, inclua esses trechos também. Um pequeno trecho preciso impede que o modelo adivinhe nomes ou perca uma constraint importante.\n\nAdicione escala do mundo real. Contagens de linhas, tamanho das tabelas, taxa de escrita e pico de tráfego devem alterar o plano. “200M rows and 1k writes/sec” é uma migração diferente de “20k rows and mostly reads.” Também inclua sua versão do Postgres e como as migrações rodam no seu sistema (transação única vs múltiplos passos).\n\nDescreva como a aplicação usa os dados: leituras importantes, gravações e jobs em background. Exemplos: “API lê por email”, “workers atualizam status”, ou “reports varrem por created_at”. Isso orienta se você precisa de expand/contract, feature flags e quão seguro será um backfill.\n\nFinalmente, seja explícito sobre restrições e entregáveis. Uma estrutura simples funciona bem:\n\n- Trechos do esquema atual e o objetivo\n- Suposições de escala (linhas, writes/sec, janela de manutenção se houver)\n- Dependências do app (queries/endpoints/jobs)\n- Restrições rígidas (sem downtime, evitar locks longos, evitar reescritas completas)\n- Entregáveis: SQL mais um plano em linguagem natural, verificação e rollback\n\nPedir tanto SQL quanto um plano de execução força o modelo a pensar sobre ordenação, risco e o que checar antes de enviar.\n\n## Expand/contract em português simples (e quando usar)\n\nO padrão expand/contract muda um banco PostgreSQL sem quebrar o app enquanto a mudança está em progresso. Em vez de um único switch arriscado, você faz o banco suportar as duas formas — antiga e nova — por um período.\n\nPense assim: adicione coisas novas com segurança (expand), mova tráfego e dados gradualmente e só então remova as peças antigas (contract). Isso é especialmente útil em trabalho assistido por IA porque força a planejar o meio confuso.\n\n### As quatro fases\n\nUm fluxo prático fica assim:\n\n- Expand: adicione uma nova coluna nullable ou tabela, adicione índice se necessário, e acrescente constraints de forma a evitar bloqueios (por exemplo, adicionar constraints como NOT VALID quando apropriado).\n- Compatibility: atualize o app para lidar com ambos os campos. Pode ser dual-write (gravar nos dois) ou read-fallback (ler novo e cair para o antigo).\n- Backfill: copie dados antigos para o novo em pequenos lotes, com checkpoints e modo de retomar.\n- Contract: quando tiver certeza que tudo usa o novo caminho, endureça regras (tornar coluna NOT NULL, validar constraints), então remova a coluna ou tabela antiga.\n\nUse esse padrão sempre que usuários ainda possam estar em código antigo enquanto o banco muda. Isso inclui deploys com múltiplas instâncias, apps móveis que atualizam devagar, ou qualquer release onde uma migração possa levar minutos ou horas.\n\nUma tática útil é planejar dois releases. Release 1 faz expand mais compatibility para que nada quebre se o backfill estiver incompleto. Release 2 faz o contract só depois de confirmar que o novo código e os novos dados estão no lugar.\n\n## Um template seguro de prompt para migrações expand/contract\n\nCopie este template e preencha os colchetes. Ele força o Claude Code a produzir SQL executável, checagens para provar que funcionou e um plano de rollback que você consegue seguir.\n\n```\nYou are helping me plan a PostgreSQL expand-contract migration.\n\nContext\n- App: [what the feature does, who uses it]\n- Database: PostgreSQL [version if known]\n- Table sizes: [rough row counts], write rate: [low/medium/high]\n- Zero/near-zero downtime required: [yes/no]\n\nGoal\n- Change: [describe the schema change]\n- Current schema (relevant parts):\n [paste CREATE TABLE or \\d output]\n- How the app will change (expand phase and contract phase):\n - Expand: [new columns/indexes/triggers, dual-write, read preference]\n - Contract: [when/how we stop writing old fields and remove them]\n\nHard safety requirements\n- Prefer lock-safe operations. Avoid full table rewrites on large tables when possible.\n- If any step can block writes, call it out explicitly and suggest alternatives.\n- Use small, reversible steps. No “big bang” changes.\n\nDeliverables\n1) UP migration SQL (expand)\n - Use clear comments.\n - If you propose indexes, tell me if they should be created CONCURRENTLY.\n - If you propose constraints, tell me whether to add them NOT VALID then VALIDATE.\n\n2) Verification queries\n - Queries to confirm the new schema exists.\n - Queries to confirm data is being written to both old and new structures (if dual-write).\n - Queries to estimate whether the change caused bloat/slow queries/locks.\n\n3) Rollback plan (realistic)\n - DOWN migration SQL (only if it is truly safe).\n - If down is not safe, write a rollback runbook:\n - how to stop the app change\n - how to switch reads back\n - what data might be lost or need re-backfill\n\n4) Runbook notes\n - Exact order of operations (including app deploy steps).\n - What to monitor during the run (errors, latency, deadlocks, lock waits).\n - “Stop/continue” checkpoints.\n\nOutput format\n- Separate sections titled: UP.sql, VERIFY.sql, DOWN.sql (or ROLLBACK.md), RUNBOOK.md\n```\n\nDuas linhas extras que ajudam na prática:\n\n- Peça para rotular qualquer passo que bloqueie escrita como `RISK: blocks writes`, além de quando executá-lo (off-peak vs anytime).\n- Force honestidade sobre locks: "If you're not sure whether a statement takes an ACCESS EXCLUSIVE lock, say so and offer a safer option."\n\n## Operações comuns de esquema e como pedir SQL mais seguro\n\nPequenas mudanças de esquema ainda podem prejudicar se pegarem locks longos, reescreverem tabelas grandes ou falharem no meio. Ao usar Claude Code para migrações, peça SQL que evite reescritas e mantenha seu app funcionando enquanto o banco alcança o novo estado.\n\n### Adicionar colunas e defaults (sem locks longos)\n\nAdicionar uma coluna nullable normalmente é seguro. Adicionar uma coluna com default não-nulo pode ser arriscado em versões antigas do Postgres porque pode reescrever a tabela inteira.\n\nUma abordagem mais segura é em dois passos: adicionar a coluna como NULL sem default, backfill em lotes, então definir o default para novas linhas e adicionar NOT NULL quando os dados estiverem limpos.\n\nSe precisar aplicar um default imediatamente, exija uma explicação do comportamento de lock para sua versão do Postgres e um plano de fallback se o tempo de execução for maior que o esperado.\n\n### Índices, FKs, constraints, drops\n\nPara índices em tabelas grandes, solicite `CREATE INDEX CONCURRENTLY` para que leitura e escrita continuem fluindo. Também exija uma nota de que não pode rodar dentro de um bloco de transação, o que significa que sua ferramenta de migração precisa de um passo não transacional.\n\nPara foreign keys, o caminho mais seguro costuma ser adicionar a constraint como `NOT VALID` primeiro, depois validar mais tarde. Isso torna a mudança inicial mais rápida ao mesmo tempo que impõe a FK para novos writes.\n\nAo tornar constraints mais rígidas (NOT NULL, UNIQUE, CHECK), peça por “limpar primeiro, impor depois.” A migração deve detectar linhas erradas, corrigi-las e só então ativar a regra mais rígida.\n\nSe quiser um checklist curto para colar em prompts, mantenha-o enxuto:\n\n- Incluir notas sobre locks e tempo de execução esperado.\n- Usar CONCURRENTLY para índices grandes e citar limites de transação.\n- Preferir NOT VALID então VALIDATE para novas FKs.\n- Separar backfill de impor NOT NULL/UNIQUE.\n- Dropar objetos somente após um ciclo completo de release e confirmação de que ninguém os lê.\n\n## Pedindo backfills lentos, constantes e recuperáveis\n\nBackfills são onde a maior parte da dor aparece, não o `ALTER TABLE`. Pedidos seguros tratam backfills como jobs controlados: mensuráveis, reiniciáveis e gentis com a produção.\n\nComece com checagens de aceitação fáceis de rodar e difíceis de contestar: contagens de linhas esperadas, taxa de null alvo e algumas checagens pontuais (por exemplo, comparar valores antigos vs novos para 20 IDs aleatórios).\n\nDepois peça um plano de batching. Batches mantêm locks curtos e reduzem surpresas. Um bom pedido especifica:\n\n- Como batchar (ranges de PK ou janelas de tempo como created_at)\n- Tamanho de lote alvo (por exemplo, 5.000 a 50.000 linhas)\n- Se deve pausar entre batches em tabelas quentes\n- Que cada batch seja uma transação clara e curta (não uma transação enorme)\n\nExija idempotência porque backfills falham no meio. O SQL deve ser seguro para re-executar sem duplicar ou corromper dados. Padrões típicos são “update apenas onde a nova coluna é NULL” ou uma regra determinística onde a mesma entrada sempre gera a mesma saída.\n\nTambém detalhe como o app se mantém correto enquanto o backfill roda. Se novas escritas continuarem chegando, precisa de uma ponte: dual-write no código do app, um trigger temporário, ou lógica read-fallback (ler novo quando presente, caso contrário o antigo). Diga qual abordagem você pode implantar com segurança.\n\nPor fim, inclua pause e resume no desenho. Peça por tracking de progresso e checkpoints, como uma tabela pequena que armazena o último ID processado e uma query que relata progresso (linhas atualizadas, último ID, hora).\n\nExemplo: você adiciona `users.full_name` derivado de `first_name` e `last_name`. Um backfill seguro atualiza apenas linhas onde `full_name IS NULL`, roda em ranges de ID, registra o último ID atualizado e mantém novos cadastros corretos via dual-write até o switch-over completo.\n\n## Como pedir planos de rollback que funcionem na prática\n\nUm plano de rollback não é só “escreva um down migration.” São dois problemas: desfazer a mudança de esquema e lidar com quaisquer dados que mudaram enquanto a versão nova estava ativa. Rollback de esquema costuma ser possível. Rollback de dados frequentemente não é, a menos que você tenha planejado.\n\nSeja explícito sobre o que rollback significa para sua mudança. Se você está dropando uma coluna ou reescrevendo valores no lugar, exija uma resposta realista como: “Rollback restaura compatibilidade do app, mas dados originais não podem ser recuperados sem um snapshot.” Essa honestidade é o que mantém você seguro.\n\nPeça gatilhos de rollback claros para que ninguém discuta durante um incidente. Exemplos:\n\n- Taxa de erro ou latência cruza um limite definido por 10 minutos\n- Um plano de query crítico regrediu (ex.: seq scan numa tabela quente)\n- Job de backfill fica atrasado por mais de N horas\n- Checagens de dados falham (nulls onde não devem, duplicatas, linhas faltando)\n- Um passo de migração bloqueia writes por mais de X segundos\n\nExija o pacote completo de rollback, não só SQL: DOWN migration SQL (só se seguro), passos no app para manter compatibilidade, e como parar jobs em background.\n\nEsse padrão de prompt geralmente é suficiente:\n\n```\nProduce a rollback plan for this migration.\nInclude: down migration SQL, app config/code switches needed for compatibility, and the exact order of steps.\nState what can be rolled back (schema) vs what cannot (data) and what evidence we need before deciding.\nInclude rollback triggers with thresholds.\n```\n\nAntes de enviar, capture um “safety snapshot” leve para comparar antes/depois:\n\n- Contagens de linhas das tabelas afetadas (e subconjuntos chave)\n- Um pequeno conjunto de queries de amostra com resultados esperados\n- Agregados simples (sum, min/max) para colunas tocadas\n- Uma lista curta de IDs para checagens spot antes e depois\n\nTambém seja claro sobre quando não reverter. Se você só adicionou uma coluna nullable e o app faz dual-write, um fix para frente (hotfix no código, pausar o backfill, depois retomar) costuma ser mais seguro que reverter e criar mais divergência.\n\n## Erros comuns a observar com migrações assistidas por IA\n\nIA pode escrever SQL rapidamente, mas não vê seu banco de produção. A maioria das falhas acontece quando o prompt é vago e o modelo preenche lacunas.\n\nUma armadilha comum é pular o esquema atual. Se você não colar a definição da tabela, indexes e constraints, o SQL pode mirar colunas que não existem ou ignorar uma regra de unicidade que torna o backfill lento e bloqueante.\n\nOutro erro é enviar expand, backfill e contract em um único deploy. Isso elimina sua rota de escape. Se o backfill demora ou falha no meio, você fica preso com um app esperando o estado final.\n\nAs falhas que mais aparecem:\n\n- Backfills que não são idempotentes e não têm rastreamento de progresso\n- Adicionar NOT NULL, UNIQUE ou FKs antes de limpar e validar dados\n- Transações longas sem lock timeouts ou statement timeouts\n- Sem queries de verificação, então problemas ficam escondidos até os usuários acharem\n\nUm exemplo concreto: “renomear uma coluna e atualizar o app.” Se o plano gerado renomeia e backfill em uma única transação, um backfill lento pode segurar locks e quebrar o tráfego ao vivo. Um prompt mais seguro força lotes pequenos, timeouts explícitos e queries de verificação antes de remover o caminho antigo.\n\n## O que verificar em staging antes de enviar\n\nStaging é onde você encontra problemas que nunca aparecem num banco dev pequeno: locks longos, nulls surpresa, índices faltando e caminhos de código esquecidos.\n\nPrimeiro, verifique que o esquema bate com o plano após a migração: colunas, tipos, defaults, constraints e índices. Uma olhada rápida não basta. Um índice faltando pode transformar um backfill seguro em um caos lento.\n\nDepois rode a migração contra um dataset realista. Idealmente é uma cópia recente do production com campos sensíveis mascarados. Se não puder, ao menos iguale volume e hotspots de produção (tabelas grandes, linhas largas, tabelas com muitos índices). Registre tempos de cada passo para saber o que esperar em produção.\n\nUm checklist curto para staging:\n\n- Esquema corresponde ao plano (colunas, tipos, constraints, índices)\n- Tempos registrados com volume de dados realista\n- Compatibilidade testada: app antigo com novo esquema e app novo com esquema antigo (quando o plano diz que deve funcionar)\n- Queries de verificação rodadas: taxas de null, contagens de linhas, checagem de órfãos para novas FKs, leituras de amostra\n- Sinais operacionais observados durante a execução: locks, deadlocks, timeouts, queries lentas\n\nPor fim, teste fluxos reais de usuário, não só SQL. Crie, atualize e leia registros tocados pela mudança. Se expand/contract for o plano, confirme que ambas formas funcionam até a limpeza final.\n\n## Um exemplo realista: mudar uma coluna sem quebrar usuários\n\nImagine que você tem `users.name` que guarda nomes completos como “Ada Lovelace.” Você quer `first_name` e `last_name`, mas não pode quebrar cadastros, perfis ou telas administrativas enquanto a mudança é rolada.\n\nComece com um passo de expand que seja seguro mesmo que nenhum código novo seja deployado ainda: adicione colunas nullable, mantenha a coluna antiga e evite locks longos.\n\n```sql\nALTER TABLE users ADD COLUMN first_name text;\nALTER TABLE users ADD COLUMN last_name text;\n```\n\nDepois atualize o comportamento do app para suportar os dois esquemas. No Release 1, o app deve ler das colunas novas quando presente, recorrer para `name` quando elas forem null, e gravar em ambos para que novos dados fiquem consistentes.\n\nO próximo passo é o backfill. Rode um job em batches que atualiza um pequeno conjunto de linhas por execução, registra progresso e pode ser pausado com segurança. Por exemplo: atualize users onde `first_name` é null em ordem de ID ascendente, 1.000 por vez, e registre quantas linhas mudaram.\n\nAntes de endurecer regras, valide em staging:\n\n- Novos cadastros preenchem `first_name` e `last_name` e continuam setando `name`\n- Usuários existentes exibem corretamente mesmo se apenas `name` estiver presente\n- Backfill pode parar e reiniciar sem duplicar trabalho\n- Não restam nulls inesperados após o fim do backfill\n- Queries básicas em `users` não ficam visivelmente mais lentas\n\nO Release 2 muda leituras para as colunas novas apenas. Só depois disso aplique constraints (como `SET NOT NULL`) e remova `name`, idealmente em um deploy separado posterior.\n\nPara rollback, mantenha simples. O app continua lendo `name` durante a transição, e o backfill é pausável. Se precisar reverter o Release 2, volte as leituras para `name` e deixe as colunas novas até estabilizar novamente.\n\n## Próximos passos: transforme seus prompts numa rotina repetível de migração\n\nTrate cada mudança como um pequeno runbook. O objetivo não é um prompt perfeito. É uma rotina que exige os detalhes certos: esquema, constraints, plano de execução e rollback.\n\nPadronize o que todo pedido de migração deve incluir:\n\n- Esquema atual e a mudança exata (tabelas, colunas, índices)\n- Restrições e fatos de tráfego (tamanho da tabela, taxa de escrita, downtime permitido)\n- Sequência de release (expand, deploy do app, backfill, contract)\n- Como você vai observar progresso (queries/métricas, tempo esperado)\n- Passos de rollback (o que reverter primeiro, quais dados podem ficar para trás)\n\nDecida quem é responsável por cada passo antes de alguém rodar SQL. Uma divisão simples evita “todo mundo achou que outra pessoa fez”: desenvolvedores cuidam do prompt e do código de migração, ops cuida do timing e monitoramento em produção, QA verifica comportamento em staging e casos de borda, e uma pessoa decide go/no-go.\n\nSe você está construindo apps via chat, pode ajudar esboçar a sequência antes de gerar qualquer SQL. Para times que usam Koder.ai, o Planning Mode é um lugar natural para escrever essa sequência, e snapshots mais rollback podem reduzir o blast radius se algo inesperado acontecer durante o rollout.\n\nDepois de enviar, agende o cleanup do contract logo enquanto o contexto está fresco, para que colunas antigas e código de compatibilidade temporário não fiquem por meses.Perguntas frequentes
Compartilhar
Koder.ai
Crie seu próprio app com Koder hoje!

A melhor maneira de entender o poder do Koder é experimentar você mesmo.

Comece GrátisAgendar Demo