Luca Mele
Luca Mele

Engineering

Warum YAGNI besser ist als DRY (und warum DRY missverstanden wird)

Warum YAGNI besser ist als DRY (und warum DRY missverstanden wird)
Zurück zu allen Beiträgen
·8 Min. Lesezeit

Jeder Entwickler hat es gehört: „Don't Repeat Yourself." Es ist eines der ersten Prinzipien, die wir lernen, und es klingt so vernünftig, dass wir es selten hinterfragen. Doppelten Code gesehen? Extrahieren. Ein Muster erkannt? Abstrahieren. Drei ähnliche Zeilen? Zeit für eine Hilfsfunktion.

Aber nach 16 Jahren Softwareentwicklung in verschiedenen Branchen und Ländern — von E-Learning-Plattformen in Rom bis zu Finanz-Trading-Systemen in Zürich — bin ich zu einem anderen Schluss gekommen: YAGNI — „You Aren't Gonna Need It" — ist das wertvollere Prinzip, und blindes Befolgen von DRY schadet Codebasen aktiv.

Die DRY-Falle

DRY wurde von Andy Hunt und Dave Thomas in „The Pragmatic Programmer" eingeführt. Ihre ursprüngliche Definition bezog sich auf Wissen: „Every piece of knowledge must have a single, unambiguous, authoritative representation within a system." Beachte das Wort: Wissen, nicht Code.

Was die meisten Entwickler praktizieren, ist nicht DRY — es ist „Don't Repeat Code", und das ist etwas grundlegend anderes. Zwei Codestücke können heute identisch aussehen, aber völlig unterschiedliche Geschäftskonzepte repräsentieren, die sich morgen auseinanderentwickeln.

Ich habe das oft erlebt: Validierungslogik für zwei verschiedene Abläufe sieht zunächst identisch aus. Ein gut meinender Entwickler führt sie in einer gemeinsamen Hilfsfunktion zusammen. Monate später erfordern regulatorische Änderungen oder Produktentscheidungen unterschiedliche Regeln für jeden Ablauf, und das Entwirren dieser gemeinsamen Funktion bedeutet, jeden Aufrufer anzupassen, Tests neu zu schreiben und Deployments teamübergreifend zu koordinieren.

// These look the same, but they're NOT the same knowledge
function validateUserEmail(email: string) {
  return email.includes('@') && email.length > 5;
}

function validateNewsletterEmail(email: string) {
  return email.includes('@') && email.length > 5;
}

// Merging them into validateEmail() creates coupling
// between user registration and newsletter signup.
// When newsletter needs to accept '+' aliases but
// registration doesn't, you'll fight the abstraction.

Warum YAGNI gewinnt

YAGNI sagt: Bau es nicht, bis du es brauchst. Abstrahiere nicht, bis du das echte Muster gesehen hast. Verallgemeinere nicht, bis du die spezifischen Fälle verstehst. Das ist keine Faulheit — es ist Disziplin.

Die Kosten einer falschen Abstraktion sind viel höher als die Kosten von Duplikation. Duplikation ist offensichtlich und lokal — man sieht sie, und die Behebung ist unkompliziert. Eine falsche Abstraktion ist subtil und viral — sie prägt, wie Menschen über den Code denken, und das Rückgängigmachen bedeutet, jeden Aufrufer zu refaktorieren.

Ich folge der „Rule of Three": Abstrahiere nicht, bis du drei wirklich unabhängige Verwendungen gesehen hast. Und selbst dann frage dich, ob die Duplikation dasselbe Wissen oder nur ähnlich aussehenden Code repräsentiert.

Bei Migros, als wir Bikeworld und Micasa auf einem gemeinsamen Next.js-Monorepo neu aufbauten, war die Versuchung enorm, alles zwischen den Shops zu teilen. Aber Produktseiten für Velos und Möbel haben grundlegend unterschiedliche User Journeys. Wir hielten Komponenten getrennt, bis sich echte gemeinsame Muster organisch zeigten — und das Ergebnis war eine Codebasis, in der jeder Shop sich unabhängig weiterentwickeln konnte, ohne Releases zu koordinieren.

