Skip to content
Published on

The Complete Guide to Technical Writing in English: RFC, ADR, and Design Doc Patterns

Authors
  • Name
    Twitter
Technical Writing Guide

Introduction

Software engineers spend only 30-40% of their working time actually writing code. The rest goes to design discussions, code reviews, and documentation. In global engineering organizations, the ability to write clear technical documents in English is a decisive factor in career growth. Companies like Google, Meta, Uber, and Stripe have deeply ingrained cultures where engineers write technical documents and gain team consensus before writing any code.

The three primary forms of technical documentation -- RFC (Request for Comments), ADR (Architecture Decision Record), and Design Doc -- each serve distinct purposes with different structures. This guide clearly distinguishes these document types, provides the key English expression patterns used when writing them, and offers practical templates you can adapt immediately.


1. Types and Purposes of Technical Documents

Key Differences Between RFC, ADR, and Design Doc

AspectRFCADRDesign Doc
PurposeCollect team feedback on a new proposalRecord and preserve architecture decisionsAchieve consensus on detailed technical design
Length2-5 pages1-2 pages5-20 pages
LifespanArchived after approval/rejectionPermanently preserved (history)Living document during project
AudienceBroad (team, organization)Current and future team membersImplementation team and reviewers
TimingIdea/proposal stageImmediately after a decisionJust before implementation
Core Question"Is this the right direction?""Why did we make this decision?""How will we implement this?"

When to Use Each Document

  • RFC: When proposing new technology adoption, process changes, or organization-wide standards
  • ADR: When recording tech stack selections, database migrations, API versioning strategies, or other key decisions
  • Design Doc: When planning new feature implementation, system architecture changes, or large-scale refactoring

2. RFC Structure and Key Expressions

Standard RFC Section Structure

1. Title / RFC Number
2. Status: Draft | In Review | Accepted | Rejected | Superseded
3. Author(s)
4. Date
5. Summary / Abstract
6. Motivation / Problem Statement
7. Detailed Proposal
8. Alternatives Considered
9. Risks and Mitigations
10. Open Questions
11. References

Template 1: Full RFC Template

# RFC-042: Migrate to Event-Driven Architecture for Order Processing

**Status:** In Review
**Author:** Jane Kim
**Date:** 2026-03-10
**Reviewers:** Backend Team, Platform Team

## Summary

This RFC proposes migrating the order processing pipeline from a synchronous
REST-based architecture to an event-driven architecture using Apache Kafka.
The goal is to improve system resilience and reduce coupling between services.

## Motivation

The current synchronous order processing pipeline has the following limitations:

- **Single point of failure**: If the payment service is down, the entire
  order flow is blocked.
- **Tight coupling**: Adding a new step (e.g., fraud detection) requires
  modifying multiple services.
- **Scalability bottleneck**: Peak traffic (Black Friday) causes cascading
  timeouts across services.

## Detailed Proposal

We propose introducing Kafka as the central message broker with the following
topology:

- Order Service publishes `OrderCreated` events
- Payment Service subscribes to `OrderCreated` and publishes `PaymentProcessed`
- Inventory Service subscribes to `PaymentProcessed`
- Notification Service subscribes to all terminal events

### Migration Strategy

Phase 1: Dual-write (weeks 1-4) - Both sync and async paths active
Phase 2: Shadow mode (weeks 5-8) - Async primary, sync for validation
Phase 3: Cutover (weeks 9-10) - Async only, sync deprecated

## Alternatives Considered

### Alternative A: gRPC with Circuit Breaker

- Pro: Lower latency for happy path
- Con: Does not address fundamental coupling issue

### Alternative B: AWS Step Functions

- Pro: Managed service, less operational overhead
- Con: Vendor lock-in, limited throughput at scale

## Risks and Mitigations

| Risk                     | Likelihood | Impact | Mitigation                     |
| ------------------------ | ---------- | ------ | ------------------------------ |
| Message ordering issues  | Medium     | High   | Partition key by order ID      |
| Consumer lag during peak | Low        | Medium | Auto-scaling consumer groups   |
| Data consistency         | Medium     | High   | Saga pattern with compensation |

## Open Questions

1. Should we use Kafka Connect for database CDC, or custom producers?
2. What is the acceptable end-to-end latency for order confirmation?

## References

- Internal: Design Doc for Kafka Cluster Setup (link)
- External: "Designing Event-Driven Systems" by Ben Stopford

Key RFC English Expression Patterns

