The Art of Software Reviews: Probleme und Risiken zielsicher identifizieren

Seite 4: Code-Reviews

Inhaltsverzeichnis

Mit der Analyse der Softwarearchitektur rein auf Grundlage des vorhandenen Quellcodes ist es leider nicht getan. Es ist ein Irrglaube, dass alle Informationen im Code stehen. Das klingt zunächst kontraintuitiv. Was sollte wichtiger sein als Quellcode? Nachfolgend finden sich ein paar (mögliche) Gründe, warum andere Dinge noch wichtiger sein könnten:

  • Möglicherweise hat man eine hervorragend strukturierte, nach allen Regeln der Kunst durchgeführte Implementierung einer äußerst schlechten Idee vor sich, zum Beispiel weil es für den gleichen Einsatzzweck bessere, fertige Komponenten gibt.
  • Vielleicht würde der ein oder andere problematische oder vielleicht sogar besonders elegante Teil der Software gar nicht benötigt, wenn man eine technische Anforderung hinterfragen würde.
  • Manchmal befindet sich ein großer Teil der wichtigen Information nicht in "klassischem" Quellcode, sondern in Konfigurationsdateien oder "Glue-Code", der auf den ersten Blick gar nicht ins Auge fällt.
  • Vielleicht ist die Laufzeitarchitektur des Systems äußerst elegant, führt aber zu enormen Herausforderungen im Entwicklungsprozess – oder andersherum.
  • Unter Umständen spielt die Aufteilung in verschiedene Teilkomponenten in einem verteilten System eine viel größere Rolle als die Implementierung der einzelnen Systeme.
  • Vielleicht entstehen Probleme durch eine suboptimale Aufteilung zwischen Hardware- und Softwarekomponenten (besonders bei Embedded-Systemen) oder durch eine verbesserungswürdige Verwendung von Standardsoftware.

Die genannten Fälle unterscheiden sich sehr, und das ist ein spannender Aspekt von Reviews: Man muss sich in das System hineindenken und es aus unterschiedlichen Perspektiven hinterfragen. Es gibt leider kein Standardrezept, und auch die mächtigen Analysewerkzeuge können nur einen Teil der möglichen Probleme aufdecken.

Dennoch sollte Reviewer Code analysieren, weil sie damit eine Reihe gravierender Probleme finden können: schwer verständliche Teile, verursacht durch übermäßige Größe oder komplizierte Implementierung, Abweichungen von Code-Konventionen oder schlicht handwerklich schlecht implementierte Teile. Es gibt Code, der verschwenderisch mit CPU oder Speicher umgeht, Stellen ohne automatisierte Tests oder Passagen, die von den vereinbarten Architekturkonzepten abweichen.

Viele solcher Probleme lassen sich mit statischer Codeanalyse herausfinden. SonarQube, TeamScale, ReSharper oder Checkstyle seien hier als Beispiele für Tools genannt. Zu achten ist bei solchen statischen Analysen besonders auf negative Ausreißer bei Codekomplexität (etwa: zyklomatische Komplexität) oder Kopplung. Ganz wesentlich bleibt bei Code-Reviews allerdings die menschliche Expertise und Einschätzung.

Eine besonders hilfreiche Art der Code-Analyse sollten Reviewer auf jeden Fall durchführen – die Hotspot-Analyse: Ein Hotspot ist ein Baustein, der häufig geändert wird und gleichzeitig eine hohe innere Komplexität besitzt. Die Änderungshäufigkeit lässt sich aus der Versionsverwaltung, Git, Subversion und Co. abfragen, die Komplexität von Code relativ einfach über statische Analysen messen. Adam Tornhill hat in seinem Buch [2] entsprechende Analysewerkzeuge als Open Source veröffentlicht. Ein Beispiel solcher Hotspot-Analysen zeigt Abbildung 6.

