Wie man eine große Codebasis leichter verständlich macht


104

Angenommen, ich entwickle ein relativ großes Projekt. Ich habe bereits alle meine Klassen und Funktionen mit Doxygen dokumentiert, aber ich hatte die Idee, auf jede Quellcodedatei einen "Programmierer-Hinweis" zu schreiben.

Die Idee dahinter ist, in Laienbegriffen zu erklären, wie eine bestimmte Klasse funktioniert (und nicht nur warum, wie die meisten Kommentare es tun). Mit anderen Worten, um anderen Programmierern eine andere Sicht auf die Funktionsweise einer Klasse zu geben.

Zum Beispiel:

/*
 * PROGRAMMER'S NOTES:
 *
 * As stated in the documentation, the GamepadManager class 
 * reads joystick joystick input using SDL and 'parses' SDL events to
 * Qt signals.
 *
 * Most of the code here is about goofing around the joystick mappings.
 * We want to avoid having different joystick behaviours between
 * operating systems to have a more integrated user experience, since
 * we don't want team members to have a bad surprise while
 * driving their robots with different laptops.
 *
 * Unfortunately, we cannot use SDL's GamepadAPI because the robots
 * are interested in getting the button/axes numbers, not the "A" or
 * "X" button.
 *
 * To get around this issue, we created a INI file for the most common 
 * controllers that maps each joystick button/axis to the "standard" 
 * buttons and axes used by most teams. 
 *
 * We choose to use INI files because we can safely use QSettings
 * to read its values and we don't have to worry about having to use
 * third-party tools to read other formats.
 */

Wäre dies eine gute Möglichkeit, um neuen Programmierern / Mitwirkenden ein umfangreiches Projekt zu erleichtern, damit sie verstehen, wie es funktioniert? Gibt es neben der Beibehaltung eines einheitlichen Codierungsstils und einer einheitlichen Verzeichnisorganisation auch „Standards“ oder Empfehlungen für diese Fälle?


32
Auf keinen Fall. Wenn Ihr Code nicht lesbar ist, hilft die Dokumentation nicht weiter.
Telastyn

35
@jeffo - das Problem ist, dass die Zeit dafür einmalig sein kann. Die Zeit, um den Code lesbar zu halten, vergeht mit der Zeit. Ich war an Orten mit dieser Art von Dokumentation, als das Projekt noch jung war oder als Joe, der Perfektionist, noch im Team war. Es wurde dann aufgegeben und die Kommentare hielten an, nicht mehr genau.
Telastyn

25
Zumindest auf einer höheren Ebene ist eine Beschreibung der Funktionsweise und der Kompromisse eines Projekts in der Architektur von unschätzbarem Wert. Diese Art von Dokument muss von Neulingen gelesen werden, bevor sie eine Codetour unternehmen. Es gibt eine Menge meiner Methodik, die zu radikal ist für Docs-Dude-Bullshit im Netz, und während es wahr ist , dass ein anfänglicher Arch-Doc und ein sich entwickelnder Arch-Doc nicht übereinstimmen, ist eine Prosa-Beschreibung notwendig für jedermann, um schnell eine große, nicht triviale Codebasis zu erfassen. Hier ist ein (schlechtes) Beispiel: zxq9.com/erlmud/html/001-002_architecture.html
zxq9

11
@Telastyn: Das hat nichts damit zu tun, ob der Code lesbar ist oder nicht (und ich hoffe es ist). Das Dokumentieren der Entwurfsgründe ist absolut wichtig.
Leichtigkeitsrennen im Orbit

7
@ Telastyn: Ja, vielleicht. Persönlich würde ich es in einem eigenständigen Dokument schreiben. Aber Kommentarblöcke am Anfang von Quelldateien sind nicht so schlecht.
Leichtigkeitsrennen im Orbit

Antworten:


139

