All posts
processguide

From RFC to Implementation: Bridging the Gap

An approved RFC that never gets built is worse than no RFC at all — here's how to close the loop between decisions and code.

DesignDoc Team··6 min read

There's a specific kind of waste that's endemic in engineering organizations. A team spends two weeks writing an RFC. Senior engineers spend hours reviewing it. Stakeholders weigh in. The RFC is approved with consensus. Then it goes into a Google Doc graveyard, and six months later someone builds something that vaguely resembles the proposal but diverges in critical ways that nobody tracked.

The RFC process has a last-mile problem. Approval is not the finish line — it's the halfway point.

Why RFCs Die After Approval

The most common failure mode isn't that teams ignore their RFCs. It's more subtle than that. The RFC represents a snapshot of understanding at a specific point in time. By the time implementation starts — sometimes weeks later — the team's understanding has evolved. They discover edge cases the RFC didn't cover. Dependencies have changed. A constraint that seemed fixed turns out to be negotiable.

Without a mechanism to keep the RFC connected to the implementation, the document becomes stale. Engineers stop referencing it because "it's outdated." New team members never read it because it "doesn't reflect how things actually work." The RFC becomes organizational fiction — a document that says one thing while the codebase says another.

This is worse than having no RFC at all. At least without an RFC, nobody has the false confidence that a decision was thoroughly documented. A stale RFC actively misleads.

Status Tracking That Reflects Reality

The first fix is simple: give RFCs a lifecycle that extends through implementation.

Most teams have something like Draft and Approved. That's not enough. A practical lifecycle looks like this:

Draft -- The RFC is being written. It's incomplete and not ready for formal review. Sharing it early for pre-feedback is fine, but nobody should be blocking on it.

In Review -- The RFC is complete enough for formal review. Designated reviewers are expected to provide feedback. There's a clear owner who incorporates feedback and drives toward a decision.

Accepted -- The decision has been made. The approach described in the RFC is the one the team will pursue. This is where most processes stop, and it's exactly where the interesting work begins.

Implementing -- Active development is underway. The RFC is a living reference, and the implementation owner is responsible for flagging any deviations. This status is the critical one that most teams skip.

Done -- The implementation is complete and matches the RFC (or the RFC has been updated to match the implementation). The RFC is now a historical record of what was decided and what was built.

Superseded -- A newer RFC has replaced this one. The link to the superseding RFC should be prominent so anyone finding the old document immediately knows it's been replaced.

The key insight is that "Implementing" is a distinct status that carries specific obligations. It means someone is actively responsible for the relationship between the document and the code.

Breaking an RFC Into Implementation Tasks

An RFC describes what and why. Implementation tasks describe how and when. Bridging these requires explicit decomposition.

Here's a pattern that works well: after an RFC is accepted, the implementation owner creates a tracking issue (in GitHub, Linear, Jira, or whatever your team uses) that links back to the RFC. That tracking issue contains a checklist of concrete implementation steps derived from the RFC.

For example, if the RFC proposes migrating from REST to gRPC for inter-service communication, the implementation tasks might be:

  • Define .proto files for the three affected service interfaces
  • Generate Go client/server stubs
  • Implement the server-side handlers in the user service
  • Update the API gateway to route gRPC calls
  • Add integration tests for the new endpoints
  • Run dual-write validation for two weeks in staging
  • Cut over production traffic
  • Remove the old REST endpoints after a 30-day deprecation period

Each of these is a trackable unit of work. Each links back to the RFC for context. And collectively, they provide visibility into how much of the RFC has been implemented.

The anti-pattern is treating the RFC approval as the end of planning. Teams that skip the decomposition step tend to implement the "easy" parts of an RFC and indefinitely defer the hard parts — the migration plan, the rollback strategy, the cleanup of the old system. The RFC said all of these things would happen, but without tasks, nobody is accountable for them.

Keeping the RFC Alive During Implementation

During implementation, the RFC serves two functions: it's a reference for the team doing the work, and it's a communication artifact for everyone else. Both functions require the document to stay current.

This doesn't mean rewriting the RFC every time you discover something new. It means adding an "Implementation Notes" section at the bottom where the team records deviations, discoveries, and decisions made during implementation. For example:

Implementation Note (March 15): The RFC proposed using a single gRPC service definition for both internal and external consumers. During implementation, we discovered that external consumers need field-level access control that's cleaner to implement as a separate service definition. We now have internal.proto and external.proto. The core approach is unchanged.

This kind of annotation is invaluable. Six months from now, when someone reads the RFC and notices the codebase has two proto files instead of one, the note explains why. Without it, the discrepancy looks like either a mistake or an undocumented decision — both of which erode trust in the RFC process.

Some teams we've talked to schedule a brief mid-implementation check-in: a 15-minute review where the implementation owner walks through what's matched the RFC and what's diverged. This catches drift early, before it compounds.

When Implementation Diverges From the RFC

Divergence is not a failure. It's expected. Implementation always reveals things that weren't visible during the design phase. The failure is untracked divergence.

When the implementation needs to deviate from the RFC in a material way, you have three options:

Minor divergence: annotate. If the change is small and doesn't affect the core decision (like the proto file example above), add an implementation note to the RFC. No formal re-review needed.

Significant divergence: amend the RFC. If a key assumption has changed — say, the performance benchmarks showed that the proposed approach doesn't meet latency requirements — update the RFC with a clearly marked amendment section. Notify the original reviewers. This isn't starting over; it's acknowledging that reality has provided new information.

Fundamental divergence: write a new RFC. If the implementation has revealed that the entire approach is wrong, don't try to patch the original document into something unrecognizable. Supersede it. Write a new RFC that references the original, explains what was learned during implementation, and proposes the revised approach. This creates a clear decision trail.

The worst option is the one most teams default to: silently building something different from what the RFC described and never updating any documentation. This is how teams end up with a codebase that contradicts their design documents, and it's the fastest way to make the entire RFC process feel pointless.

Closing the Loop

The final step is marking the RFC as done and linking it to the implementation. This sounds ceremonial, but it serves a concrete purpose: it tells future readers that this RFC was actually built, and points them to where the code lives.

A good "done" annotation includes:

  • Link to the primary PR(s) or tracking issue where the implementation landed
  • Date of completion so readers understand the timeline
  • Delta summary — a brief note on how the final implementation differs from the original proposal, if at all
  • Status of any deferred items — parts of the RFC that were intentionally postponed

At DesignDoc, we make this easy by supporting status transitions with required fields. When you move an RFC to "Done," you're prompted to add implementation links and a completion summary. It's a small friction that prevents the common failure of approved RFCs floating in limbo indefinitely.

The Payoff

Teams that close the loop between RFCs and implementation get a compounding benefit. Over time, their RFC repository becomes an accurate map of their architecture — not a collection of aspirational documents, but a linked history of decisions and their outcomes. New engineers can trace from a line of code to the PR that introduced it, from the PR to the RFC that motivated it, and from the RFC to the problem it was solving.

This is the real promise of a design document process. Not the documents themselves, but the decision trail — the ability to understand not just what was built, but why, and whether the result matched the intent.

The gap between decision and implementation is where most RFC processes fail. Closing it isn't complicated. It just requires treating implementation as part of the RFC lifecycle, not something that happens after it.

Stop losing decisions in Slack and Docs

DesignDoc gives every RFC a structured workflow, inline reviews, and a permanent home.

Get Started