Skip to content

Writing Effective Technical RFCs

How to write RFC documents that drive clear technical decisions: structure, audience calibration, alternatives analysis and asynchronous consensus.

5 min read
An RFC document template with sections for context, proposal, alternatives, and decision record

An RFC (Request for Comments) is a structured document that proposes a technical decision and invites feedback before implementation begins. It forces the author to think through a problem rigorously, surfaces concerns from stakeholders early, and creates a permanent record of why decisions were made.

The cost of writing an RFC is hours. The cost of not writing one — building the wrong thing, discovering integration issues mid-sprint, or making decisions that only one person understood — is weeks or months.

When to Write an RFC

Not every change needs an RFC. Adding a utility function does not. But anything that affects multiple teams, introduces new infrastructure, changes a core abstraction, or is difficult to reverse should have one.

tstypescript
// Decision framework: does this change need an RFC?
interface ChangeAssessment {
  affectsMultipleTeams: boolean;       // Cross-team coordination needed
  hardToReverse: boolean;              // Database schema, API contract, etc.
  newInfrastructure: boolean;          // New service, new database, new queue
  changesCorAbstraction: boolean;      // Auth system, data model, caching layer
  significantCostOrRisk: boolean;      // Large effort, performance implications
  controversialApproach: boolean;      // Multiple valid approaches, strong opinions
}
 
function needsRFC(change: ChangeAssessment): boolean {
  // If any two of these are true, write an RFC
  const factors = Object.values(change).filter(Boolean);
  return factors.length >= 2;
}
 
// Examples
needsRFC({
  affectsMultipleTeams: false,
  hardToReverse: false,
  newInfrastructure: false,
  changesCorAbstraction: false,
  significantCostOrRisk: false,
  controversialApproach: false,
}); // false — just do it
 
needsRFC({
  affectsMultipleTeams: true,
  hardToReverse: true,
  newInfrastructure: true,
  changesCorAbstraction: false,
  significantCostOrRisk: true,
  controversialApproach: true,
}); // true — definitely write an RFC

RFC Structure

A well-structured RFC makes it easy for reviewers to understand the problem, evaluate the proposal, and provide focused feedback. Here is a template that works across most engineering organizations.

markdownmarkdown
# RFC-042: Migrate User Sessions from Redis to DynamoDB
 
**Author:** Jane Smith
**Status:** Under Review
**Created:** 2022-02-15
**Decision Deadline:** 2022-03-01
 
## Summary
One paragraph that explains the proposal at a high level.
A reader should understand what you want to do after reading this section.
 
## Motivation
Why are we doing this? What problem does it solve?
Include data: error rates, latency percentiles, cost numbers.
 
## Current State
How does the system work today?
Include a diagram if the architecture is complex.
 
## Proposal
The detailed technical plan. What changes, how it works,
and what the migration path looks like.
 
## Alternatives Considered
At least two alternatives with honest pros and cons.
This section is the most important for building trust.
 
## Risks and Mitigations
What could go wrong? How will we detect it? What's the rollback plan?
 
## Open Questions
Things you genuinely don't know yet. Invite specific feedback here.
 
## Decision
(Filled in after the review period)
What was decided, by whom, and why.

Writing the Motivation Section

The motivation section is where most RFCs fail. Authors jump straight to the solution without establishing why the current state is inadequate. Reviewers who do not share the author's context will not understand why the change matters.

tstypescript
// ❌ Weak motivation — vague and assertion-based
const weakMotivation = `
  Redis is not a good fit for user sessions.
  We should use DynamoDB instead because it's more scalable.
`;
// Reviewer thinks: "Redis works fine for sessions. Why change?"
 
// ✅ Strong motivation — specific, data-driven, problem-focused
const strongMotivation = `
  Our Redis session store is hitting scaling limits:
  
  - P99 latency has increased from 5ms to 45ms over the last quarter
    as session count grew from 500K to 2.1M (chart: link)
  - We've had 3 incidents in the last month where Redis OOM killed
    caused session loss for ~12K users each time
  - The single-node Redis setup has no replication; failover requires
    manual intervention and ~8 minutes of downtime
  - Monthly cost: $1,200/mo for an r6g.2xlarge instance that's at 87%
    memory utilization with no headroom for growth
  
  We expect session count to reach 5M by Q4 based on current growth
  trends (appendix A). The current architecture cannot support this.
`;
// Reviewer thinks: "Clear problem. Let me see the proposed solution."

The Alternatives Section

The alternatives section is the most important part of an RFC for building reviewer trust. When you show that you honestly considered multiple approaches — including doing nothing — reviewers know you have done your homework.

tstypescript
// Always include "Do Nothing" as an alternative
interface Alternative {
  name: string;
  description: string;
  pros: string[];
  cons: string[];
  estimatedEffort: string;
  whyNotChosen: string;
}
 
