El arte de la toma de decisiones técnicas bajo incertidumbre
Un marco para decidir bien cuando los requisitos son incompletos y hay mucho en juego: análisis de reversibilidad, registros de decisiones y ambigüedad.

Toda decisión se toma con información incompleta
La característica definitoria de la toma de decisiones técnicas es que nunca se cuenta con suficiente información. Los requisitos cambian. Las características de rendimiento solo se manifiestan bajo carga. El comportamiento de integración solo aflora en producción. Esperar información perfecta equivale a no lanzar nada.
La habilidad no consiste en eliminar la incertidumbre, sino en tomar buenas decisiones a pesar de ella. Esto exige un enfoque sistemático para clasificar las decisiones, evaluar su reversibilidad y documentar el razonamiento, de modo que los equipos futuros entiendan no solo qué se decidió, sino por qué.
Clasificar las decisiones según su reversibilidad
Jeff Bezos popularizó la distinción entre decisiones de Tipo 1 (irreversibles, puertas de un solo sentido) y de Tipo 2 (reversibles, puertas de doble sentido). La mayoría de las decisiones técnicas son de Tipo 2, pero los equipos las tratan todas como si fueran de Tipo 1: las sobreanalizan, las someten a comités y las postergan hasta que el plazo termina decidiendo por ellos.
interface TechnicalDecision {
id: string;
title: string;
context: string;
options: DecisionOption[];
type: "type-1" | "type-2";
reversibilityCost: "trivial" | "moderate" | "significant" | "prohibitive";
timeToReverse: string;
decisionDate: string;
deciders: string[];
outcome?: string;
}
interface DecisionOption {
name: string;
pros: string[];
cons: string[];
risks: Risk[];
estimatedEffort: string;
reversibilityPlan: string;
}
interface Risk {
description: string;
likelihood: "low" | "medium" | "high";
impact: "low" | "medium" | "high";
mitigation: string;
}
// ❌ Treating every decision the same way
function handleDecision(decision: TechnicalDecision): string {
return "Schedule a meeting with all stakeholders";
}
// ✅ Matching decision process to decision type
function handleDecisionByType(decision: TechnicalDecision): string {
if (decision.type === "type-2") {
return decision.reversibilityCost === "trivial"
? "Decide now, one person, document briefly"
: "Quick discussion with 2-3 people, decide within a day";
}
// Type 1: Irreversible decisions deserve careful analysis
return "Write a detailed decision record, gather input from affected teams, set a decision deadline";
}El registro de decisiones como herramienta de pensamiento
Los Architecture Decision Records (ADR) no son burocracia: son un mecanismo que obliga a pensar con claridad. Poner por escrito el contexto, las opciones y la justificación revela vacíos en el razonamiento que la discusión verbal suele ocultar.
interface ADR {
number: number;
title: string;
status: "proposed" | "accepted" | "deprecated" | "superseded";
date: string;
context: string;
decision: string;
consequences: {
positive: string[];
negative: string[];
neutral: string[];
};
alternatives: Array<{
option: string;
rejected: string; // Why this was not chosen
}>;
supersededBy?: number;
}
function createADR(input: {
title: string;
context: string;
options: Array<{
name: string;
analysis: string;
}>;
chosenOption: string;
rationale: string;
}): ADR {
const chosen = input.options.find(
(o) => o.name === input.chosenOption
);
const rejected = input.options.filter(
(o) => o.name !== input.chosenOption
);
return {
number: getNextADRNumber(),
title: input.title,
status: "proposed",
date: new Date().toISOString().split("T")[0],
context: input.context,
decision: `We will use ${input.chosenOption}. ${input.rationale}`,
consequences: {
positive: [], // Fill during review
negative: [],
neutral: [],
},
alternatives: rejected.map((opt) => ({
option: opt.name,
rejected: opt.analysis,
})),
};
}Tomar decisiones bajo presión de tiempo
Cuando el plazo vence mañana y la cuestión de arquitectura sigue sin resolverse, se necesita un marco que produzca rápidamente una decisión defendible. El marco RAPID asigna roles claros: Recommend (recomendar), Agree (aprobar), Perform (ejecutar), Input (aportar) y Decide (decidir).
interface RAPIDDecision {
recommender: string; // Proposes the solution
agreers: string[]; // Must agree (blockers)
performers: string[]; // Will implement
inputProviders: string[]; // Consulted for expertise
decider: string; // Makes the final call
}
interface TimeboxedDecision {
decision: TechnicalDecision;
rapid: RAPIDDecision;
deadline: Date;
fallback: string; // What happens if no decision by deadline
}
function evaluateUnderPressure(
decision: TechnicalDecision,
timeAvailable: number // hours
): {
approach: string;
analysisDepth: "shallow" | "moderate" | "deep";
requiredParticipants: number;
} {
if (timeAvailable < 2) {
return {
approach: "Choose the most reversible option. " +
"Document the decision and revisit in one week.",
analysisDepth: "shallow",
requiredParticipants: 1,
};
}
if (timeAvailable < 8) {
return {
approach: "List top 2-3 options. Score on reversibility " +
"and alignment with existing architecture. " +
"Quick sync with one other engineer.",
analysisDepth: "moderate",
requiredParticipants: 2,
};
}
return {
approach: "Full ADR process. Evaluate all options against " +
"stated criteria. Seek input from affected teams.",
analysisDepth: "deep",
requiredParticipants: 3,
};
}Toma de decisiones impulsada por spikes
Cuando el análisis por sí solo no basta para resolver una decisión, conviene construir el prototipo más pequeño posible para generar datos reales. Un spike de dos días produce información más útil que dos semanas de debate.
interface Spike {
question: string;
hypothesis: string;
timebox: string;
successCriteria: string[];
deliverables: string[];
decision: string; // What will be decided based on results
}
// ❌ Debating database choice for weeks with no data
// "Let's discuss whether PostgreSQL or DynamoDB is better
// for our access patterns"
// ✅ Running a spike to generate concrete data
const databaseSpike: Spike = {
question: "Can DynamoDB handle our query patterns " +
"at the required latency?",
hypothesis: "DynamoDB single-table design can serve " +
"our 5 primary access patterns under 10ms p99",
timebox: "2 days",
successCriteria: [
"All 5 access patterns implemented",
"Load test at 2x projected traffic",
"p99 latency measured for each pattern",
"Cost estimate at projected scale",
],
deliverables: [
"Benchmark results for each access pattern",
"DynamoDB table design document",
"Cost projection spreadsheet",
"Go/no-go recommendation",
],
decision: "If all patterns meet latency targets, " +
"proceed with DynamoDB. Otherwise, use PostgreSQL.",
};
function evaluateSpikeResults(
spike: Spike,
results: Record<string, boolean>
): {
recommendation: string;
confidence: "high" | "medium" | "low";
openQuestions: string[];
} {
const metCriteria = Object.values(results).filter(Boolean).length;
const totalCriteria = Object.keys(results).length;
const successRate = metCriteria / totalCriteria;
if (successRate === 1) {
return {
recommendation: "Proceed with the proposed approach",
confidence: "high",
openQuestions: [],
};
}
if (successRate >= 0.7) {
const failed = Object.entries(results)
.filter(([, passed]) => !passed)
.map(([criteria]) => criteria);
return {
recommendation: "Proceed with mitigations for failed criteria",
confidence: "medium",
openQuestions: failed,
};
}
return {
recommendation: "Pursue alternative approach",
confidence: "high",
openQuestions: [],
};
}Aprender de decisiones pasadas
La parte más valiosa de un registro de decisiones es su actualización retrospectiva. Seis meses después de tomar una decisión, ya se sabe si fue acertada. Dejarlo por escrito construye conocimiento institucional sobre qué patrones de decisión funcionan y cuáles no.
interface DecisionRetrospective {
adrNumber: number;
originalDecision: string;
retrospectiveDate: string;
outcome: "validated" | "partially-validated" | "invalidated";
surprises: string[];
lessonsLearned: string[];
wouldChangeApproach: boolean;
whatWouldChange: string;
}
function generateRetrospectivePrompt(
adr: ADR
): string[] {
return [
`Did the decision achieve its intended goals?`,
`What consequences occurred that we didn't predict?`,
`Were the rejected alternatives actually better in hindsight?`,
`What information would have changed the decision?`,
`Would we make the same decision again with current knowledge?`,
`What should future teams know about this decision's real-world impact?`,
];
}Conclusiones clave
La calidad de las decisiones técnicas no depende de contar con información completa, sino de tener un proceso sistemático para navegar la incertidumbre. Clasifica primero cada decisión según su reversibilidad: la mayoría son de Tipo 2 y merecen horas de análisis, no semanas. Escribe los registros de decisiones no como documentación, sino como una herramienta de pensamiento que revela vacíos en tu razonamiento.
Cuando el tiempo escasea, elige la opción más reversible y documéntala. Cuando el análisis se estanca, ejecuta un spike con tiempo limitado para generar datos reales. El prototipo de dos días que produce mediciones vence al debate de dos semanas que solo produce opiniones.
La decisión en sí misma es solo la mitad del valor. La retrospectiva —escrita seis meses después, con datos reales de producción— es donde se acumula el conocimiento institucional. Los equipos que revisan sistemáticamente sus decisiones pasadas toman mejores decisiones futuras, no porque recuerden cada detalle, sino porque desarrollan una intuición calibrada sobre qué patrones de razonamiento conducen a buenos resultados.


