PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Code Qualität schnell beurteilen



RapidMax
01-12-2005, 00:09
Aus Fehler lernt man. Ich habe mir mal die wichtigsten Punkte aufgeschrieben, um in Zukunft schneller schlechen Code zu erkennen. Das hilft mir dabei zu beurteilen, ob bestehender Code angepasst/korriegiert werden muss, oder ob lieber gleich das ganze neu geschrieben wird. Auch für Fälle, wenn entschieden werden muss, ob Code den Preis Wert ist, sollte das hier helfen.

Ich will das niemandem vorenthalten und veröffentliche es hier. Ergänzungen sind gewünscht, Diskussion bitte nicht hier in diesem Forum.

Einleitung

Es kommt häufig vor, dass innerhalb kurzer Zeit ein bestehendes Projekt beurteilt werden muss. Die Zeit für das vollständige Sichten des Quelltext reicht nicht, also muss man sich auf das Wesentliche konzentrieren.

Ich versuche hier anhand eigener, praktische Erfahrungen Tipps zu geben auf was zu achten ist. Natürlich ohne Anspruch auf Vollständigkeit.

Quick Blackbox Tests

Laufzeit-Warnungen

Das einfachste Mittel überhaupt. Das fertige Programm starten und auf die Meldungen in der Konsole achten. Vorsicht, falls diese Meldungen von irgend einem Wrapper nach /dev/null umgeleitet werden! Kommen hier Warnungen von verwendeten Bibliotheken und Frameworks, so ist das als Hinweis auf unsaubere Programmierung zu verstehen. Etwas schwieriger sind eigene Meldungen zu beurteilen, hier müssen Warnungen nicht unbedingt schlecht sein.

Kommen keine Warnungen und Meldungen, einfach mal versuchen die Verbosity des Programms, oder der verwendeten Bibliotheken heraufzusetzen (z.B. über Kommandozeilenparameter oder Enviroment Variablen). Auch nicht vergessen, in die Logfiles zu schauen.

Compiler-Warnings

Das einfachste Mittel überhaupt neben letzterem. Einfach den Übersetzungsvorgang mit eingeschalteten Compiler-Warnings starten und auf die Anzahl der Warnings achten. Typischerweise sollten keine bis wenige Warnungen erscheinen, so als Daumenregel eine pro Modul. Kommentierte Warnungen geben Pluspunkte. (gcc: -Wall)

Code-Quality-Checker

Der nächste Schritt ist die Zuhilfenahme eines Code-Quality-Checker wie lint, splint, pylint, htmltidy etc. Diese sollten im tolerantesten Modus auch gegen Null Warnungen melden.

Memory Leak Checker

Ebenfalls recht gute Aussagen können mit der Zuhilfenahme eines Memory Profilers/Leak Checker gemacht werden (valgrind, memprof, etc.). Hier kann zwar verlorener Speicher vorkommen, was jedoch nicht passieren darf, ist dass bei langer Laufzeit und starker Benutzung immer mehr Speicher verlohren geht.

Projekt-Struktur

Sehr einfach ist auch die Strukturierung des Projekts anhand der Verzeichnisstruktur und Dateinamen zu beurteilen: Sieht es aufgeräumt aus? Ist alles flach in einem Verzeichnis oder im Gegenteil verteilt in hunderten von Unterverzeichnissen? Liegen Leichen herum (modul.c.old, modul.c.goody, modul.c.veryold etc.)? Wurden eigener Code sauber von fremden Modulen getrennt? Wurden Daten/Ressourcen und Code getrennt?

Verbotene Funktionen

In jeder Sprache gibt es typische Funktionen, welcher ein erfahrener Programmieren nie einsetzen würde. In C ist das z.B. strcpy, strcat und sprinf (anstatt des sicheren strncpy, strncat und snprintf). Eine einfache Suche nach diesen Funktionen sollte daher ergebnislos verlaufen.

Build-Enviroment

Dieser Punkt ergibt sich oft von selbst: Kann ich das Build-Enviroment ohne grosse Kunstücke auf einem neuen System installieren? Erfolgt die Anpassung an das eigene System zentral in wenigen Konfigurationsdateien oder Makefiles oder müssen gleiche Angaben an vielen Stellen gemacht werden? Auch hier ist das natürlich vom Projekt abhängig. Ein Cross-Compile Enviroment für Embedded Systeme ist von Natur aus komplex.

Benutzerschnittstelle

Egal ob es sich um eine GUI oder eine Konsolenanwendung handelt, wenn die Oberfläche unübersichtlich, inkonsistent und kompliziert, umständlich zu bedienen ist, dann kann das etwa auch auf den Code projiziert werden.

Direkte Beurteilung des Code

Fehlerbehandlung

Ein von unerfahrenen Programmierer gerne gemachter Fehler ist das Weglassen von Fehlerbehandlung. Im Projekt-Code heisst das, dass die kritischen Funktionen überhaupt Fehler zurückgeben und dass diese Fehler dann auch ausgewertet werden. Besonders wichtig ist die Fehlererkennung bei Systemaufrufen und Bibliotheksfunktionen. Hier ist praktisch immer auf Fehler zu überprüfen, sonst kann man das als sicheren Hinweis auf mangelnde Qualität deuten. Auch die Art der Fehlerbehandlung sollte kritisch angeschaut werden: Normalerweise bricht man das Programm bei unbekannten und nicht behandelten Fehler ab und macht nicht irgendwie weiter.