Opening a proposal:

"This RFC proposes [verb-ing]..."
"The goal of this proposal is to [verb]..."
"We are seeking feedback on [noun/gerund]..."

Explaining motivation:

"The current approach has the following limitations..."
"This change is motivated by..."
"We have observed that..."

Comparing alternatives:

"We considered [N] alternatives before arriving at this proposal."
"While [Alternative A] addresses [problem], it falls short in [area]."
"The primary trade-off of this approach is..."

3. ADR Templates and Practical Writing

Core Principles of ADR

ADRs should be short and clear. The original format proposed by Michael Nygard consists of five sections: Title, Status, Context, Decision, and Consequences. The key principle is to record decisions so that future team members can understand why a decision was made.

Template 2: Nygard-Style ADR

# ADR-017: Use PostgreSQL as the Primary Database

## Status

Accepted (2026-03-10)

## Context

We are building a new order management system that requires:

- ACID transactions for financial data integrity
- Complex queries with JOIN operations across multiple tables
- JSON support for flexible product metadata
- Strong ecosystem of ORMs and tooling

The team has experience with both PostgreSQL and MySQL. We also evaluated
NoSQL options (MongoDB, DynamoDB) for their scalability characteristics.

## Decision

We will use PostgreSQL 16 as the primary database for the order management
system.

Key factors in this decision:

- Native JSONB support eliminates the need for a separate document store
- Row-level security aligns with our multi-tenant architecture
- pg_partitioning handles our anticipated data growth (100M+ rows/year)
- The team has 3 engineers with deep PostgreSQL expertise

## Consequences

### Positive

- Unified data layer reduces operational complexity
- Strong consistency guarantees simplify application logic
- Rich extension ecosystem (PostGIS, pg_trgm) enables future features

### Negative

- Horizontal scaling requires careful planning (read replicas, partitioning)
- Higher memory requirements compared to MySQL for equivalent workloads
- Team must invest in PostgreSQL-specific performance tuning knowledge

Template 3: MADR (Markdown Architectural Decision Records) Style

# Use React Server Components for the Dashboard

## Context and Problem Statement

The dashboard currently uses client-side rendering (CSR) with React SPA.
Initial load times exceed 4 seconds on mobile devices, and SEO is not
possible for public-facing dashboard pages. We need to decide on a rendering
strategy that improves performance while maintaining interactivity.

## Decision Drivers

- Page load time must be under 2 seconds on 3G networks
- SEO required for public dashboard pages
- Developer experience should not significantly degrade
- Must be compatible with our existing component library

## Considered Options

1. React Server Components (RSC) with Next.js App Router
2. Static Site Generation (SSG) with Incremental Static Regeneration
3. Server-Side Rendering (SSR) with streaming

## Decision Outcome

Chosen option: "React Server Components with Next.js App Router" because
it provides the best balance of performance, SEO, and developer experience.

### Positive Consequences

- 60-70% reduction in client-side JavaScript bundle
- Automatic code splitting at the component level
- Server-side data fetching eliminates client-side waterfalls

### Negative Consequences

- Learning curve for the team (estimated 2-week ramp-up)
- Some third-party libraries are not yet RSC-compatible
- Debugging server/client boundary issues can be challenging

Key ADR English Expressions

"In the context of [situation], facing [concern]..."
"We decided for [option] and against [other options]..."
"We accept that [consequence] because [rationale]."
"This decision was driven by [factor]."
"The decision is [accepted/deprecated/superseded by ADR-XXX]."

4. Design Doc Structure: Google/Uber Style

Standard Structure of a Google-Style Design Doc

At Google, Design Docs are the "core tool for achieving team consensus before writing code." A good Design Doc covers design context, goals, detailed design, and alternative analysis.

1. Title and Metadata (authors, reviewers, status, last updated)
2. Overview / TL;DR (1 paragraph summary)
3. Context / Background
4. Goals and Non-Goals
5. Detailed Design
   - System Architecture
   - API Design
   - Data Model
   - Error Handling
6. Alternatives Considered
7. Cross-Cutting Concerns (security, privacy, monitoring)
8. Migration / Rollout Plan
9. Open Questions
10. Appendix

Template 4: Full Design Doc Template (Google Style)

# Design Doc: Real-Time Notification Service

**Authors:** Jane Kim, Alex Park
**Reviewers:** Backend Lead, SRE Team, Security Team
**Status:** In Review
**Last Updated:** 2026-03-10