Die wahren Kosten voreiliger Abstraktion

Ich habe dieses Muster dutzende Male in Teams erlebt, die ich geführt habe — von kleinen Agenturen in Italien bis zu Enterprise-Teams bei AXA und UBS. Jemand erstellt früh eine „gemeinsame" Komponente oder Hilfsfunktion. Sie funktioniert grossartig für zwei Anwendungsfälle. Dann kommt ein dritter, der fast passt, aber ein Flag braucht. Dann ein vierter, der ein weiteres Flag braucht. Bald hat man eine Funktion mit sechs Boolean-Parametern, und niemand kann nachvollziehen, was sie eigentlich tut.

Ich nenne das „Abstraktionsschulden". Anders als technische Schulden, die in Build-Zeiten und Abhängigkeitslisten sichtbar sind, verstecken sich Abstraktionsschulden in der Velocity des Teams. Entwickler werden langsamer, weil sie Angst haben, gemeinsamen Code zu ändern. Neue Features dauern länger, weil sie um Abstraktionen herum arbeiten müssen, die für andere Anwendungsfälle entworfen wurden.

// The abstraction graveyard — we've all seen this
function formatData(
  data: unknown,
  isUser?: boolean,
  isAdmin?: boolean,
  includeMetadata?: boolean,
  skipValidation?: boolean,
  legacyMode?: boolean
) {
  // 200 lines of branching logic
  // that nobody dares to touch
}

YAGNI in der Praxis

So wende ich YAGNI in täglichen Entscheidungen an:

Beim Aufbau der Vontobel-Markets-Plattform brauchten wir Echtzeit-Kursaktualisierungen über WebSockets. Der erste Instinkt war, eine generische Echtzeit-Datenschicht zu bauen, die jeden zukünftigen Streaming-Anwendungsfall abdecken könnte. Stattdessen bauten wir genau das, was der Kursticker brauchte — nicht mehr. Als ein zweites Echtzeit-Feature kam (Portfolio-Benachrichtigungen), verstanden wir beide Anwendungsfälle gut genug, um etwas zu entwerfen, das beiden genuinely diente, statt im Voraus zu raten.

Bei UBS, als wir die Anwendung der Administrative Officers vereinheitlichten, riet ich aktiv davon ab, ein „universelles Formular-Framework" zu erstellen, das jedes Formular im System abdeckt. Stattdessen baute jedes Team seine Formulare nach eigenen Bedürfnissen, und wir identifizierten wirklich gemeinsame Muster erst, nachdem mehrere Teams ausgeliefert hatten. Die Muster, die wir schliesslich extrahierten, waren einfacher und robuster als alles, was wir im Voraus hätten entwerfen können.

Was ich meinen Teams sage

Wenn ich Entwickler einarbeite, teile ich dieses mentale Modell: Duplikation ist weit günstiger als die falsche Abstraktion. Kopiere diese drei Zeilen. Lass das Muster natürlich entstehen. Wenn du dasselbe Konzept — nicht denselben Code, dasselbe Konzept — drei oder mehr Male gesehen hast, dann überlege, ob eine Abstraktion verdeutlichen oder verschleiern würde.

„Make sense" ist eines meiner Kernprinzipien. Jede Entscheidung sollte im Kontext Sinn ergeben, nicht nur einer Regel folgen. DRY ergibt Sinn, wenn du wirklich gemeinsames Wissen kodierst. YAGNI ergibt fast überall sonst Sinn.

Der beste Code, den ich ausgeliefert habe, war nicht der cleverste oder DRY-este. Es war der Code, bei dem jedes Stück klar seine Absicht ausdrückte, bei dem man eine Sache ändern konnte, ohne sich Sorgen zu machen, fünf andere zu brechen, und bei dem neue Teammitglieder ihn ohne Führung verstehen konnten.

Wenn du das nächste Mal nach einer gemeinsamen Hilfsfunktion greifst, halte inne. Frage: „Abstrahiere ich, weil ich ein echtes Muster sehe, oder weil mir gesagt wurde, dass Wiederholung schlecht ist?" Diese Pause ist mehr wert als jedes Design Pattern.