Cómo escribir RFCs técnicos efectivos
Cómo redactar RFCs que impulsen decisiones técnicas claras: estructura, calibración de la audiencia, análisis de alternativas y consenso asíncrono.

Un RFC (Request for Comments) es un documento estructurado que propone una decisión técnica e invita a recibir comentarios antes de comenzar la implementación. Obliga al autor a pensar el problema con rigor, saca a la luz las inquietudes de las partes interesadas desde el principio y deja un registro permanente de por qué se tomaron las decisiones.
El costo de escribir un RFC se mide en horas. El costo de no escribirlo —construir algo equivocado, descubrir problemas de integración a mitad de sprint o tomar decisiones que solo una persona entendió— se mide en semanas o meses.
Cuándo escribir un RFC
No todos los cambios necesitan un RFC. Agregar una función utilitaria, por ejemplo, no lo requiere. Pero cualquier cambio que afecte a varios equipos, introduzca infraestructura nueva, modifique una abstracción central o sea difícil de revertir sí debería tener uno.
// 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 RFCEstructura de un RFC
Un RFC bien estructurado facilita que quienes lo revisan entiendan el problema, evalúen la propuesta y den comentarios concretos. A continuación, una plantilla que funciona en la mayoría de las organizaciones de ingeniería.
# 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.Cómo redactar la sección de motivación
La sección de motivación es donde fallan la mayoría de los RFCs. Los autores saltan directo a la solución sin explicar antes por qué el estado actual es insuficiente. Quienes revisan el documento y no comparten el contexto del autor no van a entender por qué el cambio importa.
// ❌ 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."La sección de alternativas
La sección de alternativas es la parte más importante de un RFC para generar confianza en quienes lo revisan. Cuando demuestras que consideraste honestamente varios enfoques —incluida la opción de no hacer nada— quienes revisan saben que hiciste bien la tarea.
// 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',
},
];Cómo recopilar comentarios de forma efectiva
Un RFC que nadie lee durante dos semanas no le sirve a nadie. Pedir comentarios de forma activa a las personas adecuadas en el momento adecuado es lo que hace que el proceso funcione.
// ❌ 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',
};Del RFC al registro de decisión
Una vez terminado el período de revisión, el RFC se convierte en un registro de decisión. Documenta qué se decidió, el razonamiento detrás y cualquier modificación que haya surgido del proceso de revisión.
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',
],
};Puntos clave
- Escribe RFCs para cambios difíciles de revertir, que involucren a varios equipos o que sean controvertidos — no todo necesita uno, pero sí cualquier cosa con un radio de impacto considerable
- Empieza por la motivación — los planteamientos del problema respaldados por datos generan confianza en quienes revisan y justifican la inversión de ingeniería
- Incluye siempre alternativas — evaluar con honestidad la opción de "no hacer nada" y al menos un enfoque alternativo demuestra un pensamiento riguroso
- Dirige tus pedidos de comentarios — hazle preguntas concretas a personas concretas en lugar de difundir un genérico "por favor revisen"
- Define una fecha límite para la decisión — los períodos de revisión abiertos generan demoras indefinidas; ponle un límite de tiempo a la discusión
- Registra la decisión y los desacuerdos — quienes trabajen en el proyecto en el futuro necesitan entender no solo qué se decidió, sino por qué y qué concesiones se aceptaron