## Overview

This document describes the design for a real-time notification service
that delivers push notifications, in-app messages, and email digests to
users. The service will handle up to 10 million notifications per day
with a p99 delivery latency of under 500ms for push notifications.

## Context

Currently, notifications are sent inline within each microservice. This
leads to duplicated notification logic across 12 services, inconsistent
delivery behavior, and no centralized tracking of notification state.
User complaints about duplicate or missing notifications have increased
by 40% in Q4 2025.

## Goals

- Provide a unified notification API for all backend services
- Support push, in-app, and email channels with extensible architecture
- Achieve p99 delivery latency under 500ms for push notifications
- Enable user-level notification preferences and quiet hours

## Non-Goals

- Real-time chat or messaging features (separate project)
- SMS notifications (planned for Phase 2, not in this scope)
- Notification content personalization using ML (future enhancement)

## Detailed Design

### System Architecture

The notification service consists of four main components:

- **Ingestion Layer**: gRPC API that accepts notification requests,
  validates payloads, and publishes to Kafka
- **Router**: Consumes events, resolves user preferences, and routes
  to the appropriate channel handler
- **Channel Handlers**: Dedicated workers for push (FCM/APNs),
  in-app (WebSocket), and email (SendGrid)
- **State Store**: PostgreSQL for notification metadata and delivery
  status tracking

### API Design

The primary endpoint accepts notification requests:

POST /v1/notifications

Required fields: recipient_id, channel, template_id, parameters
Optional fields: priority, scheduled_at, idempotency_key

Response: notification_id, status (queued/delivered/failed)

### Data Model

Notifications table stores the core notification data:

- id (UUID, primary key)
- recipient_id (UUID, indexed)
- channel (enum: push, in_app, email)
- template_id (string)
- status (enum: queued, processing, delivered, failed)
- created_at, delivered_at (timestamps)

### Error Handling

- Transient failures: Exponential backoff with jitter, max 3 retries
- Permanent failures: Dead letter queue with alerting
- Duplicate prevention: Idempotency key with 24-hour TTL in Redis

## Alternatives Considered

### Alternative 1: Use a managed service (AWS SNS + SES)

- Pro: No operational overhead, built-in scalability
- Con: Limited customization for in-app notifications, cost at scale
  (estimated 3x our infrastructure cost at 10M notifications/day)
- Decision: Rejected due to cost and limited in-app support

### Alternative 2: Extend existing per-service notification logic

- Pro: No new infrastructure, incremental improvement
- Con: Does not address the root cause of duplication and inconsistency
- Decision: Rejected as it perpetuates technical debt

## Cross-Cutting Concerns

### Security

- All notification payloads encrypted at rest (AES-256)
- PII fields (email, device tokens) stored in a separate encrypted store
- API authentication via service-to-service mTLS

### Monitoring

- Dashboard: delivery rate, latency percentiles, failure rate by channel
- Alerts: delivery rate drops below 99%, p99 latency exceeds 1s

## Rollout Plan

- Week 1-2: Deploy service in staging, integration tests
- Week 3: Shadow mode (dual-write to old and new system)
- Week 4: Canary rollout to 5% of traffic
- Week 5-6: Gradual ramp to 100%, deprecate old notification code

## Open Questions

1. Should we support batch notification API for marketing campaigns?
2. How do we handle notification delivery when user is offline for
   extended periods (> 7 days)?

Writing Effective Goals vs Non-Goals

Non-Goals are not simply negated goals. They are things that could reasonably be in scope but are intentionally excluded.

# Good Non-Goals
"SMS notifications (planned for Phase 2, not in this scope)"
"Real-time chat or messaging features (separate project)"
"Notification content personalization using ML (future enhancement)"

# Bad Non-Goals (too obvious)
"The system should not crash" (obvious requirement)
"We will not rewrite the entire codebase" (unrealistic alternative)

5. English Expression Patterns for Technical Documents

Proposing

ExpressionWhen to Use
We propose adopting...RFC introduction
We recommend using...Recommendations in conclusions
We should consider...Presenting alternatives
One possible approach is to...Design alternative discussions
X is the best fit for our requirements because...Justifying a choice

Comparing

ExpressionWhen to Use
B outperforms A in terms of...Performance comparisons
Compared to X, this approach...Alternative comparisons
There are trade-offs between...Trade-off analysis
X has a clear advantage over Y in...Specific aspect comparison
X and Y are comparable in terms of...Performance parity

Concluding

