Die Kunst der technischen Entscheidungsfindung unter Unsicherheit
Ein Rahmen für fundierte technische Entscheidungen bei unvollständigen Anforderungen: Reversibilitätsanalyse, Entscheidungsprotokolle, Mehrdeutigkeit.

Jede Entscheidung wird mit unvollständigen Informationen getroffen
Das entscheidende Merkmal technischer Entscheidungsfindung ist, dass man nie genug Informationen hat. Anforderungen ändern sich. Performance-Eigenschaften zeigen sich erst unter Last. Integrationsverhalten tritt erst in der Produktion zutage. Wer auf perfekte Informationen wartet, liefert nie etwas aus.
Die eigentliche Fähigkeit besteht nicht darin, Unsicherheit zu beseitigen, sondern trotz ihrer gute Entscheidungen zu treffen. Das erfordert einen systematischen Ansatz, um Entscheidungen zu klassifizieren, ihre Reversibilität zu bewerten und die Begründung so zu dokumentieren, dass künftige Teams nicht nur verstehen, was entschieden wurde, sondern auch warum.
Entscheidungen nach Reversibilität klassifizieren
Jeff Bezos prägte die bekannte Unterscheidung zwischen Typ-1-Entscheidungen (unumkehrbar, Einbahntüren) und Typ-2-Entscheidungen (umkehrbar, Zweiwegtüren). Die meisten technischen Entscheidungen sind vom Typ 2, doch Teams behandeln sie alle wie Typ 1 – sie analysieren sie zu Tode, lassen Gremien darüber beraten und verzögern, bis die Frist die Entscheidung an ihrer Stelle trifft.
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";
}Das Entscheidungsprotokoll als Denkwerkzeug
Architecture Decision Records (ADRs) sind keine Bürokratie – sie erzwingen klares Denken. Wer Kontext, Optionen und Begründung schriftlich festhält, deckt Lücken in der eigenen Argumentation auf, die eine mündliche Diskussion verbirgt.
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,
})),
};
}Entscheidungsfindung unter Zeitdruck
Wenn die Frist morgen abläuft und die Architekturfrage noch offen ist, braucht man ein Vorgehen, das rasch eine vertretbare Entscheidung liefert. Das RAPID-Framework weist klare Rollen zu: Recommend (Vorschlagen), Agree (Zustimmen), Perform (Ausführen), Input (Beitragen) und Decide (Entscheiden).
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,
};
}Entscheidungsfindung durch Spikes
Wenn Analyse allein eine Entscheidung nicht klären kann, sollte man den kleinstmöglichen Prototyp bauen, um echte Daten zu gewinnen. Ein zweitägiger Spike liefert nützlichere Erkenntnisse als zwei Wochen Diskussion.
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: [],
};
}Aus vergangenen Entscheidungen lernen
Der wertvollste Teil eines Entscheidungsprotokolls ist die spätere Rückschau. Sechs Monate nach einer Entscheidung weiß man, ob sie richtig war. Das schriftlich festzuhalten, baut institutionelles Wissen darüber auf, welche Entscheidungsmuster funktionieren und welche nicht.
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?`,
];
}Die wichtigsten Erkenntnisse
Die Qualität technischer Entscheidungen hängt nicht davon ab, vollständige Informationen zu haben, sondern einen systematischen Prozess für den Umgang mit Unsicherheit. Klassifiziere jede Entscheidung zuerst nach ihrer Reversibilität – die meisten sind Typ 2 und verdienen Stunden der Analyse, nicht Wochen. Schreibe Entscheidungsprotokolle nicht als Dokumentation, sondern als Denkwerkzeug, das Lücken in deiner Argumentation aufdeckt.
Wenn die Zeit knapp ist, wähle die reversibelste Option und dokumentiere sie. Wenn die Analyse ins Stocken gerät, führe einen zeitlich begrenzten Spike durch, um echte Daten zu gewinnen. Der zweitägige Prototyp, der Messwerte liefert, schlägt die zweiwöchige Debatte, die nur Meinungen liefert.
Die Entscheidung selbst macht nur die Hälfte des Werts aus. Die Rückschau – sechs Monate später verfasst, mit echten Produktionsdaten – ist der Ort, an dem sich institutionelles Wissen ansammelt. Teams, die vergangene Entscheidungen systematisch überprüfen, treffen bessere künftige Entscheidungen – nicht weil sie sich an jedes Detail erinnern, sondern weil sie ein kalibriertes Gespür dafür entwickeln, welche Denkmuster zu guten Ergebnissen führen.