Stichprobe

Nicht alleine, aber zusammen mit anderen Methoden ist eine Stichprobe nützlich. Man sollte etwa ein einzelnes Modul anschauen. Dabei ist darauf zu achten, dass man eines wählt, welches zentrale Bedeutung hat.

Diese Stichprobe ist dann peinlich genau zu untersuchen: Man sollte die Funktion und den Code wirklich verstehen und sämltliche Pfade gedanklich durchlaufen. Stösst man dabei auf Ungereimtheiten sollte man diesen Teil noch genauer überprüfen. z.B. mit einem Debugger und versuchen Fehler zu provozieren.

Verwendung anerkannter Algorithmen, Datenstrukturen und Patterns

Wenn der Code Algorithmen enthält, dann sollte überall dort, wo ein Standard-Problem gelöst wird, auch die üblichen Algorithmen zum Einsatz kommen. Z.b. Algorithmen aus oder mit Referenz auf Werke wie Knuth's TAOCP geben Pluspunkte. Eigene Algorithmen sollten irgendwo dokumentiert sein, inclusive Diskussion Randbedinungen und Komplexität.

Ähnliches gilt auch für Datenstrukturen wie Bäume und Listen. Hier ist wenn möglich ein Framework zu benutzen und nicht jedesmal das Rad neu zu erfinden. Abzüge geben Arrays fixer länge, wenn die Datenmenge darin dadurch künstlich begrenzt wurde. Selbstverständlich sind hier Bound-Checks.

Die Verwendung von Patterns geben ebenfalls Pluspunkte (Man achte auf Begriffe wie Singleton, Observer, Factory etc.).

Spaghetti-Code

Kann leicht gefunden werden, indem man exemplarisch sich durch eine Funktion und ihre Unterfunktionen hangelt. Meist soll schon klar sein, was eine Funtion macht, wenn man den Namen sieht. Funktionen die dabei noch mehr machen verwirren. Hilfreich ist hier ein Dokumentationswerkzeug, welches graphisch den sogenannten Call-Graph aufzeichnen kann (wie z.B. Doxygen mit graphviz).
Hierzu gehört auch die "Include/Import alles ich könnte es ja mal brauchen"-Mentalität.

Kommentare

Die Kommentare sagen viel aus über die Qualität des Codes. Allerdings sagt bei Kommentaren die Quantität sehr wenig aus. Man soll sich von inhaltsleeren Kommentarhülsen nicht täuschen lassen.

Für jede Funktion, Methode, Klasse und Modul sollte ein Kommentar vorhanden sein, der beschreibt, was das Element macht. Innerhalb von Funktionen/Methoden sollte nur dort Kommentar stehen, wo nicht anhand des Quelltext ersichtlich ist, was es macht. Sehr schlecht sind veraltete Kommentare, die nicht mehr zu dem Code passt. Ebenfalls Aufmerksamkeit erregen sollten standard-Kommentare von Code-Generatoren, vorausgesetzt dieser Code wurde von Hand nachbearbeitet.

Globale Variablen

Diese sind nicht per se schlecht. Aber die Menge sollte überschaubar sein und sie sollten nie verwendet werden um zwischen zwei Funktionen zu kommunizieren.Da das noch zu unklar ist, will ich hier ein Beispiel anbringen: Funktionen sollten dann auf globale Variablen zugreiffen, um z.B. eine Referenz auf die Datenstruktur zu erhalten, aber nicht, um einen Wert von Funktion A zu Funktion B zu übertragen.

Weitere Bewertungsmöglichkeiten

Versionsverwaltung

Zuerst kommt einmal die Frage: "Wird überhaupt eine eingesetzt?". Wenn ja, dann wie wird es benutzt. Wurde z.B. alles unmotiviert eingecheckt (Binaries, Objektdateien, generierte Dateien), wurden sinnvolle Logmeldungen verwendet und findet sich der aktuelle Stand überaupt darin, oder hat jeder Entwickler noch seine eigenen Kopien?

Sehr aussagekräftig sind auch die Logmeldungen an sich.

Kein dezidiertes Versionierungsystem muss nicht schlecht sein, wenn das ganze mit dem normalen Dateisystem gemacht wurde. Bei kleineren Projekten funktioniert das durchaus mit etwas Disziplin.

Dokumentation

Wenn Dokumentation vorhanden ist, sollte darauf geachtet werden, dass es eine Entwicklerdokumentation ist, die sich deutlich von einer Userdokumentation/Installationsanleitung unterscheidet. Quantität sagt auch hier nichts aus, wichtig ist hier die Vermittlung eines "Big Picture", die Vermittlung der Designüberlegungen und eine gewisse Aktualität zum Code.

Fehlerbeschreibungen, Bugtracking Tool

Sofern Zugriff auf Fehlerbeschreibungen oder Bugtracking Tool Tickets besteht, kann anhand dieser sehr viel über den Code ausgesagt werden: Wie viele und wie schwerwiegende Fehler sind eingetragen? Wie viele Fehler konnten gelöst werden? Was war die häufigste Fehlerursache? Gibt man z.B. die Schuld häufig an Bibliotheken wie die libc ab, so muss man wirklich ein spezielles System haben, um in solch gut getesteten Projekt noch Fehler zu finden.