Das ist fantastisch. Ich wünschte, mehr Software-Entwickler hätten sich die Zeit und Mühe genommen, dies zu tun. Es:

  • Stellen Sie in einfachem Englisch fest, was die Klasse tut (dh es ist die Verantwortung),
  • Bietet nützliche Zusatzinformationen zum Code, ohne wörtlich zu wiederholen, was der Code bereits sagt.
  • Beschreibt einige der Entwurfsentscheidungen und warum sie getroffen wurden
  • Hebt einige der Fallstricke hervor, die bei der nächsten Person auftreten können, die Ihren Code liest.

Leider fallen viele Programmierer in das Lager von "wenn Code richtig geschrieben ist, sollte er nicht dokumentiert werden müssen". Nicht wahr. Es gibt viele implizite Beziehungen zwischen Codeklassen, Methoden, Modulen und anderen Artefakten, die sich nicht aus dem Lesen des Codes selbst ergeben.

Ein erfahrener Programmierer kann sorgfältig ein Design mit einer klaren, leicht verständlichen Architektur entwerfen, die ohne Dokumentation offensichtlich ist. Aber wie viele solcher Programme haben Sie tatsächlich gesehen?


15
Und warum der "Mythical Man Month" zu einer sich selbst erfüllenden Prophezeiung wird, hat sich niemand die Zeit genommen, dies alles für den neuen Entwickler zu schreiben, als es noch frisch im Kopf war und das Projekt nicht ins Hintertreffen geriet.
JeffO

3
Ich bin mit jedem Punkt einverstanden, den Sie hier machen. Ich mag den Begriff, den der OP in seinem Beitrag verwendet hat, nicht how a class works. Dies ändert sich mit der Zeit und der Wartung. Mein Team hat das oben genannte jedoch nicht in die Quelle aufgenommen. Wir pflegen ein Wiki mit Entscheidungen und kopieren die Slack-Channel-Diskussion über Entwurfsentscheidungen roh in ein Dokument. Alles ordentlich in Github gemacht (so ist alles an einem Ort).
Martin York

1
Meine einzige Ausgabe wendet dieses global an. Diese Klasse ist komplex genug, mit bestimmten verfügbaren Fallstricken, offensichtlich ist es wirklich nützlich (obwohl Sie sich immer noch mit Comment Rot beschäftigen). Wenn eine Klasse offensichtlicher ist, kann der Kommentar ein bisschen überflüssig sein.
Deworde

1
"Ein erfahrener Programmierer kann sorgfältig ein Design mit einer klaren, leicht verständlichen Architektur entwerfen, die ohne Dokumentation offensichtlich ist. Aber wie viele Programme wie dieses haben Sie tatsächlich gesehen?" der Code. Gut strukturierter Code verfügt in der Regel über eine gute, wenn auch sinnlose Dokumentation. Schlecht strukturierter Code enthält Kommentare wie "Inkrementiere x um 1"
Deworde

3
Ich stimme dieser Antwort absolut zu, und wenn ich so etwas wie das OP-Beispiel im Code finden würde, wäre ich einfach so glücklich. Nur eine Ergänzung: Erwägen Sie , Ihrem Kommentar ein Datum hinzuzufügen , um eventuellen Lesern einen Hinweis auf die Aktualität der Beschreibung zu geben, und aktualisieren Sie sie jedes Mal, wenn Sie den Text aktualisieren.
Svalorzen

36

Der Schlüssel zum Arbeiten mit einer großen Codebasis besteht darin, nicht die gesamte Codebasis zu lesen, um Änderungen vorzunehmen. Damit ein Programmierer den gesuchten Code schnell finden kann, sollte der Code organisiert und die Organisation ersichtlich sein. Das heißt, jede logische Einheit im Code, von der ausführbaren Datei, der Bibliothek, dem Namespace bis zur einzelnen Klasse, sollte eine klar erkennbare Verantwortung haben. Ich würde daher nicht nur Quelldateien dokumentieren, sondern auch die Verzeichnisse, in denen sie sich befinden.

