Eu sempre achei fascinante como alguns sistemas críticos funcionam “quietos”, sem chamar atenção, mas salvam vidas quando tudo dá errado. Segundo o Sapo.pt, dois aviões quase colidiram no Atlântico — e quem evitou a tragédia não foi sorte nem milagre humano, foi um sistema automatizado de prevenção de colisões chamado TCAS. O paralelo para quem programa é direto: confiabilidade não é “ter testes”; é projetar alertas, decisões e tempos de resposta numa arquitetura que funciona mesmo sob incerteza.
TCAS: o “sistema silencioso” que resolve conflito em segundos
O TCAS (Traffic Collision Avoidance System) é um sistema embarcado, instalado na maioria dos aviões comerciais, que monitora continuamente o espaço aéreo ao redor. A parte que muita gente ignora: ele não depende de radar em terra nem de “a torre avisar”. Ele usa trocas automáticas entre aeronaves para inferir risco e, a partir disso, manda comandos de evasão.
Na prática, o TCAS faz três coisas em sequência, rápido o bastante para mudar o desfecho:
- Detecta aeronaves próximas usando mensagens de transponder (relevante para o “não depender do controle em terra”).
- Calcula ameaça e trajetória provável com base em movimento relativo e regras de separação.
- Recomenda ação e prioriza resolução do conflito: um avião recebe instrução para subir e o outro para descer.
Essa lógica “cada um decide e age” reduz o tempo entre perceber e agir. Em computação distribuída, isso é o equivalente a evitar que você espere uma comunicação externa lenta para tomar decisão local urgente.
Por que isso é tão importante em termos de engenharia?
Quando o Sapo.pt descreve um cenário em que os aviões estavam na mesma aerovia, mesma altitude e em sentidos opostos, o mais interessante é que o sistema de controle ainda estava monitorando. Mesmo assim, o TCAS foi o “guardião” nos segundos críticos. Isso indica um princípio clássico de sistemas reais:
- monitoramento existe, mas latência e responsabilidade importam;
- autoridade e atuação precisam estar onde está o tempo.
Se você já trabalhou com produção, sabe: não adianta o “observability perfeito” se você não tem caminho rápido para “remediar” sob pressão.
TCAS vs alternativas: por que não basta “ver e avisar”
Vamos comparar com abordagens que parecem boas na teoria, mas falham em cenários de alta criticidade.
1) Só alerta para humano
Alertar é mais fácil do que automatizar. Mas o TCAS sugere ação porque humano + tempo é imprevisível. Em incidentes, milissegundos viram metros. Em software, isso vira “despacho por fila para ser tratado depois”. Funciona… até o dia em que o tempo importa e você perde a janela.
2) Confiar no controle/coordenação externa
O Sapo.pt destaca que o TCAS não depende do radar em terra nem das instruções dos controladores. Isso evita pontos únicos de falha e gargalos de comunicação. Em redes, isso equivale a não depender de um único serviço externo para decisões locais urgentes (ex.: “depende do webhook responder antes de reduzir risco”).
3) Reagir com lógica genérica
Um sistema genérico de “colisão detectada, reduce velocidade” pode ser tarde demais e pode ainda piorar. O TCAS é específico: determina instruções contraditórias controladas (subir vs descer) para gerar separação.
Em engenharia de sistemas, “genérico” costuma significar “sem garantia”. O TCAS é protocolizado: toma decisão com base em regras e estimativas.
O “porquê” por trás da arquitetura: decisão local, resposta determinística e separação
Se eu tiver que resumir como dev sênior: o TCAS é um sistema que maximiza a chance de separar trajetórias antes que o problema se torne irrecuperável.
- Decisão local: cada aeronave roda sua lógica de risco.
- Tempo é recurso: a janela de resolução é curta, então a computação precisa ser embarcada e previsível.
- Determinismo no impacto: instruções (subir/descer) são claramente definidas para produzir separação.
Agora traduza isso para sua vida como programador. Em sistemas críticos (pagamentos, controle industrial, trading, air traffic no sentido amplo), você não pode tratar o “como decidir” como um detalhe. Decisão tem que ter contrato, teste e fallback.
Na Prática: como aplicar o mesmo padrão em sistemas web (exemplo passo a passo)
Vamos trazer isso para desenvolvimento web usando um paralelo útil: detecção de conflito e resolução automática com ação local. Vou usar um exemplo realista: dois processos podem tentar atualizar o mesmo recurso com risco de inconsistência. Você pode criar um “TCAS” do seu domínio para evitar colisão de estado.
Objetivo
Quando duas rotinas estiverem prestes a “se cruzar” (mesmo recurso, mesmo intervalo, ações incompatíveis), o sistema deve detectar o risco e escolher uma estratégia de separação (ex.: uma agenda a execução e a outra replaneja).
- Defina a unidade de conflito: qual chave representa o “mesmo espaço aéreo” (ex.: resourceId).
- Adote uma “medição” local: cada instância deve conseguir estimar risco a partir de eventos recebidos (ex.: timestamps, versão, lock lógico).
- Crie uma decisão local com regra determinística: por exemplo, “se eu detectar conflito e sou o menor id, eu subo a versão; senão, eu replanejo”.
- Implemente resolução com efeito claro: a ação precisa mudar estado de forma que “separe trajetórias” (ex.: versionamento/optimistic lock + fallback).
- Teste sob concorrência: use testes de corrida e simule latência para garantir que a janela não estoura.
Exemplo de código (conflito detecta, resolve com estratégia determinística)
Aqui vai um exemplo simples em Node/TypeScript usando “optimistic concurrency” como mecanismo de separação. A ideia: se duas requisições tentam atualizar a mesma entidade ao mesmo tempo, uma falha com conflito de versão e aplica uma estratégia (replanejar e tentar de novo). Isso é o equivalente prático a “evitar colisão por ajuste de trajetória”.
type Entity = {
id: string;
version: number;
status: "PENDING" | "PROCESSING" | "DONE";
};
async function updateWithConflictResolution(
repo: {
get: (id: string) => Promise<Entity>;
save: (entity: Entity) => Promise<void>;
},
id: string,
actorId: string
) {
const maxRetries = 3;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
const entity = await repo.get(id);
// "Detecção": risco existe se alguém já está PROCESSING
if (entity.status === "PROCESSING") {
// "Resolução": regra determinística baseada em actorId (simula subir/ descer)
// Menor id assume a liderança e tenta avançar; outro replaneja.
const shouldAdvance = actorId < entity.id;
if (shouldAdvance) {
// separa trajetórias: muda estado de forma controlada
const next: Entity = { ...entity, version: entity.version + 1, status: "DONE" };
try {
await repo.save(next); // pode falhar se versão mudou no meio
return { resolved: true, action: "advance" };
} catch (e) {
// se falhar por versão, a próxima iteração recalcula risco
}
} else {
// replaneja: em vez de competir, desloca a execução
return { resolved: true, action: "replan" };
}
}
// Sem conflito: tenta avançar normalmente
const next: Entity = { ...entity, version: entity.version + 1, status: "PROCESSING" };
try {
await repo.save(next);
return { resolved: true, action: "normal" };
} catch (e) {
// versão mudou: recalcula e tenta de novo (janela curta, tentativa limitada)
}
}
throw new Error("Não foi possível resolver conflito após retries");
}
Repare no padrão: cada chamada recalcula risco e aplica uma ação de separação com regra clara. Isso não elimina concorrência — mas evita “colisão silenciosa” que vira bug difícil de reproduzir.
Erros Comuns: o que evitar (e eu já vi isso quebrar produção)
O TCAS mostra como fazer certo. O que mais vejo em projetos é exatamente o oposto. Aqui vai a lista.
1) Esperar feedback externo para decidir
Se sua lógica depende de uma resposta externa “para então agir”, você perde a janela. Em web, isso vira: “só fecho transação quando serviço X confirmar”. Quando X está lento, seu sistema vira um avião sem TCAS.
2) Tratar tudo como fallback genérico
“Se falhar, retenta” é perigoso se não houver separação de trajetórias. Retentativa sem estratégia pode piorar: vira storm. O TCAS separa subindo/descendo. No software, separação costuma ser versão, lock lógico, fila com deduplicação, ou uma regra determinística de prioridade.
3) Não modelar latência como requisito
O tempo de resposta do TCAS é parte do sistema. No seu produto, “latência” não é só performance. É requisito de segurança/consistência. Se você ignora isso no planejamento, você só descobre no incidente.
4) Falta de determinismo na decisão
Se a decisão depende de ordem aleatória (ex.: race entre workers), você terá “bugs fantasmas”. TCAS evita isso com regras. No código, prefira decisões baseadas em dados (versão, IDs, timestamps normalizados) para reduzir variabilidade.
5) Observabilidade sem remediação
Você pode ter logs perfeitos e mesmo assim não resolve. Observability é para diagnosticar; remediação precisa estar no caminho da ação. O Sapo.pt deixa implícito esse ponto: mesmo com acompanhamento no controle, o TCAS assumiu a ação. Não espere que “monitoring” substitua automação.
Implicações práticas para devs: como pensar “TCAS-first”
Quando eu aplico esse modelo mental em sistemas, eu marco checklist antes de implementar qualquer automação:
- Onde está a autoridade para agir quando há risco?
- Quanto tempo tenho até a situação virar irreversível?
- Qual é a ação de separação (não só o alerta)?
- A decisão é local e determinística o suficiente para funcionar sob concorrência?
- Qual é o fallback quando a janela aperta?
Essa forma de pensar costuma melhorar qualidade não só em incidentes — ela reduz complexidade futura, porque limita comportamentos emergentes.
FAQ
O TCAS depende do radar em terra ou de controladores?
Segundo o Sapo.pt, não. O TCAS é um sistema embarcado que monitora e toma decisão usando comunicações entre aeronaves (transponder) e lógica própria para evitar colisão.
Por que um avião recebe ordem de subir e o outro de descer?
Para gerar separação rápida e controlada. Uma ação coordenada (um sobe, o outro desce) reduz drasticamente a probabilidade de interseção de trajetórias.
Qual é o paralelo mais direto para quem programa?
Modelar “conflito” no domínio e implementar uma ação automática de resolução com decisão local e regra determinística, em vez de só alertar ou esperar serviços externos.
Como testar “conflitos” de forma parecida com cenários críticos?
Faça testes de concorrência com latência e reorder de eventos (ex.: múltiplos workers, delays artificiais, falhas intermitentes). O objetivo é garantir que a decisão se mantém consistente na janela curta.
Retentar automaticamente resolve conflitos sempre?
Não. Retentativa pura sem estratégia pode causar mais competição. Você precisa de “separação de trajetórias” (versões, locks lógicos, prioridade determinística, ou replanejamento).
Gostou? Me segue no GitHub e deixa um comentário se tiver dúvida ou quiser aprofundar algum ponto.