Naming as a Process

Was ist eines der schwierigsten Probleme eines Software Engineers? Ein Problem, das täglich auftritt, ganz unabhängig von der Programmiersprache und dem verwendeten Framework. Ja, der Titel sagt es bereits: das Naming. Oder auf Deutsch: den Dingen eine guten Namen geben.

An der CraftConf 2022 bin ich auf die Firma Deep Roots aufmerksam geworden. Arlo von Deep Roots hat einen mehrstufigen Prozess definiert, um das Naming besser zu gestalten (in Arlo’s Sprache: «wie man zu Honest Names kommt»).

Ein «Honest Name» sollte:

  • aussagekräftig sein.
  • genau sagen, was ein Stück Code macht (d.h. kein Raum für Interpretationen offen lassen).
  • «ehrlich» sein, d.h. auch das tun, was der Name sagt.

Oft wird versucht, diese Dinge in einem einzigen Schritt zu tun. Dies kann zu Problemen führen. Deshalb splitten wir es auf 5 Steps (graue/blaue/grüne Farben in der Abbildung) auf.

Im Folgenden gehen wir davon aus, dass wir ein Stück von nicht verständlichem Code haben, der:

  • selbst weitere Methoden aufruft
  • oder sehr lang ist. Und wir möchten Teile davon in neue Methoden extrahieren.

Der Prozess ist auch anwendbar auf Variablen oder Expressions.

01 Use Obvious Nonsense

Delay naming until we have more information

Wir starten mit einem (auf den ersten Blick) extremen Ansatz: was wir nicht verstehen, bekommt einen Nonsense Namen. Arlo verwendet dazu immer den Namen «applesauce». Wichtig ist, dass wir direkt umbenennen, noch bevor wir versuchen den Code zu verstehen.

Macht nicht viel Sinn, oder doch? Damit erreichen wir u.a. folgendes:

  • Wir signalisieren (auch für andere Entwickler*innen), dass wir hier Code haben, den wir momentan nicht verstehen.
  • Wir entfernen Namen, die schlecht oder einfach falsch sind (und so mehr verwirren als helfen).
  • Wir splitten den Code in kleinere Stücke.

Durch das Splitten des Codes bekommen wir einen weiteren Vorteil. Wir gewinnen einen «groben» Überblick über den Code, indem wir sehen, was für Parameter in die Methoden gehen, was zurückkommt und wo/wie es weiterverwendet wird. Was «applesauce» genau ist, verschieben wir auf einen späteren Zeitpunkt, wenn wir mehr Informationen über den Code haben.

Im letzte Schritt in diesem Step machen wir einen Commit mit dem Tag «[applesauce]».

02 Make an Honest Name

State exactly what you know, and not what you interpret or assume

Wir gehen nun zum ersten Schritt, um aus «applesauce» einen besseren Namen zu gestalten:

  • Wir schaue den Code an und finden eine Sache, wo wir sicher sind, dass dies der Code auch wirklich macht.
  • Wir haben ein «OneFact» gefunden und machen ein Renaming nach dem folgenden Schema.

[LevelOfConfidence]_[OneFact]_AndStuff`

  • LevelOfConfidence ist entweder probably oder certainly. Wir starten mit probably. Falls wir später sicher sind, dass der Name den Code vollständig beschreibt, ändern wir es auf certainly.
  • OneFact ist die Sache, die wir gefunden haben.
  • Und AndStuff signalisiert, dass das Naming noch nicht abgeschlossen ist.

Und zum Schluss machen wir noch ein Commit mit dem Tag «[honest]».

03 Add a Fact

Expand the Known

Nun starten wir einen iterativen Prozess, indem wir versuchen, weitere Facts zu sammeln. Wir möchten den Schritt von «honest» zu «honest and complete» machen.

  • Wir suchen wiederum im Code nach einer Sache, bei der wir sicher sind, dass der Code dies auch tut.
  • Wir haben ein «OneAdditionalFact» gefunden und machen ein Renaming nach folgendem Schema.

[LevelOfConfidence]_[Fact1AndFact2AndFact3]_AndStuff`

Dabei sind FactN die Facts, die wir in den einzelnen Iterationen gefunden haben. Diese sind verbunden durch ein «And».