Die Notizen Ihres Programmierers enthalten auch Hintergrundinformationen zu Entwurfsentscheidungen. Obwohl dies wertvolle Informationen sein können, würde ich sie von der Verantwortlichkeitserklärung trennen (damit der Leser entscheiden kann, ob er über die Verantwortlichkeit der Klasse oder ihre Entwurfsgründe lesen möchte) und sie so nah wie möglich an die von ihr beschriebene Quelle bringen um die Wahrscheinlichkeit zu maximieren, dass die Dokumentation aktualisiert wird, wenn der Code vorhanden ist (Dokumentation ist nur dann nützlich, wenn wir auf ihre Richtigkeit vertrauen können - veraltete Dokumentation kann schlechter als keine sein!).

Die Dokumentation sollte jedoch trocken bleiben, dh keine Informationen wiederholen, die im Code hätten ausgedrückt werden können oder bereits an anderer Stelle beschrieben wurden (Ausdrücke wie "wie in der Dokumentation angegeben" sind ein Warnzeichen). Insbesondere zukünftige Betreuer beherrschen die Programmiersprache des Projekts nur so gut wie Englisch. Das Paraphrasieren der Implementierung in Kommentaren (die ich insgesamt zu oft sehe, wenn Leute stolz auf ihre Dokumentation sind) hat keinen Nutzen und weicht wahrscheinlich von der Implementierung ab, insbesondere wenn die Dokumentation nicht in der Nähe des darin beschriebenen Codes ist.

Schließlich sollte die Struktur der Dokumentation projektübergreifend standardisiert werden, damit jeder sie finden kann (es handelt sich um ein königliches Durcheinander von Peter-Dokumenten im Bug-Tracker, Sue im Wiki, Alan in der Readme und John im Quellcode ...). .


Dein erster Satz ist genau so, wie ich das sehe. Große Codebasen sollten aus mehreren kleineren Komponenten bestehen, bei denen ein neuer Programmierer eine zuverlässig ändern kann, ohne die anderen zu gefährden.
Jon Chesterfield

1
Bewegen Sie es so nah wie möglich an die Quelle, die es beschreibt, um die Chance zu maximieren, dass die Dokumentation aktualisiert wird, wenn der Code vorhanden ist . Das ist eine wertvolle Erfahrung.
laike9m

DRY als Leitfaden für die Dokumentation ist ein sehr guter Punkt! Das stellt den Fokus automatisch richtig und verbietet die bekanntermaßen widerwärtigen "// Inkrementiere x um 1" -Kommentare.
Hans-Peter Störr

13

Ich würde nicht zustimmen, dass dies ein sehr guter Ansatz ist, hauptsächlich aufgrund von

  1. Wenn Sie Ihr Projekt umgestalten, verschieben Sie Methoden, die Dokumentation bricht ab.

  2. Wenn die Dokumentation nicht ordnungsgemäß aktualisiert wird, ist dies eher verwirrend als hilfreich für das Verständnis des Codes.

Wenn Sie Komponententests für jede Methode / Integrationstests für jedes Modul haben, ist dies eine Selbstdokumentation, die im Vergleich zu Codekommentaren leichter zu pflegen und zu verstehen ist.

Ja, eine richtige Verzeichnisstruktur wird definitiv helfen.


+1 für Tests ist der beste Weg, um eine Codebasis zu verstehen. Unit-Tests, Integrationstests und Abnahmetests beschreiben, wie die Anwendung funktionieren und wie sie verwendet werden soll.
BZink

7

Ich persönlich bin ein Fan eines High-Level-Designdokuments - vorzugsweise VOR jedem Code geschrieben -, das einen Überblick über das Design und eine Liste der Klassen und Ressourcen gibt. Ein Top-Down-Design vereinfacht die Dinge erheblich. Möglicherweise ist dies "Game Engine -> Hardware -> Controller -> Joystick". Daher wusste ein neuer Programmierer, der sagte "Fix the a" -Taste auf dem XYZ-Controller ", zumindest, wo er anfangen sollte zu suchen.

Zu viele moderne Sprachen neigen dazu, Code in hunderte winzige Dateien zu zerlegen. Daher kann es selbst bei moderaten Projekten eine Herausforderung sein, die richtige Datei zu finden.


