adesso Blog

Ob „The Home Edit“ oder „Aufräumen mit Marie Kondo“ - in den beliebten Netflix-Serien geht es um professionelles und nachhaltiges Aufräumen. Die Organisationsprofis versprechen, aus Chaos ein System zu machen, in dem wir für immer Ordnung halten können.

Doch was genau ist ein System? Ein System ist eine geordnete und umfassende Zusammenstellung von Einzelteilen in einem bestimmten Wissens- oder Denkgebiet. Es ist eine beliebige Ansammlung oder Menge von Einzelteilen, die miteinander in Beziehung stehen. Professionelle Organisatoren definieren, was ein gutes System ausmacht: Form und Funktion. Ein gutes System spart Zeit. Man findet Einzelteile schnell wieder, weiß, wo man Einzelteile ablegen muss und kann das System schnell an die eigenen Bedürfnisse anpassen. Und man spart Geld. Man hat einen Überblick über alle Einzelteile im System. Weil man sie wiederfindet, muss man sie nicht neu kaufen. Außerdem braucht ein gutes System weniger Platz. Und zu guter Letzt möchte man mit seinem System vielleicht auch einen guten Eindruck auf Besucherinnen und Besucher machen.

„Simplicity is ultimate sophistication.”
The Home Edit

In einem System können verschiedene Qualitätsmerkmale je nach Bedarf mehr oder weniger stark ausgeprägt sein. In einer Werkstatt wird mehr Wert auf Erweiterbarkeit gelegt, da ständig neue Tools und Materialien hinzukommen. Im arc42 Qualitätsmodell finden sich viele weitere solcher Qualitätsmerkmale speziell für Softwaresysteme.

Natürlich ist ein System sehr viel mehr als eine einfache Dateiablage. Von außen betrachtet legen wir aber einfach erstmal Einzelteile in eine dafür vorgesehene Kiste. Das machen Menschen auf zwei Arten:

1. Einfach alles reinwerfen. In diesem Fall sind die Benutzerinnen und Benutzer des Systems später auf Suchtools - wie etwa die Suche in unserer IDE - angewiesen.

2. Man überlegt sich eine Struktur und ordnet alles immer wieder in diese Struktur ein. Dies ermöglicht uns zusätzlich eine logische Navigation, um etwas wiederzufinden.

Auch wenn wir nicht bewusst darüber nachdenken, neigen wir dazu, unsere Repos in irgendeiner Weise zu strukturieren. Strukturen, die mir in Repos schon begegnet sind, sind nach Dateitypen strukturierte Ordner, nach Komponenten eines bestimmten Frameworks - im Falle von Angular wären das zum Beispiel Pipes und Directives - oder auch nach Features strukturierte Ordner. Letztere werden oft mit Metaphern wie „Screaming Architecture“ beworben. Der Begriff „Screaming Architecture“ wird verwendet, wenn man bei einem neuen Projekt auf den ersten Blick erkennen kann, was das Projekt im Kern macht und worum es geht.

„If the system is a story, the modules are its chapters."
Eric Evans

Alle genannten Strukturen haben ihre Vor- und Nachteile. Die Wissenschaft konnte jedoch zeigen, dass unser Gehirn dazu neigt, Wissensblöcke in Pakete zu zerlegen und objektive Schemata zu erstellen, die die reale Welt abbilden. Außerdem konnte gezeigt werden, dass wir bei der virtuellen Navigation durch ein Dateisystem die gleichen Hirnareale aktivieren wie bei der Navigation in der realen Welt, wenn wir zum Beispiel in der Küche wieder einmal den Sparschäler suchen. Bei der Suche über die Eingabe eines Suchparameters hingegen werden vor allem links lateralisierte, meist frontale Aktivierungen beobachtet, die mit sprachlichen Prozessen in Verbindung stehen (Broca-Areal). Der Navigationsprozess funktioniert auch bei Menschen mit schweren Sprachproblemen nach einer Hirnschädigung noch sehr gut. Um sinnvoll durch ein System navigieren zu können, bedarf es einer guten und für den Menschen verständlichen Struktur.

„Organization is the key to productivity.”
The Home Edit

Aber leider gelingt es uns oft nicht, eine angemessene Struktur aufrechtzuerhalten, weder im Haushalt noch in unseren digitalen Systemen. Habt ihr zu Hause eine Kiste, von der du nicht genau weißt, was drin ist, aber es könnte eigentlich alles sein? Nicht nur eine? Solche Kisten haben wir oft in unseren Repos. Entwicklerinnen und Entwickler verbringen mehr als 50 Prozent ihrer Zeit damit, Programme zu verstehen. Ein reales System mit Form und Funktion sollte aber einfach zu verstehen sein.

