How to Write a Technical RFC: Section-by-Section Guide
A section-by-section guide to technical RFCs: what each part has to establish, what reviewers look for, and where proposals stall in review.
An RFC for a critical system stalls for one of two reasons: reviewers cannot tell what problem it solves, or they cannot find the part that concerns them. Both are structural problems, and both are fixable before the first review comment lands.
Treat the RFC as a sales document. It sells one solution to four audiences with competing priorities: executives who fund the work, architects who vet the design, implementers who build it, and operators who carry the pager afterwards. Structure it so each audience finds its answer without reading the whole document, and put those answers in the order the audiences ask for them.
The Audience Sets the Order#
A short, plainly written notification system RFC often clears review faster than a technically deeper proposal from a more senior author. The reason is rarely technical merit. The shorter document answers the questions stakeholders ask, in the order they ask them.
Section by section, here is what each part of a notification system RFC has to establish and what reviewers look for when they skim it. The implementation series covers the system such an RFC describes.
Executive Summary#
The executive summary is your elevator pitch. You have about 30 seconds to convince a busy VP or senior engineer that this document is worth their time. Here’s what works:
We need to implement a robust, scalable user notification system that can handle
real-time updates, push notifications, email notifications, and in-app notifications
across our platform. This system will serve as the backbone for user engagement,
critical alerts, and feature announcements.
It states the what clearly (notification system), lists specific capabilities (real-time, push, email, in-app), connects to business value (user engagement, critical alerts), and avoids technical jargon.
Weak version:
This RFC proposes implementing a microservices-based event-driven architecture
utilizing Kafka, PostgreSQL, and WebSockets to facilitate asynchronous message
delivery across multiple channels with configurable retry mechanisms.
The weak version loses executives at “microservices-based” and never explains why anyone should care. If a system can’t be explained to a product manager in one paragraph, the design probably needs more clarity.
What reviewers actually look for:
- Scope clarity: Is this a complete rewrite or an enhancement?
- Business alignment: Does this solve a real problem or is it resume-driven development?
- Risk assessment: Are you being honest about complexity?
Problem Statement#
Numbers matter here: vague problems get vague timelines.
The notification RFC tied each pain point to something stakeholders already track:
### Current Pain Points
- Users miss important updates about their projects
- No centralized way to manage notification preferences
- Manual notification sending is error-prone and not scalable
### Business Impact
- Reduced user engagement and retention
- Increased support tickets due to missed communications
- Poor user experience leading to churn
Notice how each pain point maps to a business impact someone already tracks. Pain points written this way turn into the metrics you report against after launch, which is why the pairing is worth the extra paragraph.
Here’s what doesn’t work:
The current system is outdated and difficult to maintain. Engineers complain
about the codebase and adding new features is challenging.
This tells me nothing actionable. How outdated? What specific maintenance issues? Which features are blocked? Without specifics, this reads like every legacy system ever.
Strong RFCs include:
- Current metrics: “847 support tickets last month about missed notifications”
- Cost implications: “Engineers spend 15% of sprint time on manual notification tasks”
- Opportunity cost: “Three feature launches delayed due to notification limitations”
Proposed Solution: Balancing Vision and Specificity#
Engineers either get lost in implementation details or stay so high-level that nobody knows what’s actually being built.
The notification RFC found the perfect balance:
### System Architecture
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Notification │ │ Notification │ │ Notification │
│ Sources │───▶│ Engine │───▶│ Channels │
└─────────────────┘ └──────────────────┘ └─────────────────┘
### Core Components
- Event Processor: Handles incoming notification events
- Template Engine: Manages notification templates and personalization
- Rate Limiting: Prevents notification spam
It shows the big-picture architecture visually, breaks down into understandable components, and explains what each one does.
Watch out for:
- Solutions looking for problems (“We’ll use GraphQL subscriptions because they’re modern”)
- Technology bingo (“Kubernetes, Istio, Envoy, Linkerd…”)
- Premature optimization (“We’ll shard the database from day one”)
In practice, implementations often start simpler than the RFC suggests. The modular design allows adding complexity gradually, with rate limiting arriving in month three.
Technical Implementation#
Good technical specs are concrete enough to estimate but flexible enough to adapt.
The RFC included a concrete schema, written with operations in mind:
CREATE TABLE notification_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE,
notification_type VARCHAR(100) NOT NULL,
template_id UUID REFERENCES notification_templates(id),
data JSONB DEFAULT '{}',
status VARCHAR(20) DEFAULT 'pending',
sent_at TIMESTAMP,
delivered_at TIMESTAMP,
read_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW()
);
The audit trail is built in through the sent_at, delivered_at, and read_at timestamps, the JSONB data field absorbs requirements the RFC did not foresee, and the status column is essential for debugging production issues.
Production reality adds index requirements that often get missed (a compound index on user_id, status, and created_at), a partition strategy for time-series data (monthly partitions), and an archive strategy for moving old notifications to cold storage.
How these requirements surface in practice is covered in the production debugging post.
API design deserves the same treatment. A representative set of endpoints looks like this:
POST /api/notifications/send
GET /api/notifications/user/:userId
PUT /api/notifications/:id/read
But great RFCs also consider:
- Pagination strategies for list endpoints
- Batch operations for efficiency
- Versioning strategy for future changes
- Rate limiting at the API level
Cursor-based pagination becomes necessary after offset pagination creates performance issues at scale; something the RFC could have anticipated.
Implementation Phases#
### Phase 1: Core Infrastructure (Weeks 1-4)
- Database schema implementation
- Basic notification engine
- In-app notification system
### Phase 2: Advanced Features (Weeks 5-8)
- Push notifications
- Template management system
- Scheduling and rate limiting
The phasing holds up because it delivers value in phase 1 (users get notifications by the end of it), frontloads the hard problem (real-time delivery comes first), and leaves phase 2 loose enough to absorb what phase 1 teaches.
Phases slip in predictable places:
- Integration with authentication or billing runs past its estimate, because another team’s calendar is not in your plan
- Edge cases in rate limiting and retry logic surface only under load
- The final phase gets partially descoped once real usage patterns arrive
As long as the RFC said the estimate was an estimate, slipping like this isn’t a planning failure. The implementation series documents how to adapt a timeline while keeping stakeholder trust.
Common gaps include no buffer for discoveries (“Week 1: Implement everything”), no testing time allocated, dependencies on other teams that go unaccounted for, and “simple integration” with external services, which is never simple.
Technical Considerations#
The RFC got specific about scale:
### Performance Targets
- Notification delivery: < 100ms for in-app, < 5s for email
- System throughput: 10,000+ notifications per second
- Database query performance: < 50ms for preference lookups
These aren’t arbitrary numbers. They’re derived from:
- Current user base (10,000 notifications/second = peak load × 3)
- User experience research (100ms feels instant)
- Infrastructure constraints (database connection limits)
Write the targets so someone can check them later, and expect at least one to be wrong:
- Delivery latency usually lands near target, because it is the number the design optimizes for
- Peak throughput usually lands under the projection, because the projection was peak load times a safety factor
- Query latency is the common miss; preference lookups need an index the RFC did not specify
Pair each number with the place it will be read from: a dashboard panel, a log field, a load test. How these targets get instrumented is covered in the analytics and optimization post.
Security deserves the same specificity:
- Authentication: “JWT tokens with 15-minute expiry”
- Authorization: “Role-based access with granular permissions”
- Rate limiting: “Per-user limits with exponential backoff”
- Data privacy: “PII encryption at rest, GDPR compliance”
A security incident can surface months in, when someone attempts to use the notification system for spam; the rate limiting strategy specified in the RFC is what stops it.
Testing Strategy#
Load tests worth specifying:
### Load Tests
- High-volume notification sending
- Concurrent user connections
- Database performance under load
- Queue processing capacity
The value is in the specifics: it names which load is under test, sets clear pass/fail conditions, and names the tool (k6, Gatling, Locust) instead of leaving the choice to whoever picks up the ticket.
Testing sections routinely omit chaos testing for dependency failures (a cache or broker outage), cross-browser WebSocket compatibility, mobile app battery impact from persistent connections, and international character set handling in templates.
Monitoring & Analytics#
Most monitoring sections list every possible metric. Good ones identify the 3-5 metrics that indicate system health.
### Key Metrics
- Delivery success rate (target: 99.9%)
- Delivery time by channel
- User engagement rates
- Support ticket volume
Four metrics is roughly the limit of what a team checks daily.
The RFC suggested alerting on:
- High error rates (> 5%)
- Delivery delays (> 10s)
- System resource usage (> 80%)
What warrants an alert instead is a delivery success rate below 99%, an email delivery P99 over 30 seconds, or database connection pool exhaustion.
The real-time delivery post explains how to tell a genuine problem from normal variance.
Cost Analysis#
Good cost sections acknowledge both immediate and ongoing costs.
### Infrastructure Costs
- Database: $200-500/month
- Message Queue: $50-150/month
- Push Notification Services: $0.50 per 1000 notifications
Line items like these hold up, because they come from published pricing.
The predictable line items are rarely the whole bill. What tends to be left out:
- Log ingestion and retention, which scales with how much you decide to log
- Object storage for the notification archive
- The extra read replica added once query latency slips
- Engineering time for maintenance, a cost that continues after the project ends
Infrastructure line items are usually the smaller half of total cost of ownership. Name the omitted categories in the RFC even when you cannot price them.
The RFC projected:
- 20-30% reduction in support tickets
- 5-15% increase in user retention
A range is only useful when the measurement method ships alongside it: which ticket categories count, which retention cohort, measured over what window; without that, the conversation after launch is about definitions instead of results.
Risks & Mitigation#
The best risk sections admit what the authors don’t know.
Risk: Database performance degradation with high volume
Mitigation: Proper indexing, read replicas, query optimization
Of the risks a notification RFC lists, this is the one that lands. Query latency degrades gradually, then timeouts start arriving in batches. The listed mitigation works, but indexing, read replicas, and query rewrites are weeks of work rather than an afternoon.
Other risks go unanticipated: WebSocket connection limits in the load balancer, template rendering performance with nested conditionals, time zone edge cases for scheduled notifications, and mobile carriers blocking the SMS provider.
Success Criteria#
Make it measurable and realistic.
### Technical Success
- 99.9% notification delivery success rate
- < 100ms in-app notification delivery
- System handles 10,000+ notifications per second
They are measurable (specific numbers), achievable (based on comparable systems rather than wishful thinking), and relevant (tied directly to user experience).
Criteria get renegotiated after launch:
- The availability target drops a nine, because the last nine costs more than it returns
- Latency targets relax to the threshold users can perceive
- Throughput requirements fall to observed peak load rather than the projected one
Recording why a criterion changed, and getting stakeholder agreement on the replacement, keeps the change legitimate.
What Each Audience Looks For#
Different stakeholders care about different things:
| Audience | What they check first |
|---|---|
| VPs / directors | Executive summary with business value, cost analysis with clear ROI, a timeline with milestones, a risk section that doesn’t hide complexity |
| Senior engineers | Technical implementation that shows deep understanding, scalability considerations based on actual metrics, alternative approaches and why they were rejected, integration points with existing systems |
| Team leads | Implementation phases that deliver value iteratively, an executable testing strategy, success criteria their team can rally around, a monitoring approach that won’t create alert fatigue |
| Security teams | Authentication/authorization approach, data privacy considerations, rate limiting and abuse prevention, audit trail capabilities |
What RFCs Routinely Underweight#
Documentation Is Part of the System#
The RFC becomes the primary documentation whether you plan for it or not, so it helps to structure it as living documentation from the start.
Migration Strategy Matters#
RFCs focus on the new system and rarely mention migrating off the old one, even though migration is a project of its own that still needs a plan.
Operational Runbooks#
The RFC should include or mandate operational runbooks; the gap becomes obvious during the first production incident.
Feature Flags#
Phased rollout gets mentioned more often than feature flags, even though a flag turns a bad release from a rollback into a config change.
The RFC as a Living Document#
A useful RFC stays in use after approval, evolving into:
- Architecture documentation
- Onboarding materials for new team members
- Decision logs for future reference
- Post-mortem context when things go wrong
What Makes an RFC Worth Trusting#
Be Honest About Uncertainty#
The best RFCs include sections titled “What We Don’t Know Yet” or “Assumptions That Might Be Wrong.”
Escape Hatches and Measurable Success#
Explaining how to back out if things go wrong, alongside how to build the system, makes approval easier. Vague success criteria lead to endless debates: specific numbers force clarity about what you’re actually trying to achieve.
Show Your Work#
Include enough detail that another team could implement your design.
RFC Imperfection and Trade-offs#
No RFC is perfect. The notification system RFC walked through above has real gaps: it underestimates complexity, is thin on operational concerns, and is optimistic on timelines, yet it aligns stakeholders on a good-enough solution and leaves them a framework for improving it.
The RFC Paradox#
A pattern holds across many organizations: the teams that write the best RFCs often need them the least. Their communication is already strong, their thinking clear, their engineering practices sound, and the document simply formalizes what they do anyway. Teams that struggle with RFCs usually have deeper problems, such as unclear requirements, competing visions, or technical debt that makes every solution complex, and for them the RFC becomes a forcing function.
Use the full structure when a proposal crosses team boundaries, commits budget, or is expensive to reverse. Skip it when the change touches one team and can be undone in an afternoon; a short design note on the pull request carries the same information with less ceremony. Between those two cases, write the problem statement and the success criteria, and leave the rest to the implementation.
References#
- RFC Editor (opens in new tab) - The authoritative source for published RFCs, maintained by the RFC Production Center
- IETF RFC Process (opens in new tab) - Official IETF documentation on how RFCs are created, reviewed, and published
- Architectural Decision Records - adr.github.io (opens in new tab) - Community resource on ADRs, a lightweight alternative for capturing architecture decisions
- ADR Templates (opens in new tab) - Collection of ADR templates including the widely used Nygard format
- Google Engineering Practices (opens in new tab) - Google’s public documentation on engineering practices including design review processes
Related posts
Practical guidance on RFC structure, stakeholder review, and turning technical debates into decisions a team actually keeps.
rfc · documentation · architecture +3
How Arnold Mindell's Deep Democracy principles transform technical decision-making, build psychological safety, and ensure every voice strengthens architecture.
psychological-safety · team-management · team-dynamics +4
Documentation debt can slow teams faster than technical debt. A guide to treating docs as critical infrastructure and scaling knowledge across engineering teams.
documentation · rfc · team-management +3
An analysis of bait-and-switch hiring, power imbalances, and underemployment, with actionable frameworks for employees to protect themselves and employers to build trust.
hiring · career · team-dynamics +3
A hands-on guide to Event Storming: what it is, how to facilitate sessions effectively, and when to use this workshop technique for domain modeling.
domain-driven-design · architecture · agile +1