16
Vor 20 Jahren befand sich mein gesamter Code in einer großen Datei. Jetzt ist es in Tausenden von kleinen Dateien und Testdateien. Es gibt einen guten Grund dafür und er spiegelt 20 Jahre Softwareentwicklung wider (das allgemeine Ökosystem, nicht mein Wissen). Warte aber zu lange für einen Kommentar.
Michael Durrant

4
ah, die alte Wasserfallmethode, eine einzige, alles umfassende, unveränderliche Wahrheit zu schreiben, bevor das Codieren überhaupt beginnt, und es unmöglich zu machen, in der Implementierung von dieser Wahrheit abzuweichen.
2.

2
@jwenting: Du musst es nicht so weit bringen. Aber es ist immer noch gut haben einige Ideen , was Sie bauen.
Robert Harvey

1
Ohne die Einschränkung, wie man dies richtig aufschlüsselt und wo man die Regeln bricht, werden Sie sehr schnell ein Dokument haben, das entweder veraltet oder ein Mühlstein ist. "Ich muss eine neue Klasse hinzufügen; zu Documanto, dem Behemoth, der Zeit frisst!"
Deworde

2
@deworde: Ich habe das als "zu faul, um Dokumentation zu pflegen" gelesen.
Robert Harvey

6

Wenn die Codebasis groß ist, versuche ich, ein Designdokument bereitzustellen , das die Schlüsselelemente des Designs und der Implementierung aufzeigt . Die Absicht hier ist, keine der verwendeten Klassen zu beschreiben, sondern einen Schlüssel zum Code und den Gedanken bereitzustellen, die in das Design eingeflossen sind. Es gibt dem System, seinen Komponenten und seiner Anwendung einen übergreifenden Kontext.

Dinge, die in das Designdokument aufgenommen werden müssen, sind:

  • Anwendungsarchitektur
  • Logische Codestruktur
  • Datenflüsse
  • Verwendete Schlüsselmuster und die Motivation für ihre Verwendung
  • Quelltextstruktur
  • Wie man es erstellt (dies bietet einen Einblick in implizite Abhängigkeiten und die physikalische Quelltextstruktur)

Darauf aufbauend sollte die Dokumentation für die Klassen und Funktionen / Methoden entsprechend ergänzt werden . Insbesondere die öffentliche API; es sollte klar sein, was die folgenden in jedem Fall sind;

  • Voraussetzungen
  • Auswirkungen
  • Invarianten
  • Ausnahmebedingungen (Würfe)

+1 Besser als jede Klasse zu beschreiben, da dies viel schneller veraltet als ein Gesamtentwurf.
Lode

4

Die wichtigste Regel, die ich gefunden habe, um neuen Entwicklern das Verständnis einer Codebasis zu erleichtern, ist, dass perfekte Übereinstimmung teuer ist.

Wenn neue Entwickler das System, an dem sie arbeiten, perfekt verstehen müssen, werden alle Gelegenheiten zum Lernen am Arbeitsplatz verhindert. Ich denke, die Notizen des Programmierers sind ein ausgezeichneter Anfang, aber ich würde noch weiter gehen. Versuchen Sie, Code zu schreiben, der es einem Entwickler ermöglicht, im Handumdrehen herauszufinden, was er tut, anstatt dass er es erst lernen muss, bevor er es tut. Kleine Dinge wie Asserts für Fälle, von denen Sie wissen, dass sie niemals auftreten können, sowie Kommentare, die erklären, warum die Assertion gültig ist, sind weit verbreitet. Das gleiche gilt für das Schreiben von Code, der ordnungsgemäß fehlschlägt, anstatt Fehler zu verursachen, wenn Sie etwas falsch machen.


Meine Regel ist, dass Kommentare über WARUM , nicht WIE sein sollten . Der Code beschreibt, wie.
User11393

3

Ich habe große Klassen mit Dokumentation gesehen, und nachdem ich die Dokumentation gelesen habe, habe ich keine Ahnung, wofür diese Klasse gut sein soll und warum jemand sie verwenden würde! Gleichzeitig brauchte ich einige Funktionen, und ich war mir absolut sicher, dass es eine Klasse geben musste, die damit umgehen konnte, und konnte sie nirgendwo finden - da es keine Dokumentation gab, die mich von dem, was ich brauchte, zur Klasse führte mach es.