“Simplicity is a great virtue but it requires hard work to achieve it and education to appreciate it. Und um es noch schlimmer zu machen: Komplexität verkauft sich besser”.
Edsgar Wybe Dijkstra

Was also tun, wenn das Chaos schon da ist? Hier geben uns die Aufräumprofis ein paar wertvolle Schritte an die Hand. Aufräumen und los geht’s!

Schritt 1: Definiert ein Ziel

Aufräumen ist lästig, kostet Zeit und es gibt immer Besseres zu tun. Deshalb müssen wir uns Zeit dafür nehmen und gute Gründe finden, um alle Beteiligten - auch uns selbst - zu motivieren. In IT-Projekten sind das oft zusätzlich noch Projektleiter, Kunden und das restliche Entwicklerteam. Aber fragt euch trotzdem: Warum räume ich hier eigentlich auf? Wenn ich in meinem persönlichen Chaos bisher gut zurechtgekommen bin und niemanden gestört habe, dann investiere ich vielleicht sogar unnötig Zeit und Geld. Konkrete Ziele eines Refactorings könnten sein, in Zukunft Zeit und Geld zu sparen, Bausteine zur Wiederverwendung zu extrahieren oder Fehler zu vermeiden.

Schritt 2: Aussortieren

Ihr habt euch entschieden, den nächsten Schritt zu machen. Hier geht es darum, sich von allem zu trennen, was man nicht braucht oder was nicht funktional ist. Eine beliebte japanische Methode ist hier Dan-Sha-Ri (断 (dan): abschneiden, 捨 (sha): wegwerfen, 離 (ri): loslassen), die für Minimalismus und Einfachheit steht. Marie Kondo würde also zu jedem Gegenstand sagen: Frag dich selbst, ob er dich glücklich macht. Alles, was ihr mit Nein beantwortet, wird aussortiert. Wenn ihr jetzt mit eurem Team durch euer Repo geht und das macht, bleibt vielleicht kein Repo mehr übrig. Das geht also nicht ganz. Dennoch gibt es definitiv unbenutzte Dateien, auskommentierte Funktionen, unbenutzte Klassen, unnötige Kommentare usw. in unseren Repos, die nur durch das Löschen ihren Wert steigern können. Manchmal werden diese schon während der Entwicklung als Code Smells erkannt, manchmal liegen sie aber auch schon länger herum. Deshalb müssen wir regelmäßig aufräumen, anstatt ausschließlich weiterzuentwickeln.

„Nur gelöschter Code ist guter Code.”
Alte Developer-Weisheit

Aber es gibt noch eine ganz besondere Kategorie: die Sentimentalitäten. Natürlich gibt es einzelne Stücke oder ganze Kisten, von denen man sich nur schwer trennen kann. Mit denen hat man viel Zeit verbracht. Man hat besondere Erinnerungen an die Zeit, die man damit verbracht hat. Man kann sich einfach nicht davon trennen. Das ist in Ordnung. Pack sie in eine eigene Kiste und stell sie irgendwo in den Keller.

Schritt 3: Kategorisieren

„Be strategic about the stuff you want to use.”
The Home Edit

Was übrig bleibt, wollen wir in Kategorien einteilen, um es in Kisten zu packen. In unseren Softwaresystemen werden diese Kategorien oft als Module bezeichnet. Eine Architektur ist modular, wenn sie so zerlegt wurde, dass auf allen Abstraktionsebenen kohärente Einheiten entstehen, aus denen Wissenseinheiten - Chunks - gebildet werden können. Dabei ist es von Vorteil, wenn die Größenverhältnisse unserer Kategorien ausgewogen sind. Angenommen, ich möchte meine Badezimmerutensilien kategorisieren. Was würde ich dann kategorisieren?

Hier fallen den meisten ArchitektInnen jetzt ganz viele grundlegende Prinzipien ein. Sicherlich das Single Responsibility Principle, Seperation of Concerns und die Component Cohesion Principles. Außerdem Maßnahmen zur Ermittlung von Zusammenhängen und Abhängigkeiten in und zwischen einzelnen Kategorien:

  • 1. Kohäsion: ein Maß dafür, wie gut die Einzelteile einer Box zusammenpassen. Handelt es sich um Einzelteile, die sich hauptsächlich auf den jeweiligen Zweck konzentrieren?
  • 2. Kopplung: ein Maß dafür, wie stark zwei Kategorien (und mit Kategorien meinen wir: Methoden, Funktionen, Klassen, Module usw.) voneinander abhängig sind.

