Partielles Verständnis ist kein Fehler: Warum du deine Codebase nicht komplett verstehen musst
Inhaltsverzeichnis
Dieser Beitrag basiert auf dem Artikel „In defense of not understanding your codebase” von Sean Goedecke. Den Originalartikel findest du unter seangoedecke.com.
Wer mit kleinen Codebases in stabilen Teams arbeitet, hält vollständiges Systemverständnis für selbstverständlich. Wer an großen Systemen mit hoher Fluktuation arbeitet, weiß: Das ist eine Fantasie. Sean Goedecke beschreibt zwei grundlegend verschiedene Programmierkulturen und erklärt, warum die zweite Gruppe in Online-Diskussionen systematisch unterrepräsentiert ist.
Key Takeaways (TL;DR)
- Zwei Kulturen: Kleine Codebases fordern vollständiges Verständnis. Große Systeme machen das unmöglich. Beide Gruppen programmieren mit unterschiedlichen Methoden und Werten.
- Naurs Limit: Peter Naurs „Programming as Theory Building” funktioniert für Programme mit 200.000 Zeilen. Für Systeme mit Millionen Zeilen Code ist die Empfehlung „Code komplett neu schreiben” nicht anwendbar.
- Rewrites scheitern bei Größe: Erfolgreiche Rewrites isolieren einzelne Komponenten und ersetzen sie schrittweise. Wer das bestehende System nicht ändern kann, kann es auch nicht ersetzen.
- Verlassene Systeme werden wiederbelebt: Teams übernehmen regelmäßig Codebases ohne verbleibende Expertise. Das funktioniert, indem ein Flow End-to-End verstanden wird und von dort aus nach außen expandiert wird.
- Handeln unter Unsicherheit: In großen Systemen operiert jeder mit einer unvollständigen Theorie. Die entscheidende Fähigkeit ist, trotzdem fundierte Entscheidungen zu treffen.
Zwei Programmierkulturen, die aneinander vorbeireden
Goedecke beobachtet eine Spaltung, die selten explizit benannt wird. Wer an Redis oder an einem Indie-Game wie The Witness arbeitet, sagt: Selbstverständlich muss man die eigene Codebase komplett verstehen. Alles andere ist unsauber. Wer am Google-Search-Backend oder an GitHub arbeitet, sagt: Das ist physisch unmöglich. Man macht das Beste aus dem eigenen lokalen Verständnis.
Beide Gruppen haben valide Methoden und Praktiken. Das Problem ist die Dominanz der ersten Gruppe in der öffentlichen Diskussion. Open-Source-Entwickler bloggen häufiger, ihre Arbeit ist technisch beeindruckender (weil Koordinationsprobleme in großen proprietären Systemen die eigentliche Herausforderung sind), und über interne Systeme lässt sich aus rechtlichen Gründen nichts schreiben. Selbst wenn man es dürfte: Das Erklären großer Systeme erfordert so viel spezifischen Kontext, dass kein Blogbeitrag das transportieren kann.
Das Ergebnis ist eine verzerrte Wahrnehmung. Die Kultur des vollständigen Verständnisses dominiert die Diskussion, obwohl die Mehrheit der professionellen Softwareentwicklung in der Kultur des partiellen Verständnisses stattfindet.
Naurs „Programming as Theory Building” und seine Grenzen
Peter Naurs Paper von 1985 ist die stärkste Formulierung der Position „vollständiges Verständnis ist essenziell”. Der Kernpunkt: Wenn Programmierer an einem Programm arbeiten, ist der Code nur ein Nebenprodukt. Das eigentliche Produkt ist ihre „Theorie des Programms”, also das intuitive Verständnis davon, was passiert und warum. Code und Dokumentation können diese Theorie nur teilweise erfassen.
Soweit ist das schlüssig. Wenn ein Team 100 Prozent Fluktuation erlebt, wird das neue Team tatsächlich Schwierigkeiten haben, den Code zu verstehen. Aber Naur geht einen Schritt weiter, der in der Praxis nicht hält. Er empfiehlt, den bestehenden Code komplett zu verwerfen und das Problem von Grund auf neu zu lösen:
„Die Rekonstruktion der Theorie eines Programms allein aus der Dokumentation ist strikt unmöglich. Daher sollte der vorhandene Programmcode verworfen und dem neu gebildeten Programmierteam die Möglichkeit gegeben werden, das Problem von Grund auf neu zu lösen.”
Goedecke widerspricht dem aus zwei konkreten Gründen.
Warum große Systeme nicht neu geschrieben werden können
Ausreichend große Systeme mit echten Nutzern enthalten tausende Sonderfälle und Eigenheiten, die sich nicht replizieren lassen. Selbst ein Team, das das System vollständig kennt, könnte es nicht von Grund auf nachbauen. Es gibt schlicht zu viele Details gleichzeitig im Kopf zu halten.
Erfolgreiche Rewrites funktionieren anders. Sie beginnen damit, die bestehende Codebase in kleine, isolierte Teile zu zerlegen und dann jeweils einen Teil neu zu schreiben. Ein Rewrite ist also eine Serie von Änderungen am alten System. Wer das alte System nicht ändern kann, kann es erst recht nicht ersetzen.
Verlassene Systeme werden regelmäßig wiederbelebt
Naur schreibt, dass die Notwendigkeit, ein „völlig totes” Programm wiederzubeleben, selten auftreten wird. In Unternehmen mit hunderten Millionen Zeilen Code und tausenden Entwicklern passiert genau das ständig. Es braucht nur ein paar Kündigungen zum falschen Zeitpunkt oder ein Jahr ohne Wartung, und niemand versteht den Code mehr.
Goedecke beschreibt aus eigener Erfahrung, wie er verlassene Codebases übernommen, sich eingearbeitet und produktiv damit gearbeitet hat. Die Methode ist immer dieselbe: Einen einzelnen Flow End-to-End verstehen, dann schrittweise von dort nach außen arbeiten und dabei vorsichtige Änderungen vornehmen. Das dauert, aber es funktioniert. Naurs These, dass die Rekonstruktion einer Theorie aus dem Code unmöglich sei, widerspricht der täglichen Praxis.
Jeder operiert mit einer falschen Theorie
In ausreichend großen Codebases hat jeder eine inkorrekte Theorie des Programms. Niemand versteht alles. Kein einzelner Entwickler und kein ganzes Team. Das definierende Merkmal moderner Softwaresysteme ist, dass sie zu groß sind, um komplett im Kopf gehalten zu werden.
Effektiv arbeiten bedeutet deshalb, mit einer nur teilweise korrekten Theorie zu arbeiten. Wer sich unsicher ist, kann nicht zurücklehnen und auf jemanden mit perfektem Verständnis warten. Wenn du ein kompetenter Engineer bist, bist du diese Person. Die Fähigkeit, auf Basis einer fundierten Einschätzung zu handeln und die Konsequenzen zu tragen, ist die zentrale Kompetenz in großen Systemen.
Das ist auch der Zusammenhang zur Cognitive Debt, die wir in einem früheren Beitrag analysiert haben. Teams produzieren mehr Code, als sie verstehen können. Die Lücke zwischen Output und Verständnis wächst. In Naurs Terminologie: Die Theorie des Programms erodiert, während das Programm selbst wächst.
Der historische Kontext: 1985 vs. heute
Goedecke ist fair gegenüber Naur. 1985 war die durchschnittliche Programmgröße vermutlich um mehrere Größenordnungen kleiner als heute. Naurs Beispiele für „große Programme” sind ein industrielles Überwachungssystem mit 200.000 Zeilen und ein Compiler. 1987 hatte GCC etwa hunderttausend Zeilen Code. 2015 waren es über vierzehn Millionen.
Ein Rewrite von ein oder zwei hunderttausend Zeilen ist machbar, besonders mit bestehenden Tests. Ein Rewrite von ein oder zwei Millionen Zeilen ist es nicht. Naurs Empfehlung war für seine Zeit möglicherweise sinnvoll. Für die Realität heutiger Softwaresysteme ist sie es nicht mehr.
Theory Building als Trade-off
LLMs werden oft als schädlich für den Aufbau einer mentalen Theorie des Codes kritisiert. Goedecke sieht das differenzierter. LLMs machen es schwieriger, eine detaillierte mentale Theorie aufzubauen. Gleichzeitig ermöglichen sie, eine partielle Theorie schnell zu konstruieren und effektiver zu nutzen. Ob der Nettoeffekt positiv oder negativ ist, bleibt eine offene Frage.
Unabhängig von LLMs ist die Behauptung, alles, was das Codeverständnis stört, sei schlecht, offensichtlich zu einfach. Goedecke listet weitere Faktoren auf, die das Aufrechterhalten einer Theorie erschweren:
- Andere Menschen dürfen Code in deiner Codebase schreiben
- Gesetzlich vorgeschriebene Features wie Barrierefreiheit und Datenschutz müssen umgesetzt werden
- Kollegen dürfen kündigen oder das Team wechseln
- Sicherheitsupdates für Abhängigkeiten müssen eingespielt werden
- Externe Libraries und Dependencies werden eingebunden
Jeder dieser Faktoren macht es schwerer, die Theorie der Codebase aufrechtzuerhalten. Trotzdem würde niemand ernsthaft fordern, sie zu eliminieren. Das Aufrechterhalten einer Theorie ist ein Wert unter vielen. Manchmal ist es der wichtigste. Manchmal wird er gegen Geschwindigkeit, regulatorische Anforderungen oder organisatorische Realitäten eingetauscht.
Die Präferenz für „reines” Engineering
Fast alle Engineers bevorzugen es, ein akkurates mentales Modell ihrer Software zu haben. Es macht mehr Spaß, ist weniger stressig und fühlt sich an wie „echtes Engineering”. Deshalb arbeiten viele Engineers in ihrer Freizeit an Open-Source-Projekten: um an kleinen Codebases allein zu arbeiten, wo eine vollständige Naur-Theorie möglich ist.
Daran ist nichts falsch. Aber im Job wirst du dafür bezahlt, eine Aufgabe zu erfüllen. Du übernimmst die Engineering-Werte deines Arbeitgebers. Wie sehr du persönlich auch Wert auf Performance legen magst, manchmal musst du langsamen Code schreiben, um einen Termin zu halten oder eine unbequeme Anforderung umzusetzen. Das Aufrechterhalten einer Theorie der Codebase ist derselbe Trade-off.
Das knüpft direkt an die Beobachtung an, die wir bei der Analyse von KI und den schweren Teilen des Software Engineerings gemacht haben. KI eliminiert die Routinearbeit. Was übrig bleibt, ist Entscheidungen unter Unsicherheit zu treffen. Partielles Verständnis ist keine Schwäche. Es ist die Arbeitsbedingung.
Checkliste: Effektiv arbeiten mit partiellem Verständnis
- Einen Flow End-to-End verstehen, bevor du expandierst. Versuche nicht, ein ganzes System auf einmal zu erfassen. Wähle einen konkreten Pfad durch das System, verstehe ihn vollständig und arbeite von dort nach außen.
- Änderungen als Lernmethode nutzen. Kleine, gezielte Änderungen am Code sind der schnellste Weg, eine Theorie aufzubauen. Lesen allein reicht nicht. Verändern und beobachten, was passiert, erzeugt tieferes Verständnis.
- Entscheidungen treffen, nicht aufschieben. In großen Systemen wartet niemand mit perfektem Verständnis. Triff eine fundierte Einschätzung, dokumentiere die Annahmen (z.B. in einem ADR) und handle. Korrigiere, wenn nötig.
- Lokales Reasoning vor globalem Verständnis. Investiere in Architekturmuster, die lokales Reasoning ermöglichen: klare Interfaces, geringe Kopplung, explizite Abhängigkeiten. Wer lokal korrekt arbeiten kann, braucht kein globales Verständnis.
- Theory Building als bewussten Trade-off behandeln. Codeverständnis ist ein Wert unter vielen. Manchmal ist es der wichtigste. Manchmal nicht. Die Entscheidung, welcher Wert gerade Vorrang hat, ist eine Engineering-Entscheidung, keine moralische.
Wie geht dein Team mit dem Trade-off zwischen Systemverständnis und Liefergeschwindigkeit um, und gibt es einen bewussten Prozess dafür?
Häufig gestellte Fragen (FAQ)
Was ist 'Programming as Theory Building' von Peter Naur? ↓
Peter Naurs Paper von 1985 argumentiert, dass beim Programmieren nicht der Code das eigentliche Produkt ist, sondern die mentale Theorie des Programms. Diese Theorie umfasst das intuitive Verständnis darüber, was der Code tut und warum. Geht dieses Verständnis verloren, etwa durch Teamwechsel, lässt sich der Code laut Naur nur noch schwer sinnvoll ändern.
Warum funktioniert Naurs Empfehlung, Code komplett neu zu schreiben, in der Praxis nicht? ↓
Große Systeme mit Millionen Zeilen Code enthalten tausende Sonderfälle und Geschäftslogik, die nicht replizierbar sind. Erfolgreiche Rewrites beginnen immer mit der Isolierung einzelner Komponenten und deren schrittweiser Neuentwicklung. Ein kompletter Neustart ist bei ausreichender Systemgröße technisch und organisatorisch unmöglich.
Wie arbeitet man effektiv mit einer Codebase, die man nicht vollständig versteht? ↓
Einen einzelnen Flow End-to-End verstehen, von dort schrittweise nach außen arbeiten und dabei gezielte Änderungen vornehmen. Das erfordert die Bereitschaft, auf Basis einer fundierten Einschätzung zu handeln, auch wenn das vollständige Bild fehlt. In großen Systemen operiert jeder mit einer unvollständigen Theorie des Programms.
Senior Full-Stack Developer mit Fokus auf stabiler Software-Architektur, pragmatischem Engineering und der Realität von KI im Entwickler-Alltag. Seine Wurzeln liegen im praktischen Lösen komplexer Probleme unter realen Bedingungen.
github.com/hyretic-devÄhnliche Artikel
Cognitive Debt ist die neue technische Schuld: Was CTOs beim CTO Craft Dinner wirklich diskutieren
Engineering ManagementBeim CTO Craft Dinner in Toronto diskutierten Engineering-Leader über die Realität der KI-Adoption. Das Ergebnis: Die Phase des freien Experimentierens ist vorbei. Was bleibt, sind unkontrollierte Kosten, fehlende Karrierepfade und eine neue Art von Schulden, die niemand misst.
Fundamente statt Tools. Warum dein Klavierunterricht wichtiger ist als das neueste KI-Tool.
KICS-Professor Tom Yeh lehrt KI per Hand auf Papier. Seine These: Wer zeitlose Fundamente besitzt, übersteht jeden Technologiewechsel. Wer nur Tools lernt, muss jedes Mal von vorne anfangen.
KI hat Software Engineering nicht einfacher gemacht. Sie hat die schweren Teile schwerer gemacht.
KIKI-Tools eliminieren die Routinearbeit. Was übrig bleibt, ist der anspruchsvollste Teil des Jobs. Und der menschliche Kopf hat kein Upgrade bekommen.
Vibe-Coding ohne Architektur: Warum 1.690 Zeilen Code im Müll gelandet sind
Agentisches CodingEin Entwickler hat sieben Monate lang ein Kubernetes-Tool rein mit KI gebaut. Das Ergebnis ist ein Lehrstück über die Grenzen von Vibe-Coding ohne menschliche Architekturkontrolle.