Tomada de Decisão Técnica
Líderes técnicos escolhem entre opções imperfeitas sob incerteza. Decisões ranqueadas tornam os trade-offs explícitos, documentam escolhas erradas e definem gatilhos de revisão para que as escolhas da stack Rust (Axum vs Actix, sync vs async DB) não se tornem religião permanente.
Como Usar Esta Lista
- Percorra as decisões em ordem ao planejar arquitetura ou upgrades.
- Registre o rank escolhido e por quê em um ADR ou ticket.
- Defina um gatilho de revisão (escala, tamanho da equipe, falha de SLO) para chamadas reversíveis.
- Use spikes para reduzir o risco de opções "2ª" antes de se comprometer com a "Melhor".
- Revise quando o gatilho disparar - substitua o ADR, não desvie silenciosamente.
Decisão 1: Framework HTTP para uma nova API Rust
Cenário: API B2B Greenfield, 6 engenheiros, SLO p95 200ms, Postgres, precisa de OpenAPI.
| Rank | Escolha | Abordagem |
|---|---|---|
| Melhor | Axum 0.8 | Tower middleware, tracing, extratores serde, alinhamento de ecossistema |
| 2ª | Actix-web | Maduro, rápido; modelo de middleware diferente |
| 3ª | Hyper puro | Controle máximo; mais boilerplate |
Escolha errada: Hyper apenas para 40+ endpoints sem framework interno compartilhado - autenticação e validação inconsistentes.
Por que a melhor é a melhor: O SLO da equipe e o ecossistema Tower favorecem Axum; o crate de autenticação interno já tem como alvo Axum.
Decisão 2: Tokio Async vs servidor com threads
Cenário: API CRUD, principalmente I/O Postgres, CPU ocasional na geração de PDF.
| Rank | Escolha | Abordagem |
|---|---|---|
| Melhor | Tokio + spawn_blocking para CPU | I/O Assíncrono; isola CPU em pool de bloqueio |
| 2ª | Servidor std Multi-thread (raro) | Apenas se não houver dependências async e escala pequena |
| 3ª | Handlers Axum bloqueantes em todos os lugares | Bloqueia threads de worker em I/O |
Escolha errada: std::fs e cliente HTTP síncrono dentro de handlers assíncronos - runtime trava.
Por que a melhor é a melhor: A carga de trabalho é limitada por I/O; o padrão da equipe é Tokio 1.x com padrões conhecidos.
Decisão 3: Monolito vs serviços divididos
Cenário: 8 engenheiros, um produto, Postgres compartilhado, deploy semanal.
| Rank | Escolha | Abordagem |
|---|---|---|
| Melhor | Monolito modular (crates de workspace) | Limites claros de crate; deploy único; extrair depois |
| 2ª | Dois binários (API + worker) | Quando a escala da fila e o domínio de falha diferem |
| 3ª | Microserviços por domínio | Imposto operacional prematuro |
Escolha errada: Cinco microserviços antes que a segunda equipe exista - CFR e lead time sofrem.
Por que a melhor é a melhor: O tamanho da equipe e os limites de transação se encaixam em um workspace implantável.
Decisão 4: Pool async sqlx vs sync
Cenário: Serviço Axum considerando pool async sqlx.
| Rank | Escolha | Abordagem |
|---|---|---|
| Melhor | sqlx Async com Tokio | Encaixe natural para Axum; métricas de pool via tracing |
| 2ª | Pool Sync + spawn_blocking | Se a equipe evitar padrões de DB async |
| 3ª | Apenas tokio-postgres puro | Perde verificações de consulta em tempo de compilação |
Escolha errada: Pool async com chamadas estilo ORM bloqueantes em handlers - exaustão do pool mascarada como latência.
Por que a melhor é a melhor: A squad usa o modo offline sqlx 0.8 em CI; o caminho async é documentado.
Decisão 5: Estratégia de tipo de erro
Cenário: Binário de aplicação mais crates de biblioteca interna compartilhada.
| Rank | Escolha | Abordagem |
|---|---|---|
| Melhor | thiserror em libs, anyhow na borda do binário | Erros públicos tipados; contexto no main |
| 2ª | thiserror em todos os lugares | Verboso em ferramentas CLI com muitos códigos de saída |
| 3ª | anyhow em todos os lugares | Protótipo rápido; contratos de biblioteca fracos |
Escolha errada: unwrap na API pública da biblioteca - panics se tornam incidentes do consumidor.
Por que a melhor é a melhor: Corresponde às diretrizes da API Rust; mapeia-se perfeitamente para IntoResponse do Axum.
Decisão 6: Serialização para API pública
Cenário: API REST JSON com campos em evolução.
| Rank | Escolha | Abordagem |
|---|---|---|
| Melhor | serde com #[serde(deny_unknown_fields)] explícito em APIs de admin | Parsing seguro; DTOs versionados |
| 2ª | Protobuf / gRPC (tonic) | Serviços internos de alta taxa de transferência |
| 3ª | serde_json::Value manual | Perde documentação de schema |
Escolha errada: serde_json::Value não marcado para todos os corpos - validação espalhada, bugs em produção.
Por que a melhor é a melhor: Geração de OpenAPI e SDKs de cliente se alinham com modelos serde.
FAQs
Quanto tempo devem durar as reuniões de decisão?
45 minutos no máximo; o resultado é aceitar, revisar RFC ou spike com tempo limitado.
Quem desempata?
Tomador de decisão nomeado (tech lead ou EM) após as opções serem escritas; registre a dissidência.
Revisar decisões quando?
Quando o gatilho de revisão disparar: 10x tráfego, equipe +50%, falha repetida de SLO.
Duração do spike?
2-3 dias com critérios de sucesso binários e branch descartável.
Documentar decisões reversíveis?
Um ADR leve ou comentário de ticket ainda vale a pena - evita re-debate mensal.
Polars vs DataFusion?
Polars 0.46+ para batch analítico/em memória; decisão separada para SQL de streaming.
CLI clap derive vs builder?
Derive para CLIs estáveis; builder para subcomandos dinâmicos - ADR se API de plugin pública.
Quando envolver segurança?
Autenticação, criptografia, unsafe, FFI e spikes de dependência tocando a fronteira da rede.
Decisão sem consenso?
Discordar e comprometer; dissidência nas consequências do ADR com gatilho de revisão.
Localização do template?
Pasta docs/adr/ ou RFC vinculada do README da equipe.
Relacionados
- Registros de Decisão de Arquitetura (ADRs) - registre resultados
- Executando Revisões de Design - grandes decisões
- Lidando com Desacordos Técnicos - processo de conflito
- Spikes, PoCs e Adoção de Novos Crates - reduza o risco de opções
- Melhores Práticas de Liderança - lista resumida
Versões da Stack: Esta página foi escrita para Rust 1.97.0 (edição 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4 e Polars 0.46+.