Skip to content

Writing Effective Technical RFCs and Design Documents

Write RFCs and design documents that drive decisions by structuring proposals around trade-offs, alternatives and measurable success criteria.

5 min read
Technical design document template with sections for problem statement, proposed solution, alternatives considered, and trade-offs

Most design documents fail before anyone reads them. They're either too long—40-page specifications that nobody has time for—or too vague—a paragraph of hand-waving that doesn't actually propose anything concrete. The worst ones describe a solution without explaining the problem, leaving reviewers to guess whether the approach makes sense for the constraints they can only infer.

An effective RFC does one thing: it helps a group of engineers make a well-informed decision. Everything in the document should serve that goal. If a section doesn't help someone decide whether to approve, modify, or reject the proposal, it doesn't belong.

The Minimum Viable RFC Structure

Every RFC needs exactly five sections. Everything else is optional. If you can't fill these five sections, you don't understand the problem well enough to propose a solution.

markdownmarkdown
# RFC: [Short descriptive title]
 
## Status
- **Author**: [Name]
- **Reviewers**: [Names — people whose input you need]
- **Status**: Draft | In Review | Approved | Rejected | Superseded
- **Date**: [Created date]
- **Decision deadline**: [When this must be decided by]
 
## Problem Statement
What problem are we solving? Who has this problem? 
Why does it matter now?
 
[2-4 paragraphs, no solution details here]
 
## Proposed Solution
How do we solve it? Be specific enough that someone 
could implement this without talking to you.
 
[Technical details, diagrams, interface definitions]
 
## Alternatives Considered
What other approaches did you evaluate? Why were they 
rejected? This is the most important section — it shows 
you've actually explored the design space.
 
[At least 2 alternatives with honest trade-off analysis]
 
## Trade-offs and Risks
What are we giving up? What could go wrong? What 
assumptions might be wrong?
 
[Honest assessment of downsides]

Writing the Problem Statement

The problem statement is where most RFCs succeed or fail. A clear problem statement makes the rest of the document almost write itself. A vague one leads to endless debate about the solution because readers are actually debating the problem.

markdownmarkdown
## ❌ Bad problem statement
 
"We need to improve our authentication system. The current
system is old and doesn't scale. We should use OAuth 2.0."
 
Problems:
- Mixes problem and solution ("We should use OAuth 2.0")
- "Old and doesn't scale" — what specifically doesn't scale?
- No data or evidence
- No impact statement
 
## ✅ Good problem statement
 
"Our session-based auth system creates two problems as we
scale to multiple regions:
 
1. **Session stickiness breaks multi-region failover.** 
   Sessions are stored in-memory on the origin server. When 
   we fail over to another region, all users are logged out. 
   We've had 3 incidents in Q3 where failover caused a spike 
   of 40,000 simultaneous re-authentication requests that 
   overwhelmed the auth database.
 
2. **Third-party integrations require per-integration auth 
   code.** Each of our 12 integration partners requires 
   custom authentication middleware. Adding a new partner 
   takes 2-3 weeks of engineering time, primarily spent on 
   auth plumbing rather than business logic.
 
Our SLA requires 99.9% availability across regions, and our 
roadmap includes 8 new integration partners in the next two 
quarters. Both goals are blocked by the current auth 
architecture."

Alternatives Considered: The Most Undervalued Section

Reviewers trust proposals more when they can see you've explored the space. Alternatives aren't filler—they're evidence of engineering judgment.

markdownmarkdown
## Alternatives Considered
 
### Alternative A: Migrate sessions to Redis cluster
Store sessions in a Redis cluster replicated across regions.
 
**Pros:**
- Minimal application code changes
- Team already operates Redis
- Session data remains mutable
 
**Cons:**
- Cross-region Redis replication adds 50-100ms latency
- Redis cluster adds operational complexity 
  (split-brain risks)
- Doesn't solve the third-party integration problem
 
**Why rejected:** Solves problem #1 partially (failover works 
but with added latency) and doesn't address problem #2 at all.
 
### Alternative B: JWT tokens with no session store
Stateless JWTs eliminate the session store entirely.
 
**Pros:**
- No session storage to manage
- Naturally works across regions
- Verification is a local operation
 
**Cons:**
- Revocation is complex (requires blocklist)
- Token size increases with claims
- Cannot invalidate tokens before expiry without 
  additional infrastructure
 
**Why rejected:** Revocation complexity is a dealbreaker for 
our security requirements (we need instant session 
termination for compromised accounts).
 
### Proposed: OAuth 2.0 with short-lived access tokens 
and refresh token rotation
[Details in Proposed Solution section]
 
**Why this over the alternatives:** Addresses both problems,
revocation works via refresh token invalidation, and standard
protocol means integration partners use off-the-shelf 
libraries.

Diagrams That Actually Help