Das erste, was ich in der Dokumentation sehen möchte, sind nur ein paar Sätze, was eine Klasse macht und warum ich sie verwenden möchte. Die Kommentare in der ursprünglichen Frage sind in dieser Hinsicht recht gut. Wenn ich nach dem Lesen dieser Kommentare einen Joystick hätte, der nicht gut funktioniert, weil ich die gelieferten Werte nicht interpretieren kann, würde ich wissen, welchen Code ich überprüfen soll.


0

Teilen Sie den Code ähnlich wie bei @meriton in separate Komponenten auf. Zerlegen Sie die Codebasis in separate Pakete (JARs, Gems, Eier usw.), um die Trennung der Komponenten noch deutlicher zu machen. Wenn es einen Fehler gibt, muss ein Entwickler nur das Paket finden , in dem sich der Fehler befindet, und ihn (hoffentlich) nur dort beheben. Ganz zu schweigen davon, dass Unit-Tests einfacher sind und Sie das Abhängigkeitsmanagement nutzen können.

Eine andere Lösung: Verkleinern Sie die Codebasis. Je weniger Code vorhanden ist, desto leichter ist es zu verstehen. Refactor aus nicht verwendetem oder dupliziertem Code. Verwenden Sie deklarative Programmiertechniken . Dies ist natürlich aufwändig und oft nicht möglich oder praktisch. Aber es ist ein würdiges Ziel. Jeff Atwood hat geschrieben: Der beste Code ist überhaupt kein Code


-1

Bei komplexen Systemen kann es sich lohnen, nicht nur jede Datei zu dokumentieren, sondern auch deren Interaktionen und Hierarchien sowie die Struktur und den Grund für das Programm.

Zum Beispiel ist eine Game-Engine normalerweise recht komplex und es ist schwer zu entscheiden, was nach hundert Abstraktionsebenen als was bezeichnet wird. Es kann sich lohnen, eine Datei wie "Architecture.txt" zu erstellen, um zu erklären, wie und warum der Code so aufgebaut ist und warum es diese sinnlos aussehende Abstraktionsschicht gibt.


-7

Dies kann zum Teil daran liegen, dass es für einen einzelnen Programmierer schwierig ist, es zu schreiben, da jeder einzelne nur seinen Teil des Projekts versteht.

Manchmal können Sie diese Informationen aus den Notizen des Projektmanagers abrufen, aber das ist alles, was Sie erhalten, da sie ihre Notizen selten in diesem Format umschreiben.


7
Wenn Sie sich github ansehen, werden Sie in einer README.md-Datei viele Projekte finden, die diese Art von Hinweis enthalten. Es ist so sehr ein Teil der Kultur von Git im Allgemeinen geworden und JavaScript-Projekte im Besonderen werden von den meisten Menschen nicht mehr in einer Bibliothek verwendet, die nicht über diese Art von High-Level-Dokumentation verfügt. Es ist also falsch, dass "kein Programmierer es schreiben würde", da Sie sich nur etwas wie jQuery oder socket.io ansehen und Programmierer finden müssen, die solche Dinge schreiben. Es ist auch zu einer Kultur geworden, dass README-Dateien, die nicht genau sind, Fehlerberichte generieren.
Slebetman

1
Dies scheint die Frage nicht zu beantworten, die nach Gründen suchte, warum der vorgeschlagene Dokumentationsstil funktionieren würde oder nicht, und auch nach Dokumentationsstandards.
user52889

5
Wenn Sie ein Team von Programmierern haben, die an einem Produkt arbeiten, und jeder Programmierer nur den Code versteht, an dem er gearbeitet hat, ist Ihr Team nicht nur unglaublich dysfunktional mit einem absurden Busfaktor, sondern man stellt auch die Qualität des Codes in Frage. Wie kann man Code in ein Produkt integrieren, ohne den Rest des Codes im selben System zu verstehen?!?
Leichtigkeitsrennen im Orbit
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.