The Twelve-Factor App: O Manual Que Ainda Define Aplicações Cloud-Native
A metodologia 12-Factor descreve como construir aplicações que sobem em qualquer plataforma, escalam sem drama e implantam sem downtime. Fator a fator, com exemplos práticos.
Neste artigo
Antes de existir a palavra "cloud-native", já existia um documento que descrevia, sem citar Kubernetes ou containers, exatamente como uma aplicação precisa se comportar para viver bem nesse mundo. É o The Twelve-Factor App, escrito por engenheiros da Heroku por volta de 2011. Mais de uma década depois, quase todo antipadrão de operação que você vê em produção é a violação de um desses doze fatores.
A metodologia não é sobre linguagem, framework ou provedor. É um conjunto de restrições de design que, quando respeitadas, tornam a aplicação portável, escalável horizontalmente e descartável. Se você já sofreu para subir um serviço em um ambiente novo, ou viu um deploy quebrar porque "na minha máquina funcionava", provavelmente esbarrou na ausência de um desses fatores. Vamos percorrer os doze, agrupados por tema, com o olhar de quem opera infraestrutura.
Código, dependências e configuração#
Fator 1 — Base de código#
Uma aplicação corresponde a um repositório rastreado em controle de versão, do qual saem muitos deploys (dev, homologação, produção). Um repositório por app, e não vários apps compartilhando o mesmo repo de forma acoplada. Se dois serviços precisam do mesmo código, esse código vira uma dependência publicada, não um diretório copiado.
A violação clássica é o monólito que virou "vários serviços" mas continua sendo um único artefato que sobe inteiro. Cada deploy é a mesma base de código em uma revisão diferente; nunca bases divergentes rodando como se fossem o mesmo app.
Fator 2 — Dependências#
Declare todas as dependências explicitamente e isole-as. A aplicação nunca pode confiar na existência implícita de um pacote no sistema operacional hospedeiro. Nada de "mas o curl sempre está instalado".
``dockerfile # A imagem parte de uma base mínima e declara tudo que precisa. FROM python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . ``
O manifesto de dependências (requirements.txt, package.json, go.mod, Cargo.toml) é o contrato. O container é a materialização do isolamento: se a imagem sobe limpa em uma máquina sem nada instalado, o fator está cumprido.
Fator 3 — Configuração#
Tudo que varia entre deploys — credenciais de banco, URLs de serviços externos, chaves de API — mora no ambiente, nunca no código. O teste decisivo: você poderia tornar a base de código pública agora sem vazar nenhum segredo?
``bash # A aplicação lê do ambiente, não de um arquivo commitado. export DATABASE_URL="postgres://user:pass@db:5432/app" export REDIS_URL="redis://cache:6379/0" export LOG_LEVEL="info" ``
Config não é um arquivo config.production.yml versionado com senhas dentro. São variáveis de ambiente injetadas pela plataforma. Isso é o que permite o mesmo artefato subir em três ambientes diferentes sem recompilar nada — a imagem é imutável, o ambiente é que muda.
Serviços de apoio e o ciclo de vida#
Fator 4 — Serviços de apoio (backing services)#
Banco de dados, fila, cache, provedor de e-mail: todos são recursos anexados, acessados por uma URL na configuração. Não deve haver diferença de código entre usar um Postgres local e um RDS gerenciado. Trocar de provedor é trocar a string de conexão, não reescrever a aplicação.
Isso desacopla a aplicação da topologia. Se o banco cair e você precisar apontar para uma réplica, é uma mudança de configuração, não um redeploy com código novo.
Fator 5 — Build, release, run#
Separe rigidamente as três etapas:
- Build: converte o código em um artefato executável (a imagem do container).
- Release: combina o artefato com a configuração daquele ambiente.
- Run: executa o release no ambiente de destino.
``text código + deps --build--> artefato imutável artefato + config --release--> release v42 (com ID único) release v42 --run--> processos rodando ``
Cada release é imutável e recebe um identificador. Você nunca edita código na etapa de run — se algo está errado, faz um novo build e um novo release. É essa disciplina que torna o rollback trivial: basta rodar o release anterior, que continua intacto.
Fator 6 — Processos#
A aplicação executa como um ou mais processos stateless e share-nothing. Qualquer dado que precise persistir vai para um backing service (banco, cache). Nada de guardar sessão na memória do processo ou arquivo no disco local esperando que a próxima requisição caia no mesmo lugar.
Esse é talvez o fator com maior impacto operacional. Um processo stateless pode ser morto e recriado a qualquer momento sem perda de dados — que é exatamente o que Kubernetes faz o dia inteiro. Se sua aplicação guarda o carrinho de compras na memória local, ela quebra no segundo pod.
Concorrência, robustez e paridade#
Fator 7 — Vínculo de portas (port binding)#
A aplicação é autocontida e expõe seu serviço vinculando-se a uma porta. Ela não depende de um servidor de aplicação externo injetado em runtime (nada de "jogue o WAR dentro de um Tomcat pré-existente"). O próprio processo é o servidor web.
``bash # O processo escuta na porta que o ambiente definir. PORT=8080 ./meu-servico ``
Isso permite que uma aplicação seja backing service de outra: um serviço fala com outro por HTTP na porta vinculada, sem acoplamento de runtime.
Fator 8 — Concorrência#
Escale para fora (horizontal), adicionando mais processos, e não apenas para cima (vertical), engordando uma única instância. O modelo trata os processos como um conjunto: tipos diferentes de carga (requisições web, jobs de background) viram tipos diferentes de processo, cada um escalando de forma independente.
``text web=5 # cinco processos atendendo HTTP worker=3 # três processos consumindo a fila ``
Como os processos são stateless (fator 6), multiplicá-los é seguro. Isso é a base do autoscaling: quando o tráfego sobe, a plataforma sobe mais réplicas do tipo web; quando a fila cresce, sobe mais worker.
Fator 9 — Descartabilidade (disposability)#
Os processos devem subir rápido e desligar graciosamente. Um processo precisa estar pronto para receber tráfego em segundos, e precisa lidar com um SIGTERM parando de aceitar novas requisições, terminando as em andamento e liberando recursos.
```go // Ao receber SIGTERM, para de aceitar conexões e drena as ativas. ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM) defer stop()
<-ctx.Done() // sinal recebido shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() srv.Shutdown(shutdownCtx) // drena requisições em andamento ```
Descartabilidade é o que torna deploys, autoscaling e recuperação de falhas indolores. Se matar um processo corrompe estado ou perde requisições, você não tem um sistema resiliente — tem uma bomba-relógio. Jobs de background também devem ser reentráveis: se o worker morre no meio, a tarefa volta para a fila e é processada de novo sem efeito colateral duplicado (idempotência).
Fator 10 — Paridade dev/prod#
Mantenha desenvolvimento, homologação e produção o mais parecidos possível. As três lacunas que 12-Factor combate:
- Tempo: reduza o intervalo entre escrever código e implantá-lo (horas, não
semanas).
- Pessoal: quem escreve o código participa do deploy.
- Ferramenta: use o mesmo tipo de backing service em todos os ambientes.
Rodar SQLite em dev e Postgres em produção "porque é mais simples localmente" é convidar bugs que só aparecem em produção. Containers e IaC existem justamente para tornar os ambientes reproduzíveis e próximos.
Logs e processos administrativos#
Fator 11 — Logs#
Trate logs como fluxos de eventos. A aplicação nunca gerencia arquivos de log nem sabe para onde eles vão — ela apenas escreve, sem buffer, no stdout. O ambiente de execução é quem captura, agrega e roteia esse fluxo para o destino (Loki, Elasticsearch, um data warehouse).
``python # A aplicação só emite no stdout; roteamento é problema da plataforma. import sys, json print(json.dumps({"level": "info", "event": "pedido_criado", "id": 42}), file=sys.stdout, flush=True) ``
Isso desacopla a aplicação da infraestrutura de logging. Em um container, o stdout é coletado pelo runtime e enviado ao agregador. A aplicação que escreve direto em /var/log/app.log quebra esse modelo e some quando o container é descartado.
Fator 12 — Processos administrativos#
Tarefas pontuais de administração — migração de banco, um script de correção de dados, um console interativo — devem rodar como processos efêmeros no mesmo ambiente e no mesmo artefato do app. Nada de rodar a migração com uma cópia diferente do código na sua máquina.
``bash # A migração roda a partir da mesma imagem do release atual. kubectl run migrate --image=registry/app:v42 --restart=Never -- ./migrate up ``
Rodar o admin com o mesmo código e a mesma configuração garante que o schema que você migrou é o schema que a aplicação espera.
Além dos doze fatores#
A metodologia envelheceu bem, mas o mundo cloud-native adicionou preocupações que o documento original não cobre a fundo. Uma leitura moderna costuma estender a lista com fatores como: telemetria (métricas, traces e health checks como cidadãos de primeira classe, não só logs), autenticação e autorização tratadas como parte do contrato do serviço, e API-first — desenhar o contrato antes da implementação.
O ponto não é decorar os números. É perceber que cada fator remove uma suposição implícita que, mais cedo ou mais tarde, quebra em produção: a suposição de que o processo sempre roda no mesmo lugar, de que o disco local persiste, de que a config está no código, de que o ambiente de dev é igual ao de produção.
Um checklist mental para o dia a dia#
Quando você for revisar um serviço — seu ou de um time vizinho — vale passar por estas perguntas, que condensam os doze fatores em decisões operacionais:
- Consigo subir esse serviço em uma máquina limpa só com a imagem e um punhado de
variáveis de ambiente?
- Se eu matar qualquer instância agora, perco algum dado ou alguma requisição?
- Consigo dobrar o número de réplicas sem mudar uma linha de código?
- O rollback é rodar a release anterior, ou envolve reverter arquivos na mão?
- Os segredos estão fora do repositório?
- O ambiente de dev usa o mesmo tipo de banco e cache que produção?
Se a resposta a qualquer uma dessas for desconfortável, você encontrou dívida de arquitetura que vai cobrar juros no próximo incidente. O 12-Factor não é uma regra burocrática: é a destilação de dores reais de operação em princípios que, seguidos, tornam a plataforma que roda por baixo — seja Kubernetes, seja um PaaS — capaz de fazer o trabalho pesado por você. A aplicação bem-comportada é a que deixa a infraestrutura fazer sua mágica sem atrito.