Und wann beenden wir diese Iteration? Genau dann, wenn wir genug Informationen für unseren aktuellen «Entwicklung Task» haben. Es geht nicht darum, in dieser Iteration alle Facts zu finden. Es geht um einen «honest» Namen. Für offene Dinge ist ja noch immer «AndStuff» da.

Was haben wir nun am Ende der Iteration erreicht (ausser sehr lange Namen)?

  • Der Name ist «honest and complete» in Bezug auf unseren aktuellen «Entwicklung Task».
  • Der Code muss nicht mehr gelesen werden. Alle Informationen über den Code stehen direkt im Namen.

Und zum Schluss machen wir noch ein Commit mit dem Tag «[add-fact]»

04 Refine a Question

Narrow the Unknow

In diesem Schritt möchten wir uns um «AndStuff» kümmern. Wir möchten dies ersetzen durch konkrete Dinge, die wir über den Code noch nicht wissen.

  • Wir gehen nochmals durch den Code und notieren uns Dinge, die noch offen (d.h. nicht im Namen) sind.
  • Damit können wir auch eventuelle Risiken abdecken.
  • Für jeden gefundenen Punkt machen wir ein Renaming nach folgendem Schema.

[LevelOfConfidence]_[Fact1AndFact2AndFact3]_AndUnknowThing1And
UnknowThing2

Nach diesem Schritt haben wir «complete honest» Namen. Offene Punkte und Risiken sind abgedeckt.

05 Structural Refactoring

Die «complete honest» Namen bilden eine gute Grundlage für ein Code Refactoring. Ein schöner Nebeneffekt ist, dass die Namen wieder kürzer werden. Es gibt 2 Hauptgründe, dass die Namen so lange geworden sind:

(1) Die Methode macht zuviel.
(2) Abstraktion/Encapsulation im Code könnte besser sein.

(1) Hier können wir entgegenwirken, indem wir Teile der Funktionalität einer Methode in eine neue Methode auslagern. Die Original Methode delegiert aber nicht weiter, sondern macht einfach weniger Dinge. Und der Aufruf der Original Methode muss nun 2 verschiedene (kleinere) Methoden aufrufen. Schöner Nebeneffekt: wir können den Namen verkürzen.

(2) Eventuell macht es Sinn, eine neue Abstraktion (Klassen) einzuführen und Methoden in diese neuen Klassen zu verschieben.

Fazit

Der eine oder andere Leser mag skeptisch sein, ob das Ganze Sinn macht bzw. überhaupt funktioniert. Von meiner Seite aus kann ich nur sagen: Ja, es funktioniert wirklich gut, teilweise sogar magisch! Ich habe mir die Zeit genommen und die einzelnen Schritte an einem Open Source Java Projekt ausprobiert. Meine Impressionen kann ich folgendermassen zusammenfassen:

Der wichtigste Punkt ist «applesauce». Er verhindert, dass man sich verzettelt. Wenn ich vorher versucht habe eine Methode a() zu verstehen, habe ich mich Stück für Stück durch den Code gearbeitet. Wenn a() aber selber b(), c() und d() aufruft und die wiederum e() bis x() … dann habe ich, vermutlich bevor ich bei x() angekommen bin, den Kontext bzw. Überblick verloren. Aber jetzt gebe ich der Methode a() direkt den Namen «applesauace» und kümmere mich in einem ersten Schritt nicht um b() bis x(). Wenn ich auf dem gleichen Level wie a() noch weitere Methoden habe, gebe ich denen auch den Namen «applesauce”, zusätzlich noch mit einem Index. Diese Schritte passieren rein mechanisch. Mittels den Parametern und Return Werte der «applesauce» Methoden bekomme ich schneller einen Überblick, was der Code eigentlich macht. In den nächsten Schritten erarbeitet man dann schrittweise einen besseren Namen für die «applesauce» Methoden. Und im finalen Schritt kann man von der Vorarbeit profitieren und ein Code Refactoring machen.

Ich empfehle es, im Code des aktuellen Projekts oder an einem Open Source Projekt einfach mal auszuprobieren. Optimal ist es natürlich, wenn das Projekt auch Unit Tests hat. Viel Spass beim Ausprobieren!

PS: Wer tiefer in die Materie einsteigen möchte: Arlo bietet Coaching für Teams und öffentliche Schulungen an. Zusätzlich habe ich noch ein Step-by-Step Tutorial verlinkt.

CraftConf

Deep Roots

Kommentare sind geschlossen.