Alertar bem é mais difícil do que parece. A tentação é simples: adicionar um alerta para cada métrica que pode dar errado. O resultado é uma coleção de thresholds arbitrários que disparam às 3am, ninguém acorda com urgência real, e o time inteiro começa a ignorar o canal de alertas. Alert fatigue não é um problema de configuração — é um problema de filosofia. A abordagem correta começa com uma pergunta: o que o usuário está experimentando agora? SLO-based alerting responde a essa pergunta de forma direta, mensurável, e com muito menos ruído do que threshold alerting.
Threshold Alerting vs SLO-based Alerting
Threshold Alerting — O problema
A abordagem clássica: "alerte quando a métrica cruzar X". Error rate acima de 1%? Alerta. CPU acima de 80%? Alerta. Latência acima de 500ms? Alerta. Simples de configurar, mas com problemas sérios em produção:
- Thresholds arbitrários: 1% foi escolhido por quê? Às 3am com 100 req/s, 1% são 1 erro por segundo. No pico de natal com 100k req/s, 1% são 1000 erros por segundo — impactos completamente diferentes.
- Sem contexto de duração: um error rate de 5% por 1 minuto é diferente de 5% por 1 hora. Threshold não distingue — dispara para os dois da mesma forma.
- Alert fatigue: cada threshold ligeiramente errado gera alertas que são ignorados — o pior estado possível para on-call.
- Orientado a causa, não a impacto: CPU a 80% pode não impactar usuário nenhum. Error rate de 0.1% afetando o endpoint de pagamento pode ser catastrófico.
SLO-based Alerting — A abordagem correta
SLO (Service Level Objective) define o nível de confiabilidade prometido aos usuários. Um SLO típico: "99.9% das requests devem completar com sucesso em menos de 500ms, medido em janela rolling de 30 dias". O error budget é o que sobra: 0.1% × 30 dias × 24h × 60min = 43.2 minutos de downtime permitido no mês.
Alertar com base em SLOs significa alertar quando o consumo do error budget está em ritmo que vai esgotar o budget antes do fim do período — não quando uma métrica cruzou um threshold arbitrário. Isso vincula o alerta diretamente ao impacto no usuário.
SLI (Service Level Indicator): a métrica específica que você mede — proporção de requests com sucesso, latência P99, disponibilidade. SLO (Service Level Objective): o objetivo sobre o SLI que o time se compromete internamente: "error rate < 0.1% em 30 dias". SLA (Service Level Agreement): o contrato legal/comercial com consequências financeiras se violado — geralmente mais permissivo que o SLO interno (se SLO é 99.9%, SLA pode ser 99.5%), criando margem de segurança para reagir antes de quebrar o contrato com o cliente.
Burn Rate Alerts
Burn rate é a velocidade com que o error budget está sendo consumido, normalizada pela velocidade "normal" de consumo. Burn rate 1 = consumo exatamente na taxa esperada (o budget dura o período completo). Burn rate 14.4 = budget esgota em 30/14.4 ≈ 2 dias.
O Problema de uma Única Janela
Alertar com burn rate alto em janela curta (1h) captura incidentes rápidos e graves, mas dispara para spikes temporários de 10 minutos que se autorresolverem. Alertar em janela longa (6h) é mais estável mas reage tarde demais para incidentes rápidos que consomem muito budget em pouco tempo. A solução é exigir que o burn rate esteja alto em duas janelas simultâneas: uma longa (para confirmar que não é spike) e uma curta (para confirmar que ainda está ativo).
# SLO: 99.9% success rate, error budget: 0.1% por 30 dias
# Burn rate = taxa atual de consumo / taxa normal de consumo
# Alerta de alta urgência (page imediato):
# burn rate > 14.4x em DUAS janelas (1h longa + 5m curta)
- alert: ErrorBudgetBurnFast
expr: |
(
rate(http_requests_total{status=~"5.."}[1h])
/ rate(http_requests_total[1h]) > (14.4 * 0.001)
) and (
rate(http_requests_total{status=~"5.."}[5m])
/ rate(http_requests_total[5m]) > (14.4 * 0.001)
)
for: 2m
labels:
severity: page
team: platform
annotations:
summary: "High error budget burn rate"
description: >
Error budget consumindo a {{ $value | humanizePercentage }} de erro.
Burn rate 14.4x: budget esgota em menos de 2 dias.
# Alerta de baixa urgência (ticket/slack):
# burn rate > 6x em janela longa (6h + 30m)
- alert: ErrorBudgetBurnSlow
expr: |
(
rate(http_requests_total{status=~"5.."}[6h])
/ rate(http_requests_total[6h]) > (6 * 0.001)
) and (
rate(http_requests_total{status=~"5.."}[30m])
/ rate(http_requests_total[30m]) > (6 * 0.001)
)
for: 15m
labels:
severity: ticket
team: platform
annotations:
summary: "Elevated error budget burn rate"
description: "Burn rate 6x em janela de 6h. Budget em risco se mantiver."
Burn Rate Thresholds Práticos
| Severidade | Burn Rate | Janelas | Budget Esgota em | Ação |
|---|---|---|---|---|
| Page (crítico) | > 14.4× | 1h + 5m | ~2 dias | Despertar on-call imediatamente |
| Page (urgente) | > 6× | 6h + 30m | ~5 dias | Despertar on-call se fora do horário |
| Ticket (baixa) | > 3× | 24h + 6h | ~10 dias | Criar ticket, resolver no próximo dia útil |
| Info | > 1× | 72h | < 30 dias | Monitorar no dashboard semanal |
Alertmanager
O Alertmanager (parte do ecossistema Prometheus) recebe alertas disparados pelo Prometheus e é responsável por: deduplicação, agrupamento, silenciamento, inibição, e roteamento para receivers (PagerDuty, OpsGenie, Slack, email). A separação entre "avaliar alertas" (Prometheus) e "gerenciar notificações" (Alertmanager) permite tratar a lógica de notificação como configuração independente da lógica de alerting.
Conceitos Fundamentais
- Grouping: agrupa alertas relacionados em uma única notificação. Sem grouping, um outage que afeta 50 serviços gera 50 notificações — com grouping, uma única notificação com contexto consolidado.
- Inhibition: suprime alertas secundários quando um alerta primário está ativo. Se "Datacenter Down" está alertando, iniba "Service Unavailable" — o engenheiro já sabe a causa raiz.
- Silencing: silenciar alertas temporariamente durante manutenção planejada, com duração e motivo registrados.
- Routing: rotear alertas para times diferentes baseado em labels (team, service, environment).
global:
resolve_timeout: 5m
route:
group_by: ['alertname', 'cluster', 'service']
group_wait: 30s # aguarda antes de enviar para agrupar alertas chegando juntos
group_interval: 5m # intervalo mínimo entre notificações do mesmo grupo
repeat_interval: 4h # reenviar se ainda ativo após 4h
receiver: default-slack
routes:
# Page de alta urgência → PagerDuty
- matchers:
- severity = page
receiver: pagerduty-critical
continue: false
# Alertas de banco → time de dados
- matchers:
- team = data
receiver: slack-data-team
group_by: ['alertname', 'database']
# Staging → apenas Slack, nunca page
- matchers:
- environment = staging
receiver: slack-dev-channel
receivers:
- name: pagerduty-critical
pagerduty_configs:
- routing_key: ${PAGERDUTY_INTEGRATION_KEY}
severity: critical
- name: slack-data-team
slack_configs:
- api_url: ${SLACK_WEBHOOK_DATA}
channel: '#alerts-data'
send_resolved: true
- name: default-slack
slack_configs:
- api_url: ${SLACK_WEBHOOK_DEFAULT}
channel: '#alerts-platform'
send_resolved: true
# Inibição: se datacenter alert ativo, inibe alerts de serviços individuais do mesmo cluster
inhibit_rules:
- source_matchers:
- alertname = DatacenterDown
target_matchers:
- severity =~ "warning|page"
equal: ['cluster']
Alert Fatigue — O Maior Problema de On-call
Alert fatigue ocorre quando o volume ou frequência de alertas é alto o suficiente para que os engenheiros comecem a ignorá-los — consciente ou inconscientemente. O estado mais perigoso: alertas ativos que ninguém olha, porque o time aprendeu que "eles sempre disparam". A consequência lógica é um incidente real passando despercebido em meio ao ruído.
Sinais de Alert Fatigue
- Alertas resolvidos com "snooze" sem investigação
- Plantão de on-call resulta em mais de 2-3 pages por semana (Google SRE Book sugere < 2 por turno de 8h)
- Alertas "ruidosos" conhecidos que o time decidiu coletivamente ignorar
- Runbooks não atualizados — ninguém atualizou porque o alerta foi ignorado
- Engenheiros pedem transferência de time após turno de on-call
Como Combater
- Alerte no sintoma, não na causa: "usuários estão recebendo erros" é um alerta. "CPU em 80%" é uma métrica — pode ou não ser problema. Só page quando há impacto confirmado no usuário.
- Todo alerta deve ter runbook: se você não consegue escrever um runbook para um alerta, o alerta não deveria existir. O runbook define o que fazer — se é "verifique o dashboard", o alerta é desnecessário.
- Review semanal de alertas: quantos alertas dispararam? Quantos foram acionáveis? Quantos foram ruído? Alertas com < 50% de acionabilidade são candidatos a remoção ou ajuste.
- Postmortem de alertas falsos: trate um alerta falso como você trataria um bug em produção — causa raiz, fix, e prevenção de reincidência.
- Janela de manutenção com silencing explícito: não ignore alertas manualmente — crie silences com duração e motivo no Alertmanager. Mantém o histórico e evita esquecer de reativar.
Rob Ewaschuk (Google, 2013): um bom alerta responde "sim" a três perguntas: (1) Este alerta detecta algo que impacta usuários agora? (2) A pessoa que recebe precisa agir urgentemente? (3) A ação requerida não pode ser automatizada? Se a resposta a qualquer uma for "não", o alerta precisa ser reformulado ou eliminado.
Alertas vs Tickets vs Dashboards
| Tipo | Quando usar | Canal | Exemplo |
|---|---|---|---|
| Page (urgente) | Impacto ativo em usuários agora, requer ação imediata | PagerDuty/OpsGenie | Error rate > 1% por 5min |
| Alerta de atenção | Degradação detectada, pode aguardar horas úteis | Slack #alerts | P99 aumentando, ainda dentro do SLO |
| Ticket | Tendência preocupante, agir no próximo sprint | Jira/Linear | Burn rate 2× por 24h |
| Dashboard | Informação para revisão proativa | Grafana | Error budget restante, deployment freq |
On-call Sustentável
Rotação e Responsabilidade
On-call sustentável requer time grande o suficiente para rotação com intervalo mínimo de 1 semana por engenheiro. Google SRE Book recomenda: máximo de 25% do tempo de trabalho em atividades operacionais (on-call ativo + postmortems + toil). Com menos de 6 pessoas em rotação em uma semana de 7 dias, algum engenheiro está on-call por mais de uma semana seguida em algum momento — isso é insustentável em médio prazo.
Runbooks Acionáveis
Runbooks são o que torna on-call sustentável para quem não é expert em cada serviço. Um bom runbook contém: link para o dashboard relevante, causas prováveis em ordem de frequência, passos de diagnóstico com comandos concretos (não "verifique os logs" — kubectl logs -l app=order-service --tail=100 | grep ERROR), ações de mitigação com rollback se aplicável, e quando escalar e para quem.
Postmortem Blameless
Postmortem blameless é a prática de analisar incidentes sem culpar indivíduos — o foco é em falhas de sistema, processo e tooling. O postmortem documenta: linha do tempo, causa raiz técnica e de processo, impacto quantificado, o que foi feito de bem, o que falhou, e action items concretos com dono e prazo. Action items sem dono e prazo definidos nunca são executados.
# Postmortem: [Título do Incidente]
**Data**: 2026-05-09
**Duração**: 14:23 - 16:47 UTC (2h24min)
**Impacto**: ~12% dos usuários receberam erro 503 no checkout
**Severity**: SEV-2
## Linha do Tempo
- 14:23 — Alertmanager disparou ErrorBudgetBurnFast
- 14:25 — Engenheiro on-call reconheceu o alerta
- 14:31 — Identificado: deploy de 14:18 alterou pool de conexões DB
- 14:45 — Rollback iniciado
- 15:02 — Rollback completado, error rate normalizado
- 16:47 — Monitoramento confirmou estabilidade, incidente fechado
## Causa Raiz
Deploy do PR #1842 reduziu max_connections do pool de 50 para 5
(erro de configuração em values.yaml). Sob carga normal, o pool saturou
causando timeouts que se propagaram para o checkout.
## Por que não foi detectado antes?
- Teste de staging usa mock do DB, não detecta saturação de pool
- Code review não incluiu revisão de mudanças em values.yaml
## O que foi bem
- Alerta disparou dentro de 5min do início do impacto
- Rollback executado dentro de 14min do alerta
## Action Items
| Item | Dono | Prazo |
|------|------|-------|
| Adicionar test de pool de conexão em staging | @camila | 2026-05-16 |
| PR checklist: mudanças em values.yaml requerem review de infra | @lucas | 2026-05-12 |
| Aumentar coverage de smoke tests pós-deploy | @ana | 2026-05-30 |
Alerting na Prática — C#, Python e Go
# prometheus/rules/aspnetcore.yml
groups:
- name: aspnetcore_slo
interval: 1m
rules:
# SLI: success rate (excluindo 4xx esperados como not found)
- record: job:http_request_success:rate5m
expr: |
sum(rate(http_server_request_duration_seconds_count{
job="order-service",
http_response_status_code!~"4.."
}[5m])) by (job)
/
sum(rate(http_server_request_duration_seconds_count{
job="order-service"
}[5m])) by (job)
# Burn rate alto → page
- alert: OrderServiceHighErrorBurn
expr: |
(
1 - job:http_request_success:rate5m{job="order-service"}
) > (14.4 * 0.001)
for: 5m
labels:
severity: page
team: platform
service: order-service
annotations:
summary: "Order Service: high error budget burn"
runbook_url: "https://wiki.internal/runbooks/order-service-errors"
description: >
Error rate {{ $value | humanizePercentage }} excede burn
rate de 14.4x. Budget esgota em menos de 2 dias.
# Latência P99 acima do SLO
- alert: OrderServiceHighLatency
expr: |
histogram_quantile(0.99,
sum(rate(http_server_request_duration_seconds_bucket{
job="order-service",
http_route="/api/orders"
}[5m])) by (le)
) > 1.0
for: 10m
labels:
severity: warning
team: platform
annotations:
summary: "Order Service: P99 latency > 1s"
description: "P99 em {{ $value | humanizeDuration }}"
ASP.NET Core com prometheus-net gera http_server_request_duration_seconds automaticamente via UseHttpMetrics(). Labels incluem http_route com o template do path (não o path real), o que permite agrupar por endpoint sem cardinalidade explosiva.
# prometheus/rules/fastapi.yml
groups:
- name: fastapi_slo
interval: 1m
rules:
- record: job:http_requests_success:rate5m
expr: |
sum(rate(http_request_duration_seconds_count{
job="api-service",
status!~"5.."
}[5m])) by (job)
/
sum(rate(http_request_duration_seconds_count{
job="api-service"
}[5m])) by (job)
# Error budget restante na janela de 30 dias
# 1 - (taxa_de_erro_realizada / slo_error_rate)
- record: job:error_budget_remaining:ratio
expr: |
1 - (
(1 - avg_over_time(job:http_requests_success:rate5m[30d]))
/ 0.001 # 1 - 0.999 SLO target
)
# Alerta quando budget abaixo de 25%
- alert: ErrorBudgetLow
expr: job:error_budget_remaining:ratio < 0.25
for: 0m # urgente — budget já baixo
labels:
severity: warning
annotations:
summary: "Error budget below 25%"
description: >
Apenas {{ $value | humanizePercentage }} de budget restante.
Evite deployments de alto risco neste período.
- name: celery_workers
rules:
- alert: CeleryWorkerDown
expr: celery_workers_active == 0
for: 2m
labels:
severity: page
annotations:
summary: "Todos os workers Celery estão inativos"
runbook_url: "https://wiki.internal/runbooks/celery-down"
avg_over_time sobre a métrica gravada (recorded rule) é mais eficiente do que recalcular a janela de 30 dias a cada evaluation. Recorded rules são pré-computadas e armazenadas pelo Prometheus como séries temporais.
# prometheus/rules/go_services.yml
groups:
- name: go_service_slos
interval: 30s
rules:
# Recorded rule: success rate por serviço e endpoint
- record: service:http_success_rate:rate5m
expr: |
sum by (service, http_handler) (
rate(http_requests_total{
job=~".*-service",
status_code!~"5.."
}[5m])
)
/
sum by (service, http_handler) (
rate(http_requests_total{job=~".*-service"}[5m])
)
# Burn rate alert com dupla janela
- alert: ServiceHighErrorBurnRate
expr: |
(1 - service:http_success_rate:rate5m) > (14.4 * 0.001)
and
(
1 - sum by (service, http_handler)(
rate(http_requests_total{status_code!~"5.."}[5m])
) / sum by (service, http_handler)(
rate(http_requests_total[5m])
)
) > (14.4 * 0.001)
for: 2m
labels:
severity: page
# label 'team' vem do service discovery → routing no Alertmanager
annotations:
summary: >
{{ $labels.service }}/{{ $labels.http_handler }}: burn crítico
description: >
Error rate {{ $value | humanizePercentage }}.
# alertmanager.yml — routing por team label vindo dos targets
route:
routes:
- matchers:
- team = payments
receiver: pagerduty-payments
group_by: [alertname, service]
- matchers:
- team = platform
receiver: pagerduty-platform
Labels nos alvos do Prometheus (via service discovery ou static_configs) são propagados automaticamente para os alertas — permite routing no Alertmanager sem hardcodar a label no rules file. Cada serviço declara seu team no scrape config.
Decisões de engenharia
Quando threshold alerts causarem mais de 2-3 pages não acionáveis por semana, ou quando o time começar a ignorar alertas sistematicamente. SLO alerts requerem definir SLOs explicitamente — o processo de definição em si é valioso porque força alinhamento sobre o que "bom" significa para cada serviço. Se você não sabe o baseline atual do serviço, comece medindo por 2-4 semanas antes de definir o SLO. Nunca defina um SLO mais agressivo do que o baseline atual medido — você terá alertas constantes desde o primeiro dia.
Alertas só no Slack: aceitável para startups pequenas sem SLA crítico e equipes que monitoram o canal ativamente. O problema do Slack: mensagens se perdem no fluxo, não há escalação automática, e não há histórico estruturado de incidentes. PagerDuty/OpsGenie adicionam: escalation policies (se não respondido em Xmin, escala para o próximo), schedule management com rotação automática, e histórico de incidentes pesquisável. O custo ($) é justificado assim que um incidente perdido no Slack (ou o custo de acordar a pessoa errada às 3am) custa mais que a ferramenta.
Não há número fixo, mas um critério prático: se durante a criação do alerta você pensou "vou adicionar por precaução" — não adicione. Todo alerta deve ter uma história clara: "este alerta dispara quando X acontece, e quando isso acontece o engenheiro precisa fazer Y imediatamente". Comece com os 4 golden signals (latência, traffic, errors, saturation) e adicione alertas apenas quando a ausência deles teria causado impacto não detectado em um incidente real. Para um serviço CRUD típico, 2-4 alertas de page são suficientes.
Não comece pela meta aspiracional — comece medindo o que você tem hoje por 30 dias. Se o serviço está em 99.5% availability hoje, um SLO de 99.9% é irreal sem investimento significativo em confiabilidade. Um SLO ligeiramente abaixo do estado atual (99.3% se você está em 99.5%) é um bom ponto de partida — é alcançável hoje e dá espaço para melhorar sem alertas constantes. O SLO deve também refletir o que o usuário percebe: se 0.1% de erros afeta usuários VIP e 2% de erros afeta usuários gratuitos, eles precisam de SLOs diferentes.
Como praticar
-
Implemente SLO-based alerting completo para um serviço HTTP simples (pode ser uma app de exemplo): defina um SLI de success rate, calcule o error budget para um SLO de 99.9% em 30 dias, e configure dois alertas de burn rate no Prometheus — um de alta urgência (14.4× em 1h+5m) e um de baixa urgência (6× em 6h+30m). Dispare erros artificialmente em diferentes intensidades e verifique quais alertas disparam.
Critério: um spike de 10% de erros por 3 minutos não dispara o alerta de alta urgência; um erro rate de 5% sustentado por 10 minutos dispara o alerta de alta urgência; um erro rate de 2% por 1 hora dispara o alerta de baixa urgência; os dois alertas têm runbook_url preenchido. -
Configure Alertmanager com routing, grouping e inibição: crie três receivers (PagerDuty simulado via webhook local, Slack-dev para staging, Slack-prod para produção), configure grouping por alertname+service, adicione uma regra de inibição que suprime alertas de serviço quando um alerta de infraestrutura do mesmo cluster está ativo. Valide que um alerta de infraestrutura ativo inibe os alertas de serviço downstream.
Critério: um alerta de page vai para o receiver correto (PagerDuty); alertas em staging vão apenas para Slack-dev; um alerta de "DatabaseClusterDown" inibe os alertas dos serviços que dependem do DB; o histórico no Alertmanager UI mostra o silence/inhibition correto. -
Conduza uma triagem de alert fatigue: pegue um serviço existente (ou crie um cenário simulado com 10+ alertas) e aplique o "teste de três perguntas" de Rob Ewaschuk a cada alerta. Documente quais alertas passam, quais falham, e o motivo. Para os que falham, proponha uma alternativa (rebaixar para ticket, remover, transformar em dashboard).
Critério: todos os alertas foram avaliados; pelo menos 30% foram identificados como candidatos a remoção ou rebaixamento; cada decisão está documentada com justificativa; a "acionabilidade" estimada pós-triagem é > 80%. -
Escreva um runbook completo para um alerta existente: escolha um alerta de burn rate e escreva o runbook com link para dashboard relevante, causas prováveis em ordem de frequência, e pelo menos 3 passos de diagnóstico com comandos kubectl/PromQL/CLI concretos. Valide o runbook fazendo um colega executá-lo durante um incidente simulado sem sua ajuda.
Critério: o colega consegue diagnosticar a causa simulada seguindo apenas o runbook, sem perguntar; cada passo de diagnóstico tem um comando concreto (não "verifique os logs"); o runbook inclui critério de escalação (quando e para quem escalar); o tempo médio de diagnóstico é < 15 minutos. -
Escreva um postmortem blameless para um incidente real ou simulado: use o template com linha do tempo, causa raiz técnica e de processo, impacto quantificado, e pelo menos 3 action items com dono e prazo. Valide que nenhuma linguagem do postmortem culpa um indivíduo — use revisão por pares.
Critério: o postmortem não contém linguagem de culpa individual (termos como "o engenheiro X deveria ter" devem ser refraseados como "o processo de review não detectou"); causa raiz inclui tanto causa técnica quanto causa de processo; todos os action items têm dono nomeado e prazo em data absoluta; pelo menos um action item melhora o alerting ou o runbook do serviço afetado.
Perguntas de entrevista
O que é burn rate de error budget e por que é melhor que threshold alerting?
Error budget é o quanto o serviço pode falhar dentro do período do SLO. Para um SLO de 99.9% em 30 dias: 0.1% × 30d × 24h × 60min = 43.2 minutos de downtime permitido. Burn rate é a velocidade de consumo normalizada: burn rate 1 = consumo exatamente na taxa esperada (budget dura o período); burn rate 14.4 = consumo 14.4× mais rápido, budget esgota em ~2 dias.
Burn rate é superior ao threshold porque: (1) considera duração — 1% de erro por 5 minutos vs 1% por 4 horas têm impactos completamente diferentes no budget; (2) alinha com impacto ao usuário — você alerta quando o acúmulo de erros é significativo, não quando uma métrica cruzou um número arbitrário; (3) reduz falsos positivos — spikes curtos não disparam alertas de alta urgência (a janela curta + longa simultânea filtra spikes); (4) força clareza sobre SLOs — para definir burn rate você precisa definir um SLO, e esse processo força alinhamento sobre o que "confiabilidade suficiente" significa para cada serviço.
Como você estruturaria alerting para um time de 8 engenheiros com 5 microserviços?
Com 8 engenheiros e 5 serviços, o risco principal é alert fatigue — poucos engenheiros para absorver muito ruído. Estruturaria em camadas:
(1) SLOs por serviço: definiria um SLO para cada serviço baseado no baseline atual medido por 30 dias, não em metas ideais. (2) Alertas em duas camadas: page (PagerDuty, burn rate > 14.4× em 1h) e ticket (Slack + Jira, burn rate > 3× em 24h). (3) Grouping agressivo: usar grouping no Alertmanager para que um outage que afeta 3 endpoints gere 1 notificação consolidada, não 3. (4) Inibição de alertas causais: se o DB está down, inibir alertas de todos os serviços que dependem do DB. (5) Runbook obrigatório: nenhum alerta sem runbook — enforced via PR review. (6) Review semanal de 15min: quantos alertas dispararam, quantos foram acionáveis. Com 8 pessoas, rotação de on-call seria 1 pessoa por semana em turno, com handoff documentado no início do turno e postmortem para qualquer SEV-1 ou SEV-2.
Qual a diferença entre SLI, SLO e SLA?
SLI (Service Level Indicator): a métrica específica que você mede. Ex: "proporção de HTTP requests completadas com sucesso (2xx/3xx) no último minuto". O SLI é um número entre 0 e 1 (ou percentual). Múltiplos SLIs podem ser combinados em um SLO composto (ex: success rate AND latência P99 < 500ms).
SLO (Service Level Objective): o alvo de qualidade que o time se compromete internamente a atingir. Ex: "SLI de success rate deve ser ≥ 99.9%, medido em janela rolling de 30 dias". O SLO é interno — é o contrato do time com ele mesmo e com os stakeholders internos. A regra de ouro: o SLO deve ser alcançável hoje, e viola-se internamente um SLO antes de violar o SLA, dando tempo de reagir.
SLA (Service Level Agreement): o contrato legal/comercial com consequências (créditos, penalidades, rescisão) caso não seja cumprido. Geralmente mais permissivo que o SLO — se o SLO interno é 99.9%, o SLA pode ser 99.5%, criando uma margem de 0.4% para absorver variações antes de impactar o cliente comercialmente. O SLA é negociado com clientes e definido com base no que o sistema pode genuinamente entregar.
Como você identificaria e eliminaria alert fatigue no time?
Em três frentes: (1) Diagnóstico quantitativo: pull do histórico de alertas das últimas 4 semanas — quantos dispararam, quantos resultaram em ação imediata, quantos foram silenciados/ignorados. Alertas com acionabilidade < 50% são candidatos prioritários à eliminação ou rebaixamento. (2) Triage de alertas existentes: para cada alerta, aplicar o "teste de três perguntas" (impacto ativo no usuário? ação urgente necessária? ação não pode ser automatizada?). Se qualquer resposta for não, o alerta não deveria ser page. (3) Processo contínuo: instaurar revisão semanal de 15min de alertas (retro focada especificamente em alertas da semana). Criar uma métrica de saúde do alerting: "% de pages que requereram ação imediata" — meta > 80%. Abaixo disso, há ruído demais. Por fim, exigir que qualquer novo alerta seja adicionado via PR com runbook incluído — o atrito do processo previne adição irresponsável de alertas.
O que é um postmortem blameless e por que a cultura blameless é difícil de implementar?
Postmortem blameless: análise de incidente que foca em falhas de sistema, processo e tooling — não em erros individuais. O pressuposto é que engenheiros tomam as melhores decisões possíveis com a informação disponível no momento. Se um engenheiro causou um incidente ao executar um comando "óbvio", a pergunta certa não é "por que ele fez isso?" mas "por que o sistema permitiu que esse comando causasse esse impacto?"
Por que é difícil: (1) Pressão implícita por responsabilização — gestores e stakeholders frequentemente querem "quem causou" como resposta, e equipes aprendem a satisfazer essa expectativa mesmo em postmortems nominalmente blameless; (2) Auto-censura — engenheiros omitem detalhes embaraçosos por medo de consequências, o que compromete a causa raiz real; (3) Viés retrospectivo — após o incidente, as ações que levaram ao problema parecem óbvias, o que torna fácil culpar quem tomou essas decisões. A implementação requer: linguagem explicitamente revisada para remover culpa ("o engenheiro X deveria ter visto" → "o processo de review não sinalizou"), participação da gestão como guardiã da cultura, e postmortems de incidentes menores onde o risco de culpa é baixo para construir o hábito antes de incidentes graves.
Referências para aprofundar
- livro Site Reliability Engineering — Niall Richard Murphy, Betsy Beyer, Chris Jones, Jennifer Petoff (O'Reilly, 2016).
- livro The Site Reliability Workbook — Betsy Beyer et al. (O'Reilly, 2018).
- livro Implementing Service Level Objectives — Alex Hidalgo (O'Reilly, 2020).
- paper My Philosophy on Alerting — Rob Ewaschuk (Google, 2013).
- docs Alertmanager — Documentação oficial — Prometheus Project (2024).
- docs Prometheus Recording Rules — Prometheus Project (2024).
- artigo Error Budget Policy — Google Cloud Blog (2023).
- artigo Meaningful Availability — Tamara Munzner, Hauer et al. (USENIX NSDI, 2020).
- artigo Incident Response at Slack — Slack Engineering Blog (2022).
- artigo How to Avoid On-call Burnout — Increment Magazine (2021).
- docs PagerDuty Incident Response Guide — PagerDuty (2024).
- docs OpenSLO Specification — OpenSLO Project (2024).