ExpressionWhen to Use
Therefore, we have chosen to...Final decision
Based on the analysis above...Drawing conclusions
In summary, this design...Wrapping up documents
The next steps are...Action items
This proposal assumes that...Stating assumptions

Expressing Uncertainty

Expressing what is not yet certain is equally important in technical documents.

"We believe this approach will scale to 10M requests/day,
 but this needs to be validated through load testing."

"The estimated latency improvement is 30-50%, pending benchmarks."

"This is our current best understanding; we will revisit after
 the proof of concept."

6. Code Review Comment Expressions

Technical document writing skills transfer directly to code review comments. Review comments are shorter than documents, but clarity and constructive tone are critical.

Common Review Abbreviations

AbbreviationFull FormMeaningExample
LGTMLooks Good To MeApproval"LGTM, ship it!"
NITNitpickMinor suggestion"nit: trailing whitespace on line 42"
PTALPlease Take Another LookRe-review request"Updated the error handling. PTAL."
WIPWork In ProgressNot complete"WIP: still need to add tests"
TBDTo Be DeterminedUndecided"TBD: will finalize after benchmarks"
ACKAcknowledgedAgreement"ACK, will address in follow-up PR"
NACKNot AcknowledgedDisagreement"NACK: this breaks backward compatibility"

Constructive Feedback Patterns

# Suggestion (soft tone)
"Consider extracting this into a helper function for reusability."

# Question (seeking understanding)
"What is the rationale for using a map here instead of a slice?"

# Blocking issue (must fix before merge)
"This must be addressed before merging: the error is silently swallowed,
 which could mask production issues."

# Praise (acknowledging good code)
"Nice approach! Using the builder pattern here makes the API much more
 readable."

# NIT (minor improvement)
"nit: This variable name could be more descriptive. Maybe
 'userNotificationPreferences' instead of 'prefs'?"

7. Detailed Comparison: RFC vs ADR vs Design Doc

Comparison AspectRFCADRDesign Doc
Primary PurposeBuild consensusRecord decisionsDetail the design
TimingIdea/proposal stageImmediately after decisionJust before implementation
Primary AudienceBroad organizationFuture team membersImplementation team/reviewers
Length2-5 pages1-2 pages5-20 pages
Key SectionsMotivation, Proposal, AlternativesContext, Decision, ConsequencesGoals, Design, Alternatives, Rollout
Status ManagementDraft - In Review - Accepted - RejectedProposed - Accepted - Deprecated - SupersededDraft - In Review - Approved - Archived
Update FrequencyModified during review periodRarely modified (immutable)Continuously updated during implementation
Used ByUber, Rust, Python (PEP)ThoughtWorks, GitHubGoogle, Meta, Amazon
Failure ModeApproved without sufficient reviewMissing context confuses future teamNo alternatives: "just do this"

8. Common Mistakes and Corrections

Mistake 1: Jumping Straight to the Solution Without Motivation

# Bad
"We should use Kafka for message processing."

# Good
"The current synchronous pipeline fails under peak load, causing
cascading timeouts. We propose using Kafka to decouple services
and improve resilience."

Principle: Always explain "Why" before presenting "What."

Mistake 2: Superficial Alternative Analysis

# Bad
"We considered MongoDB but decided not to use it."

# Good
"We considered MongoDB for its flexible schema support. However,
our workload is primarily relational with complex JOIN queries across
5+ tables. MongoDB would require application-level joins, increasing
code complexity and latency. Therefore, PostgreSQL better fits our
query patterns."

Principle: Provide concrete pros, cons, and rejection rationale for each alternative.

Mistake 3: Overusing Passive Voice

# Bad (excessive passive voice)
"It was decided that the service would be migrated."

# Good (active voice)
"We decided to migrate the service to the new cluster."

Principle: Both Google and Microsoft style guides recommend active voice for clarity.

Mistake 4: Vague Quantitative Claims

# Bad
"The system should be fast and handle lots of requests."

# Good
"The system must achieve a p99 latency of under 200ms and sustain
50,000 requests per second during peak hours."

Principle: Use specific, measurable metrics.

Mistake 5: Omitting Non-Goals

Leaving out the Non-Goals section in a Design Doc allows reviewers to misunderstand the scope. Explicitly stating "this is not in scope for this document" prevents scope creep.

# Non-Goals section example
"Non-Goals:
- This design does not cover the migration of historical data.
  That will be addressed in a separate design doc.
