É possível instalar todas as ferramentas descritas nos artigos anteriores — Kubernetes, ArgoCD, Prometheus, Falco, Backstage — e ainda assim ter uma organização que entrega software lentamente, sofre incidentes frequentes e não aprende com seus erros. As ferramentas são necessárias mas não suficientes. O que determina se uma organização DevOps é de alto desempenho ou medíocre é, em última análise, cultural: como as pessoas colaboram, como respondem a falhas, como tomam decisões e como aprendem.
O relatório State of DevOps, produzido anualmente pelo DORA — DevOps Research and Assessment — é o estudo longitudinal mais abrangente sobre performance em entrega de software. Ao longo de mais de uma década de pesquisa com dezenas de milhares de organizações, o DORA identificou que os maiores preditores de performance organizacional em entrega de software não são as ferramentas usadas, mas práticas culturais: psicologia de segurança para relatar problemas sem medo de punição, trabalho em pequenos lotes, colaboração entre times e foco em aprendizado contínuo.
Este artigo explora como medir a maturidade de uma organização DevOps, como conduzir postmortems que geram aprendizado real em vez de relatórios que são arquivados, e como construir os rituais de melhoria contínua que sustentam a evolução ao longo do tempo.
Medindo Maturidade DevOps com as Métricas DORA
As quatro métricas DORA — introduzidas no Artigo 19 em seu contexto de pipeline — são, em sua forma completa, métricas de resultado organizacional que medem a capacidade de entrega de valor com qualidade:
Deployment Frequency — com que frequência a organização faz deploy em produção. Times de elite fazem múltiplos deploys por dia. Times de baixo desempenho fazem deploys mensais ou menos frequentes.
Lead Time for Changes — tempo desde o commit até o código estar em produção. Times de elite medem em minutos a horas. Times de baixo desempenho medem em semanas a meses.
Change Failure Rate — percentual de deploys que resultam em degradação de serviço ou incidente. Times de elite mantêm abaixo de 5%. Times de baixo desempenho chegam a 46-60%.
Time to Restore Service — tempo para restaurar o serviço após um incidente. Times de elite restauram em menos de uma hora. Times de baixo desempenho levam de uma semana a um mês.
Coletando as Métricas DORA Automaticamente
// scripts/coletar-metricas-dora.js
// Coleta as quatro métricas DORA via GitHub API e dados de incidente
// Executa diariamente via pipeline e publica no Prometheus Pushgateway
const { Octokit } = require('@octokit/rest');
const { Pushgateway, Gauge } = require('prom-client');
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
const pushgateway = new Pushgateway(process.env.PROMETHEUS_PUSHGATEWAY);
const ORG = process.env.GITHUB_ORG;
const REPO = process.env.GITHUB_REPO;
const JANELA_DIAS = 30;
// ── Deployment Frequency ─────────────────────────────────────────────
async function calcularDeployFrequency() {
const fim = new Date();
const inicio = new Date(fim - JANELA_DIAS * 24 * 60 * 60 * 1000);
// Busca todos os deployments no período
const { data: deployments } = await octokit.repos.listDeployments({
owner: ORG,
repo: REPO,
environment: 'production',
per_page: 100,
});
const deploymentsNoPeriodo = deployments.filter(d => {
const criado = new Date(d.created_at);
return criado >= inicio && criado <= fim;
});
// Conta apenas deployments bem-sucedidos
const deploymentsSuccessful = await Promise.all(
deploymentsNoPeriodo.map(async d => {
const { data: statuses } = await octokit.repos.listDeploymentStatuses({
owner: ORG,
repo: REPO,
deployment_id: d.id,
per_page: 1,
});
return statuses[0]?.state === 'success';
})
);
const totalSuccessful = deploymentsSuccessful.filter(Boolean).length;
const deploysPerDia = totalSuccessful / JANELA_DIAS;
return {
total: totalSuccessful,
porDia: deploysPerDia,
classificacao: classificarDeployFrequency(deploysPerDia),
};
}
function classificarDeployFrequency(deploysPerDia) {
if (deploysPerDia >= 1) return 'elite'; // >= 1 deploy/dia
if (deploysPerDia >= 0.14) return 'alto'; // >= 1/semana
if (deploysPerDia >= 0.03) return 'medio'; // >= 1/mês
return 'baixo';
}
// ── Lead Time for Changes ─────────────────────────────────────────────
async function calcularLeadTime() {
const fim = new Date();
const inicio = new Date(fim - JANELA_DIAS * 24 * 60 * 60 * 1000);
// Busca PRs mergeados no período
const { data: pulls } = await octokit.pulls.list({
owner: ORG,
repo: REPO,
state: 'closed',
base: 'main',
per_page: 100,
sort: 'updated',
direction: 'desc',
});
const prsMergeados = pulls.filter(pr => {
if (!pr.merged_at) return false;
const mergedAt = new Date(pr.merged_at);
return mergedAt >= inicio && mergedAt <= fim;
});
if (prsMergeados.length === 0) {
return { mediaMinutos: null, classificacao: 'sem-dados' };
}
// Calcula o tempo desde o primeiro commit até o merge
const leadTimes = await Promise.all(
prsMergeados.map(async pr => {
const { data: commits } = await octokit.pulls.listCommits({
owner: ORG,
repo: REPO,
pull_number: pr.number,
per_page: 1,
});
// Usa a data do primeiro commit como início do lead time
const primeiroCommit = commits[commits.length - 1];
if (!primeiroCommit) return null;
const inicio = new Date(primeiroCommit.commit.author.date);
const fim = new Date(pr.merged_at);
return (fim - inicio) / (1000 * 60); // Converte para minutos
})
);
const leadTimesValidos = leadTimes.filter(Boolean);
const media = leadTimesValidos.reduce((a, b) => a + b, 0) / leadTimesValidos.length;
return {
mediaMinutos: Math.round(media),
mediaHoras: Math.round(media / 60),
classificacao: classificarLeadTime(media),
};
}
function classificarLeadTime(minutos) {
const horas = minutos / 60;
if (horas <= 24) return 'elite'; // < 1 dia
if (horas <= 168) return 'alto'; // < 1 semana
if (horas <= 720) return 'medio'; // < 1 mês
return 'baixo';
}
// ── Change Failure Rate ───────────────────────────────────────────────
async function calcularChangeFailureRate() {
const fim = new Date();
const inicio = new Date(fim - JANELA_DIAS * 24 * 60 * 60 * 1000);
// Busca deployments totais
const { data: deployments } = await octokit.repos.listDeployments({
owner: ORG,
repo: REPO,
environment: 'production',
per_page: 100,
});
const deploymentsNoPeriodo = deployments.filter(d =>
new Date(d.created_at) >= inicio
);
// Busca incidentes do período via PagerDuty API
const incidentesResponse = await fetch(
`https://api.pagerduty.com/incidents?` +
`since=${inicio.toISOString()}&until=${fim.toISOString()}&` +
`service_ids[]=${process.env.PAGERDUTY_SERVICE_ID}`,
{
headers: {
Authorization: `Token token=${process.env.PAGERDUTY_TOKEN}`,
Accept: 'application/vnd.pagerduty+json;version=2',
},
}
);
const { incidents } = await incidentesResponse.json();
// Incidentes causados por deploy (tem tag "deploy-related")
const incidentesDeployRelated = incidents.filter(i =>
i.tags?.some(t => t.label === 'deploy-related')
);
const cfr = deploymentsNoPeriodo.length > 0
? (incidentesDeployRelated.length / deploymentsNoPeriodo.length) * 100
: 0;
return {
totalDeploys: deploymentsNoPeriodo.length,
totalIncidentes: incidentesDeployRelated.length,
percentual: Math.round(cfr * 10) / 10,
classificacao: classificarCFR(cfr),
};
}
function classificarCFR(percentual) {
if (percentual <= 5) return 'elite';
if (percentual <= 15) return 'alto';
if (percentual <= 45) return 'medio';
return 'baixo';
}
// ── Publica métricas no Prometheus ───────────────────────────────────
async function publicarMetricas() {
const [deployFreq, leadTime, cfr] = await Promise.all([
calcularDeployFrequency(),
calcularLeadTime(),
calcularChangeFailureRate(),
]);
const metricaDeployFreq = new Gauge({
name: 'dora_deployment_frequency_daily',
help: 'Número médio de deploys por dia nos últimos 30 dias',
labelNames: ['repositorio', 'classificacao'],
});
const metricaLeadTime = new Gauge({
name: 'dora_lead_time_minutes',
help: 'Lead time médio em minutos nos últimos 30 dias',
labelNames: ['repositorio', 'classificacao'],
});
const metricaCFR = new Gauge({
name: 'dora_change_failure_rate_percent',
help: 'Taxa de falha de mudanças em percentual nos últimos 30 dias',
labelNames: ['repositorio', 'classificacao'],
});
metricaDeployFreq.set(
{ repositorio: REPO, classificacao: deployFreq.classificacao },
deployFreq.porDia
);
if (leadTime.mediaMinutos) {
metricaLeadTime.set(
{ repositorio: REPO, classificacao: leadTime.classificacao },
leadTime.mediaMinutos
);
}
metricaCFR.set(
{ repositorio: REPO, classificacao: cfr.classificacao },
cfr.percentual
);
await pushgateway.pushAdd({
jobName: 'dora-metrics',
groupings: { repositorio: REPO },
});
console.log(JSON.stringify({
level: 'info',
msg: 'Métricas DORA publicadas',
deployFrequency: deployFreq,
leadTime,
changeFailureRate: cfr,
}, null, 2));
}
publicarMetricas().catch(console.error);
Postmortems Sem Culpa: Aprendendo com Incidentes
O postmortem — também chamado de incident review ou after-action review — é o processo estruturado de analisar um incidente após sua resolução para extrair aprendizados e definir ações que evitem recorrência. A qualidade cultural de uma organização se revela mais claramente em como ela conduz seus postmortems do que em quase qualquer outra prática.
A Cultura de Não-Culpa
O princípio da culpa zero — blameless postmortem — parte de uma premissa fundamentada em décadas de pesquisa em segurança industrial: em sistemas complexos, incidentes são quase sempre o resultado de múltiplos fatores que se combinaram de maneira imprevista, não da incompetência ou negligência de um indivíduo.
Quando uma organização trata incidentes como oportunidades para encontrar e punir culpados, produz três efeitos destrutivos: as pessoas deixam de reportar problemas por medo de punição; os postmortems se tornam exercícios defensivos em vez de investigativos; e as causas sistêmicas dos incidentes permanecem intocadas enquanto o indivíduo "culpado" é removido ou punido.
A cultura de não-culpa não significa ausência de responsabilidade — significa que a responsabilidade é coletiva e sistêmica. O engenheiro que fez o deploy que causou o incidente não é culpado; o sistema que permitiu aquele deploy sem os controles adequados é o problema a ser corrigido.
Estrutura de um Postmortem Eficaz
# Postmortem: Indisponibilidade do Serviço de Checkout
**Data do incidente:** 2025-03-10
**Duração:** 47 minutos (14h23 — 15h10 UTC)
**Severidade:** SEV-1
**Facilitador:** Ana Costa
**Participantes:** Pedro Silva, Luiza Mendes, Carlos Santos, Maria Lima
---
## Sumário Executivo
Indisponibilidade completa do serviço de checkout durante 47 minutos,
afetando aproximadamente 2.300 usuários. Causa raiz: migration de banco
de dados que adicionou índice em tabela de pedidos (23M linhas) sem
usar criação concorrente, bloqueando todas as escritas durante a
construção do índice. Estimativa de impacto: R$ 87.000 em pedidos não
concluídos no período.
---
## Linha do Tempo
| Hora (UTC) | Evento |
|------------|------------------------------------------------------------------------|
| 14:18 | Deploy v2.4.1 iniciado via pipeline de CI/CD |
| 14:23 | Migration executada — início da construção do índice em produção |
| 14:24 | Primeiros alertas de timeout no Grafana (latência p99 > 5s) |
| 14:26 | PagerDuty aciona plantão — Pedro Silva |
| 14:31 | Pedro identifica a migration como causa provável via Performance Insights |
| 14:35 | Decisão de rollback discutida — migration não é facilmente reversível |
| 14:40 | Opção escolhida: cancelar a construção do índice via pg_cancel_backend |
| 14:52 | Índice cancelado — serviço começa a se recuperar |
| 15:10 | Todos os pods saudáveis, métricas normalizadas |
| 15:15 | Incidente declarado resolvido |
| 15:45 | Comunicação enviada para clientes afetados |
---
## Causa Raiz
A migration `20250310_add_index_pedidos_usuario_id.sql` criou um índice
usando o comando padrão `CREATE INDEX`, que adquire um `AccessShareLock`
na tabela durante toda a construção. Para tabelas de 23M linhas, a
construção levou aproximadamente 29 minutos — bloqueando todas as
operações de escrita durante esse período.
O comando correto para tabelas em produção é
`CREATE INDEX CONCURRENTLY`, que constrói o índice em segundo plano
sem bloquear escritas, mas requer atenção especial (não pode ser
executado dentro de uma transaction block).
---
## Fatores Contribuintes
**Fatores técnicos:**
- Não havia validação automatizada de migrations contra tabelas grandes
antes do deploy
- O ambiente de staging tem apenas ~50K linhas na tabela de pedidos,
tornando o impacto invisível em testes
- Não havia alerta configurado para `lock_waits` prolongados no RDS
**Fatores de processo:**
- A revisão do PR não incluiu avaliação do impacto da migration em
produção (revisores focaram no código da aplicação)
- Não havia um checklist de pre-deploy para migrations em tabelas grandes
- O runbook de rollback de migrations não estava atualizado
**Fatores de conhecimento:**
- A diferença entre CREATE INDEX e CREATE INDEX CONCURRENTLY
não estava documentada nas diretrizes de migrations
- Novos membros do time não recebem treinamento específico sobre
operações seguras em banco de dados de produção
---
## O Que Funcionou Bem
- O alerta de latência p99 disparou 2 minutos após o início do incidente —
tempo de detecção excelente
- Pedro identificou a causa raiz em menos de 10 minutos usando
Performance Insights e CloudWatch Logs
- A comunicação interna via Slack foi clara e frequente durante o incidente
- O time tomou a decisão correta de cancelar o índice em vez de esperar,
evitando uma hora adicional de indisponibilidade
---
## Ações Corretivas
| Ação | Responsável | Prazo | Issue |
|------|-------------|-------|-------|
| Adicionar lint de migrations que detecta CREATE INDEX sem CONCURRENTLY em tabelas > 1M linhas | Pedro Silva | 2025-03-17 | #1847 |
| Atualizar o ambiente de staging para ter volume de dados proporcional ao de produção (sample de 10%) | Luiza Mendes | 2025-03-24 | #1848 |
| Criar checklist de pre-deploy para migrations com verificação de tamanho de tabela | Ana Costa | 2025-03-14 | #1849 |
| Adicionar alerta de lock_wait_timeout prolongado no CloudWatch | Carlos Santos | 2025-03-17 | #1850 |
| Documentar migrations seguras em tabelas grandes no wiki de engenharia | Maria Lima | 2025-03-21 | #1851 |
| Incluir módulo de banco de dados em produção no onboarding de novos engenheiros | Ana Costa | 2025-03-28 | #1852 |
---
## Métricas do Incidente
- **MTTD** (Mean Time to Detect): 3 minutos
- **MTTI** (Mean Time to Identify): 8 minutos
- **MTTR** (Mean Time to Restore): 47 minutos
- **Usuários afetados:** ~2.300
- **Impacto estimado:** R$ 87.000
---
## Aprendizados para o Time
Esta não é a primeira vez que uma migration de banco de dados causa
incidente — em agosto de 2024 tivemos um incidente similar por um
ALTER TABLE sem DEFAULT em tabela grande. O padrão indica uma lacuna
sistêmica em nosso processo de validação de migrations, não um
problema de competência individual.
A ação de maior impacto não é o lint (que é preventivo), mas a
paridade de dados entre staging e produção — sem ela, nenhuma
quantidade de revisão manual detectará problemas de escala.
Facilitando um Postmortem Eficaz
O facilitador do postmortem tem a responsabilidade de criar um ambiente onde as pessoas sintam segurança para compartilhar o que realmente aconteceu:
## Guia do Facilitador de Postmortem
### Antes da reunião
- [ ] Leia todos os logs e alertas do período do incidente
- [ ] Construa um rascunho da linha do tempo para validar com participantes
- [ ] Identifique as perguntas que ainda precisam de resposta
- [ ] Confirme que todos os participantes relevantes estão convidados
- [ ] Reserve 60–90 minutos (incidentes complexos podem precisar de mais)
### Durante a reunião
**Abertura (5 min)**
Reforce explicitamente que o objetivo é aprender, não atribuir culpa.
Exemplo de fala: "Estamos aqui para entender o que aconteceu e o que
podemos melhorar como sistema e como time. Não há respostas erradas,
e ninguém será julgado pelas decisões que tomou com as informações
que tinha no momento."
**Revisão da linha do tempo (15–20 min)**
Percorra a linha do tempo cronologicamente. Para cada evento, pergunte:
- O que você estava vendo nesse momento?
- Qual informação você tinha disponível?
- O que você considerou antes de tomar essa decisão?
**Análise de causa raiz (20–30 min)**
Use os "5 Porquês" iterativamente até chegar a causas sistêmicas.
Evite parar no primeiro "erro humano" — pergunte: "O que no sistema
tornou esse erro possível?"
Exemplo:
- Por que o serviço ficou indisponível? → Migration bloqueou escritas
- Por que a migration bloqueou escritas? → CREATE INDEX sem CONCURRENTLY
- Por que foi usado CREATE INDEX sem CONCURRENTLY? → Desenvolvedor
não conhecia a diferença
- Por que o desenvolvedor não conhecia? → Não há documentação nem
treinamento específico sobre isso
- Por que não há documentação? → Nunca foi priorizado formalmente
→ Causa sistêmica: lacuna de conhecimento não documentada
**Definição de ações (15–20 min)**
Para cada causa sistêmica, defina uma ação com responsável e prazo.
Prefira ações preventivas (detecção automática) sobre ações paliativas
(treinamento manual). Não defina mais de 5–7 ações — priorize as de
maior impacto.
**Encerramento (5 min)**
Agradeça a participação de todos. Reforce que o postmortem será
publicado internamente para que toda a organização possa aprender.
### Após a reunião
- [ ] Publique o postmortem no wiki de engenharia em até 48 horas
- [ ] Crie os issues/tickets para todas as ações definidas
- [ ] Agende revisão de 30 dias para verificar o progresso das ações
- [ ] Atualize o dashboard de incidentes com os dados do postmortem
Rituais de Melhoria Contínua
Os postmortems tratam de incidentes específicos. A melhoria contínua requer rituais que operem em cadências diferentes — diária, semanal, mensal, trimestral:
Retrospectiva Semanal do Time
## Formato de Retrospectiva (60 minutos, sexta-feira)
### Check-in (5 min)
Cada pessoa compartilha uma palavra sobre como está chegando à retro.
### Revisão da semana (10 min)
- Deploys realizados: X
- Incidentes: X (severidade Y)
- Métricas DORA da semana
### O que foi bem (15 min)
Cada pessoa nomeia uma coisa que funcionou bem.
Time vota nas 3 mais importantes para celebrar/manter.
### O que pode melhorar (15 min)
Cada pessoa nomeia uma coisa que foi difícil ou ineficiente.
Time vota nas 3 mais importantes para abordar.
### Ações (10 min)
Para cada item da lista "melhorar", define uma ação com responsável.
Máximo de 3 ações por semana — foco é melhor que quantidade.
### Revisão das ações anteriores (5 min)
Verifica o progresso das ações definidas na retro anterior.
Quarterly Business Review de Engenharia
Uma vez por trimestre, o time de engenharia revisa suas métricas de desempenho em um nível mais alto:
## Engineering QBR — Q1 2025
### Métricas DORA — Evolução Trimestral
| Métrica | Q4 2024 | Q1 2025 | Δ | Meta Q2 |
|-----------------------|----------|----------|---------|-----------|
| Deploy Frequency | 2.1/dia | 3.8/dia | +81% | 5.0/dia |
| Lead Time | 4.2h | 2.1h | -50% | < 1h |
| Change Failure Rate | 8.3% | 4.1% | -51% | < 3% |
| MTTR | 42min | 18min | -57% | < 15min |
### Análise de Incidentes SEV-1 e SEV-2
Total de incidentes: 12 (vs 18 em Q4 2024 — redução de 33%)
Causas raiz recorrentes:
- Migrations de banco de dados: 4 incidentes (33%)
- Dependências externas (pagamento, email): 3 incidentes (25%)
- Configuração incorreta de feature flags: 2 incidentes (17%)
- Outros: 3 incidentes (25%)
### Toil Report
Trabalho repetitivo identificado e seu status:
- Rotação manual de credenciais: ELIMINADO (External Secrets Operator)
- Provisioning manual de ambientes: REDUZIDO 70% (Backstage templates)
- Debugging de builds falhando: EM PROGRESSO (melhores mensagens de erro)
### Investimentos para Q2
Baseado na análise acima, os três maiores investimentos para Q2:
1. Lint automático de migrations (4 incidentes prevenidos/trimestre estimado)
2. Chaos Engineering game days mensais (medir resiliência proativamente)
3. Melhorar paridade staging/produção (reduzir surpresas em deploy)
Psicologia de Segurança: A Base de Tudo
O modelo de Westrum — desenvolvido pelo sociólogo Ron Westrum para estudar culturas organizacionais em ambientes de alta complexidade como aviação e medicina — classifica organizações em três tipos:
Patológica — informação é ocultada por medo. Mensageiros são punidos. Falhas são escondidas. Novidades são disruptivas e ameaçadoras.
Burocrática — informação é compartimentada. Responsabilidades são rigidamente definidas. Processos são seguidos mesmo quando não fazem sentido.
Generativa — informação flui livremente. Responsabilidades são compartilhadas. Falhas são oportunidades de aprendizado. A missão importa mais do que o território.
O DORA demonstrou empiricamente que organizações com cultura generativa têm desempenho significativamente superior em todas as métricas de entrega de software. A cultura generativa não é um fim em si mesmo — é um pré-requisito para que todas as práticas técnicas descritos nesta série funcionem.
Um sinal concreto e mensurável de psicologia de segurança é a disposição dos engenheiros de reportar problemas antes que se tornem incidentes. Organizações que punem os portadores de más notícias rapidamente aprendem a não receber más notícias — até que se tornem incidentes de grandes proporções.
Encerrando o Módulo 9
Com este artigo encerra-se o Módulo 9 — Segurança e Compliance em DevOps — e com ele o conteúdo central do curso. Foram cobertos DevSecOps com integração de segurança em todo o pipeline, compliance e auditoria com geração automatizada de evidências, otimização de performance e custos com FinOps, resiliência com Chaos Engineering, Platform Engineering com Backstage e, neste artigo, a dimensão cultural que torna sustentáveis todas as práticas técnicas.
Os próximos artigos formam o projeto capstone da série — a integração de todos os conceitos em um sistema completo de produção.
Referências para Aprofundamento
Pesquisa e métricas - DORA State of DevOps Report — dora.dev — Relatório anual do DORA com os dados mais recentes sobre performance em entrega de software, incluindo análise das quatro métricas e dos preditores culturais de alto desempenho. - DORA Quick Check — dora.dev — Ferramenta online para avaliar rapidamente o desempenho da organização nas quatro métricas DORA e identificar áreas de melhoria.
Cultura e postmortems - Blameless Postmortems — etsy.com — Artigo original da Etsy que popularizou o conceito de postmortem sem culpa, com a fundamentação filosófica e prática do modelo. - The DevOps Handbook — itrevolution.com — Livro de referência de Gene Kim e colaboradores cobrindo os três caminhos do DevOps — fluxo, feedback e aprendizado contínuo — com casos de estudo de transformações reais. - An Elegant Puzzle — Will Larson — lethain.com — Livro sobre gestão de engenharia com capítulos específicos sobre como construir times de alta performance, gestão de incidentes e crescimento organizacional.