A well-placed diagram replaces paragraphs of text. But a poorly drawn diagram confuses more than it clarifies.

markdownmarkdown
## Diagram best practices
 
### Include diagrams for:
- System architecture (what talks to what)
- Request flow (step-by-step sequence)
- Data model relationships
- State machines
 
### Skip diagrams for:
- Obvious relationships that text explains clearly
- Decorative purposes
- Things that will change before implementation
 
### Format:
- Use Mermaid, PlantUML, or Excalidraw
  (version-controllable, editable by reviewers)
- Label every arrow (what data flows, what protocol)
- Include a legend if using symbols
- Keep to one concept per diagram
tstypescript
// Anti-pattern: implementation details that don't 
// help the decision
 
// ❌ Don't include in the RFC:
// - Exact variable names and function signatures
// - Database column types
// - CSS class names
// - Test file structures
 
// ✅ Do include:
// - API contracts (what the interface looks like)
// - Data flow between systems
// - Key algorithms or approaches (pseudocode is fine)
// - Performance characteristics (Big O, expected latency)
 
// Example: API contract in an RFC
interface AuthAPI {
  // Token endpoint — called by clients to authenticate
  'POST /auth/token': {
    request: { grant_type: string; code?: string; refresh_token?: string };
    response: { access_token: string; refresh_token: string; expires_in: number };
  };
 
  // Revocation — called to invalidate a session
  'POST /auth/revoke': {
    request: { token: string };
    response: { revoked: boolean };
  };
 
  // Introspection — called by services to validate tokens
  'POST /auth/introspect': {
    request: { token: string };
    response: { active: boolean; sub: string; exp: number; scope: string };
  };
}

Success Criteria and Rollback Plans

An RFC without success criteria can never be evaluated after implementation. Define what "done" and "working" look like upfront.

markdownmarkdown
## Success Criteria
 
### Functional:
- [ ] Multi-region failover completes without logging 
      out users
- [ ] New integration partner can authenticate in <3 days 
      of engineering work (down from 2-3 weeks)
- [ ] Session revocation takes effect within 5 minutes
 
### Performance:
- [ ] Auth endpoint P99 latency < 200ms (current: 150ms)
- [ ] Token validation < 5ms per request (local operation)
- [ ] No increase in auth-related error rate
 
### Operational:
- [ ] Auth system operates independently in each region
- [ ] Monitoring and alerting configured before rollout
- [ ] Runbook for common failure scenarios documented
 
## Rollback Plan
If the migration causes issues:
1. **Immediate (< 1 hour):** Feature flag to route traffic 
   back to session-based auth
2. **Short-term (< 1 week):** Dual-write to both session 
   store and token system during migration
3. **Reversal condition:** Rollback if auth error rate 
   exceeds 0.5% or P99 latency exceeds 500ms for 
   more than 10 minutes

Running the Review Process

The document is half the battle. The review process determines whether decisions actually get made.

markdownmarkdown
## RFC review process
 
### Before sharing:
- Get informal feedback from 1-2 people
  (catch obvious issues before the formal review)
- Make sure the problem statement is solid
  (reviewers will nit-pick solutions; they'll reject 
  unclear problems)
 
### Review meeting structure (60 min max):
1. Author presents problem statement only (10 min)
2. Group validates: "Is this the right problem?" (10 min)
3. Author presents proposed solution (15 min)
4. Group discusses trade-offs and alternatives (20 min)
5. Decision: approve / request changes / reject (5 min)
 
### Common review anti-patterns:
- "Let me redesign the whole thing" → Time-box feedback
- "What about edge case X?" → Great, file it as a follow-up
- No decision after review → Set a decision deadline
- "I need more time" → OK, but the deadline doesn't move
 
### Decision modes:
- **Consensus**: everyone agrees (ideal but slow)
- **Consent**: nobody objects (faster, usually good enough)
- **Authority**: designated decision-maker decides 
  (fastest, use for time-sensitive decisions)

Key Takeaways

Every RFC needs exactly five sections—problem statement, proposed solution, alternatives considered, trade-offs, and success criteria—and if you can't fill all five, you don't understand the problem well enough to propose a solution. The problem statement is the most critical section: quantify the impact with data (incidents, engineering hours lost, SLA violations), separate the problem from the solution completely, and explain why it matters now—a clear problem statement makes the solution almost obvious while a vague one guarantees endless debate. The "alternatives considered" section builds reviewer trust by demonstrating you've explored the design space—include at least two genuine alternatives with honest analysis of their pros and cons, explaining specifically why each was rejected. Set explicit success criteria with measurable targets before implementation begins, include a concrete rollback plan with trigger conditions, and run reviews as time-boxed decisions with a deadline—an RFC that never reaches a decision is worse than no RFC at all.

Wilfredo Rujel

Wilfredo Rujel

Full Stack Software Engineer

Share this postX