Hotspot-Analysen finden hochgradig riskante Teile eines Systems: Komplexe Stellen besitzen ein sehr hohes Fehler- und Änderungsrisiko, und sind tendenziell auch viel aufwändiger in der Wartung. Das kombiniert mit hoher Änderungsfrequenz ist fast schon eine Garantie, dass bei diesen Bausteinen gravierende Probleme auftreten werden!

Beispiel für eine Hotspot-Analyse (Abb. 6)

Die zuverlässige Speicherung und Bereitstellung von Daten gehören für die meisten Anwendungen zu den Kernaufgaben. Die Daten einer Anwendung leben oft länger als der Quellcode. Quellcode lässt sich vergleichsweise einfach erweitern, refaktorieren oder auf eine neue Framework-Version anpassen, Bugfixes lassen sich meist relativ einfach deployen, fehlerhafte oder verlorene Daten dagegen nur schwer und aufwendig reparieren. Änderungen an den Strukturen eines produktiven Datenbestands sind oftmals zeitintensiv und erfordern sorgfältige Planung. Fehler in der Datenmodellierung werden Teams entsprechend länger verfolgen. Auch der Wechsel einer Datenbank ist teuer und zeitintensiv. Ein Wechsel von MySQL zu PostgreSQL ist aufwendig, weil alle datenbankspezifischen Funktionen zu finden und zu ersetzen sind. Ein Wechsel von MongoDB auf MySQL noch aufwendiger, weil er einen kompletten Wechsel des Datenmodells und des Schemamanagements erfordert. Im Rahmen des Reviews ist es die Aufgabe herauszufinden, ob die Auswahl der datenspeichernden Systeme, die Modellierung der Daten und ihre Übermittlung, Transformation und Qualität den Erfordernissen der jeweiligen Anwendung entspricht.

Deswegen sollte man sich zuerst einen Überblick über alle datenspeichernden Komponenten sowie der dazu eingesetzten Software verschaffen. Passen die verwendeten Datenmodelle und -strukturen noch zur aktuellen Domäne? Oder werden die aktuellen Business-Daten in irgendwelche historischen Strukturen "gequetscht"? Werden Daten redundant gehalten oder häufig synchronisiert? Gibt es Integrationsdatenbanken – die heute eher als Anti-Pattern gelten? Gibt es Problemen mit Antwortzeiten bei Abfragen? Laufen manche Anfragen extrem lange oder extrem häufig?

Danach gilt es, den Blick vom Code respektive der Technik hin zu den Entwicklungsprozessen, und dabei konkret auf die Anforderungen zu werfen: Gibt es für das System ein vernünftiges, angemessenes Requirements Engineering – oder purzeln Change-Requests rein zufällig auf das Entwicklungsteam? Gibt es einen ordentlich gepflegten Backlog, der den Namen auch verdient?

Auf Basis mangelhafter Anforderungen mit ständig wechselnden Prioritäten können auch hervorragende Entwicklungsteams keine gute Software produzieren – eben "garbage in, garbage out". Typische Schwierigkeiten mit Anforderungsprozessen sind etwa folgende:

  • Unklarheit über das Gewollte: Anforderungen bleiben vage, unpräzise, grob. Das Resultat: Entwicklungsteams implementieren stark konfigurierbare Systeme und verschieben damit die Entscheidungen über die konkrete Bedeutung von Anforderungen auf die Laufzeit.
  • Die Involvierten wollen dauernd etwas anderes: Hohe Volatilität in Anforderungen, sodass Entwicklungsteams häufig umbauen müssen.Verschiedene Stakeholder fordern widersprüchliche Dinge: Das können verschiedene Anforderungen an Daten, Validierungs- oder Geschäftsregeln bis hin zu unterschiedlichen Algorithmen sein.
  • Anforderungen benötigen innerhalb der gesamten Organisation zu lange, bis sie beim Entwicklungsteam landen. Es gibt zu viele beteiligte Personen, zu viele Gremien oder zu viele Abstimmungsprozesse.