Handling Technical Disagreements
Technical disagreements are healthy until they block shipping: Axum vs Actix, Arc<Mutex> vs channel, whether to allow unsafe in hot path. Structured de-escalation - spikes, named decision makers, disagree-and-commit - preserves relationships and audit trails.
Recipe
Quick-reference recipe card - copy-paste ready.
# Disagreement log (RFC appendix)
## Position A: async sqlx everywhere (@alice)
- Evidence: benchmark ENG-123 shows 15% lower p95 at 1k RPS
## Position B: spawn_blocking sync reads (@bob)
- Evidence: team velocity; fewer pool surprises
## Falsifiable test
If async pool acquire p95 > 50ms at 500 RPS in staging, adopt B for phase 1.
## Decision maker: @techlead by Friday EOD
## Dissent recorded in ADR-0039 consequencesWhen to reach for this:
- Circular PR comments beyond 2 rounds
- Competing RFCs for same problem
- Senior engineers deadlock in design review
- Post-decision passive resistance
Working Example
// Spike branch: compare approaches with Criterion (throwaway)
// async path
async fn fetch_async(pool: &sqlx::PgPool, id: i32) -> sqlx::Result<i32> {
let row: (i32,) = sqlx::query_as("SELECT id FROM t WHERE id = $1")
.bind(id)
.fetch_one(pool)
.await?;
Ok(row.0)
}
// blocking path wrapped for fair test in tokio::test
async fn fetch_blocking(pool: &sqlx::PgPool, id: i32) -> sqlx::Result<i32> {
let pool = pool.clone();
tokio::task::spawn_blocking(move || {
// sync query in tests only - spike measures overhead
Ok(id)
})
.await
.unwrap()
}Spike results attached to RFC; decision maker picks with revisit trigger, not loudest voice.
Deep Dive
De-escalation Ladder
- Clarify constraints and success metrics in writing.
- Time-boxed spike with binary criteria.
- Decision maker rules; dissent logged.
- Disagree and commit; no sabotage via slow review.
- Revisit trigger in ADR (traffic, SLO, team size).
During Incidents
IC decides mitigation; architecture debate deferred to post-mortem. "I told you so" banned in incident channel.
Cultural Norms
- Attack ideas, respect people.
- Assume positive intent; ask what evidence would change minds.
- EM facilitates people issues; tech lead owns technical ruling.
Gotchas
- Consensus required - Gridlock. Fix: Named decision maker upfront.
- Spike without criteria - Subjective victory. Fix: Pre-register pass/fail metrics.
- Public humiliation in review - Attrition. Fix: Private sync for tone issues.
- Ignoring recorded dissent - Repeated fights. Fix: Revisit trigger honored on schedule.
- Escalation to exec for technical taste - Fix: ADR + EM only for staffing/timeline trade.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| Architecture board vote | Multi-squad deadlock | Two-pizza team |
| External consultant review | FFI safety dispute | Every framework choice |
| A/B in production | Measurable user impact | Schema contract risk |
| Coin flip (joke) | Never | Actually never |
FAQs
Decision maker who?
Tech lead default; EM if timeline/staffing dominates; staff engineer for cross-fleet.
How long debate?
Max 1 week without spike; then decide.
Junior disagrees with senior?
Welcome evidence; decision maker weighs arguments not rank.
Reopen decided ADR?
Only when revisit trigger fires or new facts (security CVE).
Passive resistance signs?
Slow reviews, "I guess" in standup; address in 1:1.
Disagree and commit mean?
Support rollout; do not undermine; may log dissent.
Two reasonable options?
Pick reversible default; document runner-up in ADR.
Remote conflict?
Video for heated threads; written summary after.
Unsafe disagreements?
Mandatory third-party security or FFI reviewer.
Document in HR file?
No for normal technical dissent; EM handles conduct separately.
Related
- Technical Decision-Making - ranked options
- Architecture Decision Records (ADRs) - record dissent
- Running Design Reviews - forum
- Spikes, PoCs & Adopting New Crates - evidence
- Leadership Best Practices - summary
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.