- Real-time analytics on notification delivery is out of scope
  for this phase."

9. Additional Template Examples

Template 5: Lightweight RFC (One-Page Format)

# RFC: Adopt Structured Logging Across All Services

**Author:** Alex Park | **Date:** 2026-03-10 | **Status:** Draft

## Problem

Our current logging is inconsistent: some services use plain text,
others use JSON. This makes log aggregation and alerting unreliable.
On average, engineers spend 2 hours per incident parsing logs manually.

## Proposal

Adopt structured JSON logging with a standardized schema across all
services. Use the following mandatory fields:

- timestamp (ISO 8601)
- level (DEBUG, INFO, WARN, ERROR)
- service_name
- trace_id
- message

## Impact

- Estimated 40% reduction in mean-time-to-detect (MTTD)
- Enables automated anomaly detection via log pattern analysis
- Requires updating 23 services (estimated 2 sprints)

## Decision Requested

Please review and comment by 2026-03-24. If no blocking concerns
are raised, we will proceed with implementation.

Template 6: ADR with History Tracking

# ADR-023: Switch from REST to gRPC for Internal Service Communication

## Status

Superseded by ADR-031 (2026-02-15)

## Context

In 2025, internal service-to-service calls used REST with JSON payloads.
As the number of microservices grew from 8 to 35, serialization overhead
and lack of type safety became significant pain points.

## Decision

We adopted gRPC with Protocol Buffers for all new internal service
communication. Existing REST endpoints were maintained for external
API consumers.

## Consequences

- 30% reduction in inter-service latency due to binary serialization
- Strongly typed contracts reduced integration bugs by 25%
- Increased complexity in debugging (binary payloads not human-readable)
- Required investment in gRPC tooling and team training

## Superseded By

ADR-031 introduced gRPC-Web for browser clients, extending this
decision to cover frontend-to-backend communication as well.

Template 7: Design Doc Migration Plan Section

## Migration Plan

### Phase 1: Foundation (Weeks 1-3)

- Deploy the new notification service to staging environment
- Set up monitoring dashboards and alerting rules
- Complete integration tests with all upstream services

### Phase 2: Shadow Mode (Weeks 4-6)

- Enable dual-write: existing system + new service
- Compare delivery rates and latency between old and new systems
- Acceptance criteria: new system delivers 99.9%+ of notifications
  within p99 latency target

### Phase 3: Gradual Rollout (Weeks 7-10)

- Week 7: Route 5% of traffic to new service (canary)
- Week 8: Increase to 25% if metrics are healthy
- Week 9: Increase to 75%
- Week 10: 100% cutover, begin decommissioning old code

### Rollback Plan

- Feature flag to instantly revert to the old notification path
- Data reconciliation job to sync any missed notifications
- Rollback decision criteria: error rate exceeds 1% or p99 latency
  exceeds 2 seconds for more than 5 minutes

10. Technical Document Writing Checklist

RFC Checklist

  • Title describes the change in one sentence
  • Motivation section describes the current problem with concrete data
  • Proposal is specific enough to be implementable
  • At least 2 alternatives compared
  • Risks and mitigations included
  • Review deadline and decision criteria specified

ADR Checklist

  • Context is sufficient for understanding
  • Decision is summarizable in one sentence
  • Both positive and negative consequences recorded
  • Status is correctly set
  • References to related ADRs or RFCs included

Design Doc Checklist

  • TL;DR (Overview) conveys the core idea in 30 seconds
  • Goals and Non-Goals are clearly distinguished
  • System architecture diagram included
  • API design and data model described
  • Alternative analysis is objective and unbiased
  • Migration/rollout plan included
  • Rollback strategy exists
  • Cross-cutting concerns (security, monitoring) addressed

Summary: Core Principles of Technical Writing

  1. Reader-first: Identify your audience first, then provide the information they need
  2. Why before What: Always explain the problem and motivation before presenting the solution
  3. Be specific: Use quantitative, measurable metrics instead of vague descriptions
  4. Active voice: Write clearly with active voice constructions
  5. Show alternatives: Always record considered alternatives and rejection rationale
  6. Keep it updated: Design Docs are living documents -- reflect implementation changes
  7. Invite feedback: The essence of an RFC is feedback collection -- actively use Open Questions

Mastering technical writing is not about perfect grammar. It is about structuring your thoughts logically, communicating clearly, and making it easy for others to understand your reasoning. These skills are the foundation of effective engineering leadership in any global team.