const alternatives: Alternative[] = [
  {
    name: 'Do Nothing',
    description: 'Keep the current Redis single-node setup',
    pros: [
      'Zero engineering effort',
      'No migration risk',
    ],
    cons: [
      'OOM incidents will increase as sessions grow',
      'P99 latency will continue to degrade',
      'Manual failover remains a risk',
    ],
    estimatedEffort: '0 weeks',
    whyNotChosen: 'Growth projections make this untenable within 2 quarters',
  },
  {
    name: 'Redis Cluster',
    description: 'Migrate to a Redis Cluster with 3 primary + 3 replica nodes',
    pros: [
      'Familiar technology — team knows Redis',
      'Horizontal scaling via hash slots',
      'Automatic failover with Sentinel',
    ],
    cons: [
      'Operational complexity increases significantly',
      'Cross-slot operations not supported (affects bulk session ops)',
      'Still requires manual capacity planning',
      'Estimated cost: $3,600/mo for 6-node cluster',
    ],
    estimatedEffort: '3 weeks',
    whyNotChosen: 'Higher operational burden than DynamoDB for similar cost',
  },
  {
    name: 'DynamoDB (Proposed)',
    description: 'Migrate sessions to DynamoDB with on-demand capacity',
    pros: [
      'Fully managed — no operational overhead',
      'Auto-scales to any traffic level',
      'Built-in TTL for session expiry',
      'Multi-AZ replication by default',
      'Pay-per-request pricing scales with actual usage',
    ],
    cons: [
      'Team needs to learn DynamoDB data modeling',
      'Migration requires dual-write period',
      'Slightly higher per-request latency (single-digit ms vs sub-ms)',
    ],
    estimatedEffort: '4 weeks',
    whyNotChosen: 'This is the proposed approach',
  },
];

Gathering Feedback Effectively

An RFC that sits unread for two weeks helps no one. Actively soliciting feedback from the right people at the right time makes the process work.

tstypescript
// ❌ Passive feedback request
const passiveFeedback = 'Please review this RFC and leave comments.';
// Result: no one reads it, deadline passes, author assumes consensus
 
// ✅ Targeted feedback request with specific questions
const activeFeedback = {
  to: [
    { name: 'Platform Team', ask: 'Is the DynamoDB capacity estimate realistic?' },
    { name: 'Security Team', ask: 'Any concerns with session data in DynamoDB?' },
    { name: 'Backend Lead', ask: 'Does the dual-write migration plan have gaps?' },
  ],
  openQuestions: [
    'Should we use on-demand or provisioned capacity for the first month?',
    'What is the acceptable data loss window during migration cutover?',
    'Do we need to preserve session history, or can we start fresh?',
  ],
  deadline: '2022-03-01',
  decisionMaker: 'Staff Engineer — Platform',
};

From RFC to Decision Record

After the review period, the RFC becomes a decision record. Document what was decided, the reasoning, and any modifications that came from the review process.

tstypescript
interface DecisionRecord {
  rfcId: string;
  decision: 'approved' | 'rejected' | 'deferred';
  decisionDate: string;
  decisionMaker: string;
  summary: string;
  modificationsFromReview: string[];
  dissent: string[];     // Disagreements recorded for future reference
  reviewParticipants: string[];
}
 
const decision: DecisionRecord = {
  rfcId: 'RFC-042',
  decision: 'approved',
  decisionDate: '2022-03-01',
  decisionMaker: 'Jane Smith (Staff Engineer)',
  summary: 'Approved migration to DynamoDB with on-demand capacity',
  modificationsFromReview: [
    'Added 2-week dual-write period (originally proposed 1 week)',
    'Added rollback trigger: if DynamoDB P99 > 20ms, revert to Redis',
    'Security review: encrypt session data at rest using KMS',
  ],
  dissent: [
    'Backend Lead preferred Redis Cluster for team familiarity — noted ' +
    'but overruled due to operational cost analysis',
  ],
  reviewParticipants: [
    'Platform Team (3 reviewers)',
    'Security Team (1 reviewer)',
    'Backend Lead',
  ],
};

Key Takeaways

  1. Write RFCs for changes that are hard to reverse, cross-team, or controversial — not everything needs one, but anything with significant blast radius does
  2. Lead with motivation — data-driven problem statements earn reviewer trust and justify the engineering investment
  3. Always include alternatives — honest evaluation of "Do Nothing" and at least one competing approach shows rigorous thinking
  4. Target your feedback requests — ask specific people specific questions instead of broadcasting "please review"
  5. Set a decision deadline — open-ended review periods lead to indefinite delays; time-box the discussion
  6. Record the decision and dissent — future engineers need to understand not just what was decided, but why, and what trade-offs were accepted
Wilfredo Rujel

Wilfredo Rujel

Full Stack Software Engineer

Share this postX