Kapitel 4. Verbessere deine Architektur mit dem Modularity Maturity Index
Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com
In den letzten 20 Jahren ist viel Zeit und Geld in Softwaresysteme geflossen, die in modernen Programmiersprachen wie Java, C#, PHP usw. implementiert wurden. Bei den Entwicklungsprojekten lag der Schwerpunkt oft auf der schnellen Implementierung von Funktionen und nicht auf der Qualität der Softwarearchitektur. Diese Praxis hat dazu geführt, dass sich im Laufe der Zeit immer mehr technische Schulden angehäuft haben - unnötige Komplexität, die zusätzliches Geld für die Wartung kostet. Heute müssen diese Systeme als Altsysteme bezeichnet werden, weil ihre Wartung und Erweiterung teuer, mühsam und instabil ist.
In diesem Kapitel wird erörtert, wie man den Umfang der technischen Schulden in einem Softwaresystem mit dem Modularitäts-Reifegrad-Index (MMI) messen kann. Der MMI einer Codebasis oder der verschiedenen Anwendungen in einer IT-Landschaft gibt dem Management und den Teams einen Leitfaden für die Entscheidung, welche Softwaresysteme überarbeitet werden müssen, welche ersetzt werden sollten und um welche man sich nicht kümmern muss. Das Ziel ist es, herauszufinden, welche technischen Schulden behoben werden sollten, damit die Architektur nachhaltig und die Wartung weniger kostspielig wird.
Technische Verschuldung
Der Begriff technische Schuld wurde von Ward Cunningham 1992 geprägt: "Technische Schulden entstehen, wenn bewusst oder unbewusst falsche oder suboptimale technische Entscheidungen getroffen werden. Diese falschen oder suboptimalen Entscheidungen führen zu einem späteren Zeitpunkt zu zusätzlicher Arbeit, die Wartung und Erweiterung teurer macht."1 Zum Zeitpunkt der Fehlentscheidung fängst du an, technische Schulden anzuhäufen, die mit Zinsen abbezahlt werden müssen, wenn du nicht überschuldet enden willst.
In diesem Abschnitt führe ich zwei Arten von technischer Schuld auf, wobei ich mich auf die technische Schuld konzentriere, die durch eine Architekturprüfung gefunden werden kann:
- Umsetzung Schulden
- Der Quellcode enthält sogenannte Code Smells, wie lange Methoden und leere Catch-Blöcke. Implementierungsschulden lassen sich heute mit verschiedenen Tools weitgehend automatisiert im Quellcode finden. Jedes Entwicklungsteam sollte diese Schulden in seiner täglichen Arbeit nach und nach beseitigen, ohne dass dafür ein zusätzliches Budget benötigt wird.
- Schulden für Design und Architektur
- Das Design der Klassen, Pakete, Subsysteme, Schichten und Module und die Abhängigkeiten zwischen ihnen sind inkonsistent und komplex und entsprechen nicht der geplanten Architektur. Diese Schuld lässt sich nicht durch einfaches Zählen und Messen feststellen, sondern erfordert eine umfassende Überprüfung der Architektur, die in "Überprüfung der Architektur zur Bestimmung des MMI" vorgestellt wird .
Andere Problembereiche, die ebenfalls als Schulden von Softwareprojekten angesehen werden können, wie fehlende Dokumentation, mangelhafte Testabdeckung, schlechte Benutzerfreundlichkeit oder unzureichende Hardware, werden hier ausgelassen, weil sie nicht in die Kategorie der technischen Schulden gehören.
Entstehung von technischer Verschuldung
Schauen wir uns die Entstehung und die Auswirkungen der technischen Schulden an. Wenn zu Beginn eines Softwareentwicklungsprojekts eine qualitativ hochwertige Architektur entworfen wurde, kann man davon ausgehen, dass das Softwaresystem zu Beginn leicht gewartet werden kann. In dieser Anfangsphase befindet sich das Softwaresystem im Korridor der geringen technischen Schulden bei gleichem Wartungsaufwand, wie du in Abbildung 4-1 siehst.
Wenn das System bei der Wartung und zur Integration von Änderungen immer weiter ausgebaut wird, entstehen unweigerlich technische Schulden (gekennzeichnet durch die nach oben zeigenden Pfeile in Abbildung 4-1). Softwareentwicklung ist ein ständiger Lernprozess, bei dem der erste Wurf einer Lösung selten der endgültige ist. Die Überarbeitung der Architektur (Architekturverbesserung, dargestellt durch die nach unten zeigenden Pfeile) muss in regelmäßigen Abständen durchgeführt werden. So entsteht eine ständige Abfolge von Wartung/Änderung und Architekturverbesserung.
Wenn ein Team eine konstante Abfolge von Erweiterungen und Architekturverbesserungen dauerhaft verfolgen kann, wird das System im Korridor niedriger und stabiler Wartungskosten bleiben. Leider ist dieser Aspekt der Architekturverbesserung für viele Budgetverantwortliche erst in den letzten Jahren wirklich Realität geworden - zu spät für die meisten Systeme, die in den frühen 2000er Jahren begonnen haben.
Wenn es dem Entwicklungsteam nicht möglich ist, die technischen Schulden kontinuierlich abzubauen, setzt mit der Zeit unweigerlich eine Erosion der Architektur ein, wie die aufsteigenden Pfeile zeigen, die den Korridor der niedrigen und stabilen Wartungskosten in Abbildung 4-1 verlassen. Dieser Prozess wird als Architekturerosion bezeichnet. Wenn sich die technischen Schulden erst einmal angehäuft haben, wird die Wartung und Änderung der Software immer teurer und die daraus resultierenden Fehler immer schwieriger zu verstehen, bis zu dem Punkt, an dem jede Änderung zu einem schmerzhaften Aufwand wird. Abbildung 4-1 verdeutlicht diesen langsamen Verfall durch die Tatsache, dass die nach oben zeigenden Pfeile immer kürzer werden. Mit zunehmender Verschuldung kann immer weniger Funktionalität pro Zeiteinheit implementiert werden.
Es gibt zwei Möglichkeiten, aus diesem Dilemma herauszukommen:
- Refactoring
- Du kannst das Altsystem von innen nach außen refaktorisieren und so die Entwicklungsgeschwindigkeit und Stabilität wieder erhöhen. Auf diesem meist mühsamen Weg muss das System Schritt für Schritt wieder in den Korridor niedriger und stabiler Wartungskosten gebracht werden (siehe absteigende Pfeile mit der Bezeichnung "Refactoring" in Abbildung 4-1).
- Ersetzen von
- Oder du kannst das Altsystem durch eine andere Software ersetzen, die weniger technische Schulden hat (siehe den Kreis in Abbildung 4-1).
Es kann natürlich sein, dass zu Beginn der Entwicklung kein fähiges Team vor Ort war. In diesem Fall werden die technischen Schulden gleich zu Beginn der Entwicklung aufgenommen und kontinuierlich erhöht. Das kann man über solche Softwaresysteme sagen: Sie sind unter schlechten Bedingungen aufgewachsen. Weder die Softwareentwickler noch das Management werden auf Dauer Freude an einem System in einem solchen Zustand haben.
Eine solche Sichtweise auf technische Schulden ist für die meisten Budgetverantwortlichen verständlich und nachvollziehbar. Niemand will technische Schulden anhäufen und sich langsam mit Entwicklungen verzetteln, bis jede Anpassung zu einer unkalkulierbaren Kostenschraube wird. Auch der Aspekt, dass kontinuierliche Arbeit erforderlich ist, um die technischen Schulden über die gesamte Lebensdauer der Software niedrig zu halten, lässt sich gut vermitteln. Die meisten Nicht-IT-Leute sind sich des Problems inzwischen durchaus bewusst, aber wie kann man die Schulden in einem Softwaresystem eigentlich bewerten?
Bewertung mit dem MMI
Meine Doktorarbeit über Architektur und Kognitionswissenschaft, sowie die Ergebnisse von mehr als dreihundert Architekturbewertungen haben es mir und meinem Team ermöglicht, ein einheitliches Bewertungsschema, das MMI, zu erstellen, um die in der Architektur verschiedener Systeme angesammelten technischen Schulden zu vergleichen.
Die Kognitionswissenschaft zeigt uns, dass das menschliche Gehirn im Laufe der Evolution einige beeindruckende Mechanismen entwickelt hat, die uns dabei helfen, mit komplexen Strukturen umzugehen, z. B. mit der Organisation von Regimen, dem Aufbau von Städten und Ländern, genealogischen Beziehungen und so weiter. Auch Softwaresysteme sind aufgrund ihrer Größe und der Anzahl der Elemente, die sie enthalten, zweifelsohne komplexe Strukturen. In meiner Doktorarbeit habe ich Erkenntnisse aus der Kognitionspsychologie über drei Mechanismen, mit denen unser Gehirn mit Komplexität umgeht (Chunking, Aufbau von Hierarchien und Aufbau von Schemata), mit wichtigen Architektur- und Designprinzipien der Informatik (Modularität, Hierarchie und Musterkonsistenz) verknüpft. In diesem Kapitel kann ich diese Zusammenhänge nur in Kurzform erklären. Die vollständigen Details, insbesondere zur kognitiven Psychologie, findest du in meinem Buch,2 das aus meiner Doktorarbeit hervorgegangen ist.
Diese Prinzipien haben die herausragende Eigenschaft, dass sie in unserem Gehirn Mechanismen für den Umgang mit komplexen Strukturen begünstigen. Architekturen und Entwürfe, die diesen Prinzipien folgen, werden von Menschen als einheitlich und verständlich wahrgenommen und lassen sich daher leichter warten und erweitern. Deshalb müssen diese Prinzipien in Softwaresystemen angewendet werden, damit Wartung und Erweiterung schnell und ohne viele Fehler durchgeführt werden können. Das Ziel ist, dass wir unsere Softwaresysteme mit wechselnden Entwicklungsteams über lange Zeit hinweg weiterentwickeln können und dabei die gleiche Qualität und Geschwindigkeit in der Entwicklung beibehalten.
Modularität
In der Softwareentwicklung ist die Modularität ein Prinzip, das von David Parnas in den 1970er Jahren eingeführt wurde. Parnas vertrat die Ansicht, dass ein Modul nur eine Designentscheidung enthalten sollte (Kapselung) und dass die Datenstruktur für diese Designentscheidung in der Modullokalität gekapselt sein sollte.3
In modernen Programmiersprachen sind Module Einheiten innerhalb eines Softwaresystems, wie Klassen, Komponenten oder Schichten. Unser Gehirn liebt es, über Systeme auf verschiedenen Ebenen von Einheiten zu denken, um einen Kapazitätsgewinn in unserem Gedächtnis zu erzielen. Der entscheidende Punkt dabei ist, dass unser Gehirn nur dann von diesen Einheiten profitiert, wenn die Details als kohärente Einheit dargestellt werden können, die etwas Sinnvolles bildet. Programmeinheiten, die willkürliche, nicht zusammenhängende Elemente kombinieren, sind daher nicht sinnvoll und werden von unserem Gehirn nicht akzeptiert. Ein modulares System mit kohärenten und sinnvollen Programmeinheiten hat also eine geringe technische Schuld und eine geringe unnötige Komplexität.
Ob die Programmeinheiten kohärente und sinnvolle Elemente in einer Softwarearchitektur darstellen, kann nur qualitativ beurteilt werden. Diese qualitative Beurteilung wird durch verschiedene Messungen und Untersuchungen unterstützt:
- Kohäsion durch Kopplung
- Einheiten sollten Untereinheiten enthalten, die zusammengehören. Das bedeutet, dass ihr Zusammenhalt untereinander hoch und ihre Kopplung nach außen gering sein sollte. Wenn zum Beispiel die Untermodule eines Moduls eine höhere Kopplung mit anderen Modulen haben als mit ihren "Schwestern und Brüdern", dann ist ihr Zusammenhalt untereinander gering und die Modularität nicht gut gemacht. Im Gegensatz zur Kohäsion ist die Kopplung messbar.
- Namen
- Wenn die Programmeinheiten eines Systems modular aufgebaut sind, solltest du für jede Einheit die folgende Frage beantworten können: Was ist ihre Aufgabe? Wichtig ist dabei, dass die Programmeinheit wirklich eine Aufgabe hat und nicht mehrere. Ein guter Anhaltspunkt für unklare Zuständigkeiten sind die Namen der Einheiten, die ihre Aufgaben beschreiben sollten. Wenn der Name vage ist, sollte er überprüft werden. Leider ist das nicht messbar.
- Ausgewogene Proportionen
- Modulare Programmeinheiten, die sich auf einer Ebene befinden, wie z. B. Schichten, Komponenten, Pakete, Klassen und Methoden, sollten ausgewogene Proportionen haben. Hier lohnt es sich, die sehr großen Programmeinheiten daraufhin zu untersuchen, ob sie Kandidaten für eine Zerlegung sind. Ein extremes Beispiel ist in Abbildung 4-2 zu sehen. Im linken Diagramm siehst du die Größe der neun Build Units eines Systems in einem Tortendiagramm: Das Team hat uns gesagt, dass die Build Units die geplanten Module des Systems widerspiegeln. Eine Build Unit macht mit 950.860 Codezeilen (LOC) den größten Teil des Kuchendiagramms aus. Die anderen acht Build Units summieren sich auf nur 84.808 LOC, was extrem unausgewogen ist. Auf der rechten Seite von Abbildung 4-2 siehst du die Architektur des Systems mit den neun Build Units als Quadrate und die Beziehungen zwischen den Build Units als Bögen. Der Teil des Systems, der "Monolith" genannt wird, ist der große Teil des Tortendiagramms. Er nutzt die acht kleinen Baueinheiten, die wir hier "Satellit X" nennen. Die dunkle Schattierung des Monolithen im Vergleich zur hellen Schattierung der Satelliten zeigt, genau wie das Tortendiagramm, dass sich hier der Großteil des Quellcodes befindet. Die Modularität dieses Systems ist nicht gut ausbalanciert. Dieser Indikator ist messbar.
Du kannst ähnliche Bewertungen auf allen Ebenen durchführen, um die Modularität eines Systems zu überprüfen. Der Einfluss der einzelnen Punkte auf die Berechnung des MMI folgt der Erklärung von Hierarchien und Musterkonsistenz.
Hierarchie
Hierarchien spielen eine wichtige Rolle in der Wahrnehmung und dem Verständnis komplexer Strukturen und bei der Speicherung von Wissen. Menschen können Wissen gut aufnehmen, wiedergeben und sich darin zurechtfinden, wenn es in hierarchischen Strukturen vorliegt. Die Bildung von Hierarchien wird in Programmiersprachen durch "contain-being"-Beziehungen unterstützt: Klassen befinden sich in Paketen, Pakete wiederum in Paketen und schließlich in Projekten oder Build-Artefakten. Diese Hierarchien passen zu unseren kognitiven Mechanismen.
Anders als die Beziehung "contain-being" können die Beziehungen "use" und "inherit" von auf eine Weise verwendet werden, die keine Hierarchien schafft: Wir können beliebige Klassen und Schnittstellen in einer Quellcodebasis mit "use" und/oder "inherit" Beziehungen verknüpfen. Auf diese Weise schaffen wir verflochtene Strukturen, die in keiner Weise hierarchisch sind. In unserer Disziplin sprechen wir dann von Klassenzyklen, Paketzyklen, Zyklen zwischen Modulen und Aufwärtsbeziehungen zwischen den Schichten einer Architektur. Bei meinen Architekturprüfungen sehe ich die ganze Bandbreite, von sehr wenigen zyklischen Strukturen bis hin zu großen zyklischen Monstern.
Abbildung 4-3 zeigt einen Klassenzyklus von 242 Klassen. Jedes Rechteck steht für eine Klasse, und die Linien zwischen ihnen stellen ihre Beziehungen dar. Dieser Zyklus ist über 18 Verzeichnisse verteilt, und sie alle brauchen einander, um ihre Aufgaben zu erfüllen.
Das System, aus dem der Zyklus in Abbildung 4-3 stammt, besteht aus insgesamt 479 Klassen. Mehr als die Hälfte aller Klassen (242) brauchen sich also direkt oder indirekt gegenseitig. Außerdem hat dieser Kreislauf eine starke Konzentration in der Mitte und nur wenige Satelliten. Es gibt keine natürliche Möglichkeit, diesen Zyklus aufzubrechen, aber eine ganze Menge Arbeit, diese Klassen neu zu gestalten. Es ist viel besser, von Anfang an dafür zu sorgen, dass solche großen Zyklen nicht auftreten. Glücklicherweise haben die meisten Systeme kleinere und weniger konzentrierte Zyklen, die mit ein paar Refactorings aufgebrochen werden können.
Abbildung 4-4 zeigt eine nicht-hierarchische Struktur auf der Architekturebene. Vier technische Schichten eines kleinen Anwendungssystems (80.000 LOC) - App, Services, Entities und Util - liegen übereinander und nutzen sich, wie beabsichtigt, hauptsächlich von oben nach unten (nach unten weisende Pfeile auf der linken Seite). Zwischen den Schichten haben sich einige Rückverweise (nach oben weisende Pfeile auf der rechten Seite) eingeschlichen, die zu Zyklen zwischen den Schichten und damit zu Architekturverletzungen führen.
Die Verstöße in dieser geschichteten Architektur werden durch nur 16 Klassen verursacht und waren leicht zu beheben. Auch bei dieser Art von Zyklen oder Verstößen gegen die Schichtenarchitektur gilt: Je eher du sie findest und überarbeitest, desto besser.
Die gute Nachricht ist, dass die Zyklen auf allen Ebenen leicht zu messen sind und somit die Hierarchie eines Systems genau überprüft werden kann. Der Einfluss der einzelnen Punkte auf die Berechnung des MMI folgt der Erklärung der Musterkonsistenz.
Musterkonsistenz
Der effizienteste Mechanismus, den Menschen nutzen, um komplexe Beziehungen zu strukturieren, sind Schemata. Ein Schema fasst die typischen Eigenschaften von ähnlichen Dingen oder Zusammenhängen als Abstraktion zusammen. Wenn du zum Beispiel erfährst, dass eine Person Lehrerin oder Lehrer ist, dann enthält dein Schema auf der abstrakten Ebene verschiedene Annahmen und Vorstellungen über die damit verbundene Tätigkeit: Lehrkräfte sind an einer Schule angestellt, sie haben keinen Acht-Stunden-Arbeitstag und sie müssen Klassenarbeiten korrigieren. Konkret erinnerst du dich an deine eigenen Lehrer/innen, die du als Prototypen des Lehrer/innen-Schemas gespeichert hast.
Wenn du ein Schema für einen Kontext in deinem Leben hast, kannst du Fragen und Probleme viel schneller verstehen und bearbeiten als ohne ein Schema. Die in der Softwareentwicklung weit verbreiteten Entwurfsmuster nutzen zum Beispiel die Stärke des menschlichen Gehirns, mit Schemata zu arbeiten. Wenn Entwickler/innen bereits mit einem Entwurfsmuster gearbeitet und daraus ein Schema erstellt haben, können sie Programmtexte und Strukturen, die dieses Entwurfsmuster verwenden, schneller erkennen und verstehen.
Die Verwendung von Schemata verschafft uns im Alltag entscheidende Geschwindigkeitsvorteile beim Verstehen komplexer Strukturen. Das ist auch der Grund, warum Muster schon vor Jahren ihren Weg in die Softwareentwicklung gefunden haben. Für Entwickler und Architekten ist es wichtig, dass es Muster gibt, dass sie im Quellcode zu finden sind und dass sie konsequent angewendet werden. Konsequent angewandte Muster helfen uns also, mit der Komplexität des Quellcodes umzugehen.
Abbildung 4-5 zeigt auf der linken Seite ein Diagramm, das ein Team entwickelt hat, um seine Entwurfsmuster in einem Tool festzuhalten, mit dem sie Klassen eines Musters in einer Ebene sammeln können. Auf der rechten Seite ist der Quellcode in diese Entwurfsmuster unterteilt, und du kannst viele Bögen auf der linken Seite der Achse sehen, die nach unten gehen, damit sie in das Diagramm passen. Die Bögen auf der linken Seite der Achse gehen nach unten und die Bögen auf der rechten Seite gehen entgegen der Schichtung nach oben. Ein paar Bögen auf der rechten Seite der Achse gehen gegen die Schichtung von unten nach oben. Das ist typisch; Entwurfsmuster bilden hierarchische Strukturen. Die Entwurfsmuster sind in diesem System gut umgesetzt, weil es hauptsächlich Bögen auf der linken Seite (Beziehungen von oben nach unten) und nur sehr wenige Bögen auf der rechten Seite (Beziehungen von unten nach oben, entgegen der von den Mustern vorgegebenen Richtung) gibt.
Die Untersuchung der Muster im Quellcode ist normalerweise der spannendste Teil einer Architekturprüfung. Hier musst du die Ebene erfassen, auf der das Entwicklungsteam wirklich arbeitet. Die Klassen, die die einzelnen Muster implementieren, sind oft über die Pakete oder Verzeichnisse verteilt. Durch die Modellierung des in Abbildung 4-5 rechts dargestellten Musters kann diese Ebene der Architektur sichtbar und analysierbar gemacht werden.
Die Konsistenz von Mustern kann nicht direkt gemessen werden, wie es bei Hierarchien der Fall ist, aber im nächsten Abschnitt werden wir einige Messungen zusammenstellen, die zur Bewertung der Musterkonsistenz im MMI verwendet werden.
Berechnen des MMI
Der MMI wird aus verschiedenen Kriterien und Metriken berechnet, mit denen wir versuchen, die drei Prinzipien Modularität, Hierarchien und Musterkonsistenz abzubilden. In der Gesamtberechnung des MMI sind die drei Prinzipien mit einem Prozentsatz enthalten und haben unterschiedliche Kriterien, die für sie anhand der Anweisungen in Tabelle 4-1 berechnet werden. Modularität hat mit 45% den stärksten Einfluss auf den MMI, denn Modularität ist auch die Grundlage für Hierarchie und Musterkonsistenz. Das ist auch der Grund, warum der MMI diesen Namen trägt.4
Die Kriterien in Tabelle 4-1 können mit Metrik-Tools, Architektur-Analyse-Tools oder nach dem Urteil des Prüfers ermittelt werden (siehe Spalte "Ermittelt durch"). Es gibt eine Reihe von Metrik-Tools, mit denen genaue Werte gemessen werden können, aber für die MMI-Vergleichbarkeit muss die Implementierung der einzelnen Metriken, die in dem jeweiligen Tool verwendet werden, berücksichtigt werden. Das Urteil des Prüfers kann nicht gemessen werden, sondern liegt im Ermessen des Prüfers. Die Instrumente zur Architekturanalyse sind zwar messbar, hängen aber stark von den Erkenntnissen des Prüfers ab.
Um diese nicht messbaren Kriterien an meinem Arbeitsplatz zu bewerten, sind die Prüfer auf Gespräche mit den Entwicklern und Architekten angewiesen. Dies geschieht in Workshops vor Ort oder aus der Ferne. Wir besprechen das System auch aus verschiedenen architektonischen Blickwinkeln, mit Hilfe eines Architekturanalyse-Tools.5 Um eine größtmögliche Kompatibilität zu gewährleisten, führen wir die Prüfungen immer paarweise durch und diskutieren die Ergebnisse dann in einer größeren Gruppe von Architekturprüfern.
Kategorie | Unterkategorie | Kriterien | Bestimmt durch |
---|---|---|---|
1. Modularität (45%) | |||
1.1. Fachliche und technische Modularisierung (25%) | |||
1.1.1. Aufteilung des Quellcodes auf die Domänenmodule in % des gesamten Quellcodes | Werkzeuge zur Architekturanalyse | ||
1.1.2. Aufteilung des Quellcodes auf die technischen Schichten in % des gesamten Quellcodes | Werkzeuge zur Architekturanalyse | ||
1.1.3. Größenverhältnisse der Domänenmodule [(LoC max/LoC min)/Anzahl] | Metrik-Tools | ||
1.1.4. Größenverhältnisse der technischen Schichten [(LoC max/LoC min)/Anzahl] | Metrik-Tools | ||
1.1.5. Domänenmodule, technische Schichten, Pakete und Klassen haben klare Verantwortlichkeiten | Rezensenten | ||
1.1.6. Abbildung der technischen Schichten und Domänenmodule durch Pakete/Namensräume oder Projekte | Rezensenten | ||
1.2. Interne Schnittstellen (10%) | |||
1.2.1. Fachliche oder technische Module haben Schnittstellen (% Verstöße) | Werkzeuge zur Architekturanalyse | ||
1.2.2. Abbildung der internen Schnittstellen durch Pakete/Namensräume oder Projekte | Rezensenten | ||
1.3. Proportionen (10%) | |||
1.3.1. % des Quellcodes in großen Klassen | Metrik-Tools | ||
1.3.2. % des Quellcodes in großen Methoden | Metrik-Tools | ||
1.3.3. % der Klassen in großen Paketen | Metrik-Tools | ||
1.3.4. % der Methoden des Systems mit einer hohen zyklomatischen Komplexität | Metrik-Tools | ||
2. Hierarchie (30%) | |||
2.1. Technisches und fachliches Layering (15%) | |||
2.1.1. Anzahl der Architekturverstöße in der technischen Schicht (%) | Werkzeuge zur Architekturanalyse | ||
2.1.2. Anzahl der Architekturverletzungen bei der Schichtung der Domänenmodule (%) | Werkzeuge zur Architekturanalyse | ||
2.2. Klassen- und Paketzyklen (15%) | |||
2.2.1. Anzahl der Klassen in Zyklen (%) | Metrik-Tools | ||
2.2.2. Anzahl der Pakete in Zyklen (%) | Metrik-Tools | ||
2.2.3. Anzahl der Klassen pro Zyklus | Metrik-Tools | ||
2.2.4. Anzahl der Pakete pro Zyklus | Metrik-Tools | ||
3. Musterkonsistenz (25%) | |||
3.1. Verteilung des Quellcodes auf die Muster in % des gesamten Quellcodes | Werkzeuge zur Architekturanalyse | ||
3.2. Beziehungen der Muster sind zyklusfrei (% Verstöße) | Werkzeuge zur Architekturanalyse | ||
3.3. Explizite Zuordnung der Patterns (über Klassennamen, Vererbung oder Annotationen) | Rezensenten | ||
3.4. Trennung von fachlichem und technischem Quellcode (DDD, Quasar, Hexagonal) | Rezensenten |
Der MMI wird berechnet, indem für jedes Kriterium anhand von Tabelle 4-2 eine Zahl zwischen 0 und 10 ermittelt wird. Die sich daraus ergebenden Zahlen pro Abschnitt werden addiert und durch die Anzahl der fraglichen Kriterien aus Tabelle 4-1 geteilt. Das Ergebnis wird im MMI mit dem Prozentsatz des jeweiligen Grundsatzes festgehalten, so dass eine Zahl zwischen 0 und 10 ermittelt werden kann.
Sektion. | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|
1.1.1 | <=54% | >54% | >58% | >62% | >66% | >70% | >74% | >78% | >82% | >86% | >90% |
1.1.2 | <=75% | >75% | >77.5% | >80% | >82.5% | >85% | >87.5% | >90% | >92.5% | >95% | >97.5% |
1.1.3 | >=7.5 | <7.5 | <5 | <3.5 | <2.5 | <2 | <1.5 | <1.1 | <0.85 | <0.65 | <0.5 |
1.1.4 | >=16.5 | <16.5 | <11 | <7.5 | <5 | <3.5 | <2.5 | <2 | <1.5 | <1.1 | <0.85 |
1.1.5 | Nein | teilweise | Ja, alle | ||||||||
1.1.6 | Nein | teilweise | Ja | ||||||||
1.2.1 | >=6.5% | <6.5% | <4% | <2.5% | <1.5% | <1% | <0.65% | <0.4% | <0.25% | <0.15% | <0.1% |
1.2.2 | Nein | teilweise | Ja | ||||||||
1.3.1 | >=23% | <23% | <18% | <13.5% | <10.5% | <8% | <6% | <4.75% | <3.5% | <2.75% | <2% |
1.3.2 | >=23% | <23% | <18% | <13.5% | <10.5% | <8% | <6% | <4.75% | <3.5% | <2.75% | <2% |
1.3.3 | >=23% | <23% | <18% | <13.5% | <10.5% | <8% | <6% | <4.75% | <3.5% | <2.75% | <2% |
1.3.4 | >=3.6% | <3.6% | <2.6% | <1.9% | <1.4% | <1% | <0.75% | <0.5% | <0.4% | <0.3% | <0.2% |
2.1.1 | >=6.5% | <6.5% | <4% | <2.5% | <1.5% | <1% | <0.65% | <0.4% | <0.25% | <0.15% | <0.1% |
2.1.2 | >=14% | <14% | <9.6% | <6.5% | <4.5% | <3.2% | <2.25% | <1.5% | <1.1% | <0.75% | <0.5% |
2.2.1 | >=25% | <25% | <22.5% | <20% | <17.5% | <15% | <12.5% | <10% | <7.5% | <5% | <2.5% |
2.2.2 | >=50% | <50% | <45% | <40% | <35% | <30% | <25% | <20% | <15% | <10% | <5% |
2.2.3 | >=106 | <106 | <82 | <62 | <48 | <37 | <29 | <22 | <17 | <13 | <10 |
2.2.4 | >=37 | <37 | <30 | <24 | <19 | <15 | <12 | <10 | <8 | <6 | <5 |
3.1 | <=54.5% | >54.5% | >59% | >63.5% | >68% | >72.5% | >77% | >81.5% | >86% | >90.5% | >95% |
3.2 | >=7.5% | <7.5% | <5% | <3.5% | <2.5% | <2% | <1.5% | <1.1% | <0.85% | <0.65% | <0.5% |
3.3 | Nein | teilweise | Ja | ||||||||
3.4 | Nein | teilweise | Ja |
Abbildung 4-6 zeigt eine Auswahl von 18 Softwaresystemen, die wir über einen Zeitraum von 5 Jahren bewertet haben (x-Achse). Für jedes System wird die Größe in Codezeilen (Größe des Punktes) und der MMI auf einer Skala von 0 bis 10 (y-Achse) dargestellt.
Wenn ein System zwischen 8 und 10 eingestuft wird, ist der Anteil der technischen Schulden gering. Das System befindet sich in dem Korridor niedrige und stabile Wartung und Kosten (aus Abbildung 4-1). Systeme in Abbildung 4-6 mit einer Bewertung zwischen 4 und 8 haben bereits eine ganze Menge technischer Schulden angesammelt. Entsprechende Refactorings sind hier notwendig, um die Qualität zu verbessern. Systeme unter der Note 4 können nur mit großem Aufwand gewartet und erweitert werden (siehe den Korridor in Abbildung 4-1 mit hohen und unvorhersehbaren Wartungskosten). Bei diesen Systemen muss sorgfältig abgewogen werden, ob es sich lohnt, sie durch Refactoring zu verbessern oder ob das System ersetzt werden sollte.
Überprüfung der Architektur zur Bestimmung des MMI
Die meisten Entwicklungsteams können auf Anhieb eine Liste der Design- und Architekturschulden für das System, das sie entwickeln, aufzählen. Diese Liste ist ein guter Ausgangspunkt für die Analyse der technischen Schulden. Um den Design- und Architekturschulden auf den Grund zu gehen, wird eine Architekturanalyse empfohlen. Mit einer Architekturanalyse kann überprüft werden, inwieweit die geplante Zielarchitektur im Quellcode (siehe Abbildung 4-7), der die tatsächliche Architektur darstellt, umgesetzt wurde. Die Zielarchitektur ist der Plan für die Architektur, der auf dem Papier oder im Kopf des Architekten und Entwicklers existiert. Für solche Soll-Ist-Vergleiche gibt es heute mehrere gute Tools, darunter Lattix, Sotograph/SotoArc, Sonargraph, Structure101 und TeamScale.
In der Regel weicht die tatsächliche Architektur im Quellcode von der geplanten Zielarchitektur ab. Hierfür gibt es viele Gründe. Abweichungen treten oft unbemerkt auf, weil Entwicklungsumgebungen nur einen lokalen Einblick in den gerade bearbeiteten Quellcode bieten und keinen Überblick verschaffen. Auch mangelndes Wissen über die Architektur im Entwicklungsteam führt zu diesem Effekt. In anderen Fällen werden die Abweichungen zwischen der Soll- und der Ist-Architektur absichtlich in Kauf genommen, weil das Team unter Zeitdruck steht und eine schnelle Lösung braucht. Das notwendige Refactoring wird dann auf unbestimmte Zeit verschoben.
Abbildung 4-8 zeigt den Ablauf einer Architekturanalyse zur Identifizierung technischer Schulden. Eine Architekturanalyse wird von einem Prüfer zusammen mit den Architekten und Entwicklern des Systems in einem Workshop durchgeführt. Zu Beginn des Workshops wird der Quellcode des Systems mit dem Analysetool (1) geparst und die Ist-Architektur erfasst. Die Soll-Architektur wird nun auf die Ist-Architektur modelliert, so dass Soll und Ist verglichen werden können (2).
Technische Schwachstellen werden sichtbar, und der Reviewer sucht gemeinsam mit dem Entwicklungsteam nach einfachen Lösungen, wie die Ist-Architektur durch Refactoring an die Soll-Architektur angepasst werden kann (3). Oder der Reviewer und das Entwicklungsteam entdecken in der Diskussion, dass die im Quellcode gewählte Lösung besser ist als der ursprüngliche Plan.
Manchmal ist jedoch weder die Soll-Architektur noch die abweichende Ist-Architektur die beste Lösung, und der Reviewer und das Entwicklungsteam müssen gemeinsam ein neues Zielbild für die Architektur entwerfen. Im Zuge einer solchen Architekturprüfung sammeln der Prüfer und das Entwicklungsteam technische Schulden und mögliche Refactorings (4). Schließlich betrachten wir verschiedene Metriken (5), um weitere technische Schulden wie große Klassen, zu starke Kopplung, Zyklen und so weiter zu finden.
Fazit
Das MMI bestimmt das Ausmaß der technischen Schulden in einem Altsystem. Je nach Ergebnis in den Bereichen Modularität, Hierarchie und Musterkonsistenz kann die Notwendigkeit eines Refactorings oder einer möglichen Ablösung des Systems bestimmt werden. Liegt das Ergebnis unter 4, muss überlegt werden, ob es sinnvoll ist, das System durch ein anderes System zu ersetzen, das mit weniger technischen Schulden belastet ist. Liegt das System zwischen 4 und 8, ist eine Erneuerung in der Regel billiger als eine Ersetzung. In diesem Fall sollte das Team mit dem Prüfer zusammenarbeiten, um Refactorings zu definieren und zu priorisieren, die die technischen Schulden reduzieren. Diese Refactorings müssen Schritt für Schritt in die Wartung oder Erweiterung des Systems eingeplant werden und die Ergebnisse müssen regelmäßig überprüft werden. Auf diese Weise kann ein System schrittweise in den Bereich "ständiger Aufwand für die Wartung" überführt werden.
Ein System mit einem MMI von über 8 ist eine große Freude für die Rezensenten. In der Regel stellen wir fest, dass das Team und seine Architekten eine gute Arbeit geleistet haben und stolz auf ihre Architektur sind. In einem solchen Fall freuen wir uns sehr, dass wir die Arbeit mit dem MMI positiv bewerten können.
1 Ward Cunningham, "Das WyCash Portfolio Management System: Erfahrungsbericht", OOPSLA '92, Vancouver, BC, 1992.
2 Carola Lilienthal, "Nachhaltige Softwarearchitektur" (Dissertation, Dpunkt.verlag, 2019).
3 David Parnas, "On the Criteria to be Used in Decomposing Systems into Modules", Communications of the ACM 15, no. 12 (1972): 1053-1058.
4 Lilienthal, "Nachhaltige Softwarearchitektur".
5 In meiner eigenen Analyse verwende ich Sotograph, Sonargraph, Lattix, Structure101 und TeamScale.
Get Software Architektur Metriken now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.