Im Folgenden werde ich daher nur auf zwei weitere Konzepte eingehen. Ein wichtiges Maß zur Bestimmung der optimalen Kopplung ist die Instabilität. Der Wert der Instabilität setzt sich aus den folgenden zwei Werten zusammen:

  • 1. Afferente Kopplung: wie viele andere Boxen benötigen den Inhalt einer bestimmten Box, um zu funktionieren?
  • 2. Efferente Kopplung: Wie viele andere Boxen benötigt diese Box, um zu funktionieren?

Das zweite Maß ist die Abstraktion. Abstraktionen bieten Schnittstellen, die es ermöglichen, den Code zu testen und zu modifizieren, indem Elemente hinzugefügt werden, anstatt große Änderungen vorzunehmen.

  • 1. Abstrakt: Wie viele abstrakte (nicht konkrete) Schnittstellen und mit abstrakten Schlüsselwörtern beschriftete Einzelteile oder Kisten habe ich in einem Paket?
  • 2. Konkret: Wie viele konkrete habe ich?

Es ist wichtig, die Balance zwischen ausreichender Abstraktion und Instabilität zu halten. Wenn ich wenig abstrahiere, dafür aber meine Kisten möglichst stabil sind oder jede für sich steht, befinde ich mich in der „Schmerzzone“. Im Code würde ich zum Beispiel grundlegende Tabellenfunktionen nicht abstrahieren, sondern jedes Mal neu implementieren müssen. Wenn ich jedoch zu viel abstrahiere, wird der Code unlesbar und unbrauchbar, und ich befinde mich in der „Zone der Nutzlosigkeit“.

Ein weiteres Maß ist die Connascence, eine Verallgemeinerung von Kopplung und Kohäsion. Die Connascence setzt sich aus drei Eigenschaften zusammen:

  • 1. Stärke: Hier gibt es verschiedene Formen, deren Stärke variiert.
  • 2. Grad: Wie viele Abhängige gibt es?
  • 3. Lokalität: Wie weit liegen diese voneinander entfernt?

Wie leicht eine Abhängigkeit gefunden und refaktorisiert werden kann, hängt von ihrer Form ab. Statische Formen von Constraints sind durch statische Codeanalyse leicht zu finden, während dynamische Formen wesentlich schwieriger zu refaktorisieren sind. Race Conditions oder ungünstige Ladereihenfolgen führen zu Fehlern, die oft nicht direkt zugeordnet werden können. Solche zeitlichen Abhängigkeiten sind mit bloßem Auge nicht erkennbar. Um sie zu eliminieren, könnte ich beispielsweise meine Badezimmerutensilien nach dem Zeitpunkt ihrer Verwendung kategorisieren (etwa eine Kiste „Morgenroutine“). Die Kategorisierung hängt grundsätzlich stark von den individuellen Bedürfnissen ab und ist nicht konstant.

Schritt 4: In Kisten packen

Wenn Sie nun alles wie gewünscht in Kategorien eingeteilt haben, in welche Art von Kisten packen Sie die Kategorien und wie stapeln Sie die Kisten, um den verfügbaren Platz optimal zu nutzen? In welche Art von Kiste man etwas packt, hängt davon ab, wie gut sichtbar oder leicht zugänglich der Inhalt für alle Benutzerinnen und Benutzer des Repos sein soll.

Eine Bibliothek mit allen User-Interface-Komponenten sollte beispielsweise leicht auffindbar und für jeden gut erreichbar sein.

„A place for everything, and everything in its place.”
The Home Edit

Nun wollen wir aber nicht alle Kisten im Raum stehen haben, sondern sie möglichst sinnvoll in unseren Regalen stapeln. Dabei sind die Reihenfolge und die Stapelhöhe entscheidend für die Komplexität. Die Komplexität des Systems hängt zum einen von der Anzahl der Kisten und den darin enthaltenen Teilen ab. Dies lässt sich oft nicht beeinflussen. Entscheidend sind aber auch die Stapelhöhe und die Breite der Struktur. Diese werden mit der Zeit immer größer. Dan-Sha-Ri hat dafür eine einfache „One-Touch-Rule“: Alles im System sollte mit zwei Handgriffen erreichbar sein.

Wir können Kisten nicht nur stapeln oder nebeneinander stellen, so dass jede Kiste ein Teil des Ganzen ist. Wir können Kisten auch ineinander schachteln und kleine Kisten als kategoriale Unterordnung in große Kisten packen.

Schritt 5: Etikettieren

Jetzt wo wir alles ordentlich einsortiert haben, müssen da noch Etiketten dran, damit wir später noch wissen, was in den Kisten drin ist.

“Remember your code is for a human first and a computer second. Humans need good names.”
Martin Fowler

Gute Namen sind aussprechbar, suchbar und konsistent in der Domäne. Es ist wichtig, immer nur ein Wort pro fachlichem oder technischem Konzept zu wählen und beizubehalten. Weitere gute Hinweise zur Benennung finden sich in Kapitel 4 des Buches Clean Code von Robert C. Martin.

Schritt 6: Beibehalten und pflegen

Damit kommen wir schon zum letzten Schritt. Wie können wir unsere neue Ordnung nun bewahren?

“Now I'm a pretty lazy person and am prepared to work quite hard in order to avoid work.”
Martin Fowler

1969 führte Meir M. Lehman eine empirische Studie bei IBM durch, um die Effizienz der Programmierung des Unternehmens zu verbessern. Die Studie wurde im Unternehmen kaum beachtet und hatte keine Auswirkungen auf die Entwicklungspraxis. In der Folgezeit haben jedoch einige Forscher versucht, die Gesetze der Programmentwicklungsdynamik zu ergründen. Nicht alle Gesetze konnten bisher eindeutig bestätigt werden und einige Gesetze konnten erst nach der Kommerzialisierung der untersuchten Systeme beobachtet werden. Der Wissenschaftler Pirzada kam zu dem Schluss, dass der kommerzielle Druck das Wachstum der Software begrenzt und die Verschlechterung des Systems fördert. Außerdem gelten die Gesetze nur für E-Typ-Systeme. E-Typ (evolutionäre) Programme spiegeln menschliche Prozesse oder einen Teil der realen Welt wider. Diese Art von Programmen versucht, eine Aufgabe zu lösen, die Menschen oder die reale Welt betrifft. Hier ein Auszug:

  • I. Gesetz der ständigen Veränderung: Ein System des Typs E muss ständig angepasst werden, da es sonst in der Praxis immer weniger zufriedenstellend wird.
  • II. Gesetz der zunehmenden Komplexität: Wenn ein System des Typs E verändert wird, nimmt seine Komplexität zu und es wird schwieriger, ihn weiterzuentwickeln, es sei denn, es wird daran gearbeitet, die Komplexität zu erhalten oder zu reduzieren.
  • III. Gesetz zur Erhaltung der Vertrautheit: Im Allgemeinen wird das inkrementelle Wachstum (Trend der Wachstumsrate) von Systemen des Typs E durch die Notwendigkeit, die Vertrautheit aufrechtzuerhalten, gebremst.
  • VI. Gesetz des Rückkopplungssystems: Evolutionsprozesse des E-Typs sind mehrstufige, mehrschleifige, rückgekoppelte Systeme mit mehreren Beteiligten.

Fazit

Daraus lässt sich ableiten, dass ein System nicht einfach einmal gebaut oder aufgeräumt und dann in Ruhe gelassen werden kann. Ein System muss ständig aufgeräumt werden und es müssen immer wieder wichtige Designentscheidungen getroffen werden. Wenn diese Entscheidungen hinausgezögert werden und Einzelteile wahllos hineingeworfen werden, entsteht Unordnung. Einerseits brauchen wir fest installierte „Clutter-Detektoren“ in unserer lokalen Umgebung und in unseren Pipelines: Formatter, Linter, Tests. Andererseits müssen wir Zeit zum Aufräumen einplanen.

Dan-Sha-Ri hat noch ein paar Tipps für euch:

  • Macht das Aufräumen zur Gewohnheit.
  • Wenn ihr etwas Neues baust, überlegt euch, ob ihr es nicht schon habt, und wenn ja, ersetzt das Alte komplett.
  • Überlegt genau, ob ihr die neue Lösung oder das neue Teil wirklich braucht.

Niemand erwartet von uns Systeme, die Geschichten erzählen. Aber erst Form und Funktion machen aus Chaos ein System. Zudem sollte eine gute Ordnung, die für alle NutzerInnen des Systems trägt, nicht von einer einzigen Person bestimmt werden. Gute Entscheidungen werden nur im Team getroffen.

Bild Milena Fluck

Autorin Milena Fluck

Milena Fluck ist seit 2020 Software Engineer bei adesso und verfügt über umfangreiche Projekterfahrung im Gesundheitswesen. Ihr aktueller Fokus liegt auf dem Einsatz von JavaScript und TypeScript in der Frontend- und Backend-Entwicklung. Sie bevorzugt Test Driven Development. Dabei dürfen aussagekräftige Unit-Tests natürlich nicht fehlen.

Diese Seite speichern. Diese Seite entfernen.