Ein Beitrag von Alexander Vogel

Im Jahr 2008 hat Robert C. Martin sein Buch über Clean Code „Clean Code: Refactoring, Patterns, Testen und Techniken für sauberen Code“ geschrieben und damit diesen Begriff geprägt.

Im April 2019 stellte die SAP einen Styleguide zum Clean Code für ABAP Entwickler vor, an dem sich Entwickler und Teams orientieren können. Dieser Styleguide ist eine Adaption von Robert C. Martins Buch und behandelt alle wichtigen Punkte im ABAP Umfeld.

Heute, über 2 Jahre später stellen wir uns nun die Fragen „was genau ist Clean Code?“, „sollten wir Legacy Code umbauen?“ und am wichtigsten „brauchen wir Clean Code?“. Wir wollen uns hier nun diesen Fragen widmen und uns kleine Beispiele ansehen. Also …

Was ist Clean Code?

Clean Code ist kein strenges Regelwerk, eher eine Philosophie, die sich an den folgenden Prinzipien orientiert:

/ KISS (Keep it simple, stupid)
Halte deinen Code einfach. Eine Methode sollte immer nur eine Sache machen.

/ DRY (Don’t repeat yourself)
Wiederhole deinen Code nicht. Lagere ihn in eine Methode aus, um ihn erneut zu verwenden.

/ YAGNI (You aren‘t gonna need it)
Führe zusätzliche Funktionalitäten erst ein, wenn du sie benötigst.

/ Lesbarkeit vor Prägnanz (Readability before conciseness)
Andere sollen deinen Code verstehen können – möglichst sofort!

Es empfiehlt sich, erst einmal mit einfachen Punkten zu starten, beispielsweise das KISS-Prinzip anzuwenden und Methoden auf einzelne Tätigkeiten herunterzubrechen. Reduziere die Komplexität und prüfe, ob der Code nicht einfacher darzustellen ist.

Schön… gibt’s auch ein Beispiel?

Wir schauen uns zwei Code-Ausschnitte an mit letztendlich gleichem Effekt. Der zweite Ausschnitt zeigt den Clean Code-Ansatz, der deutlich besser zu lesen ist – am anschaulichsten dargestellt in der Methode „add_mail“.

Was ändern wir im Legacy Code, abgesehen von den oben genannten Regeln, um zu Clean Code zu gelangen:

Ungarische Notation

In den 90er Jahren erfand Charles Simonyi die ungarische Notation in der Variablen mit Präfixen versehen wurden, um ihre Herkunft und den Datentyp zu zeigen. Zudem wurden Variablen möglichst kurzgehalten, um effizient für die Compiler zu sein. Deklarationen von Variablen wurden meist mit Kommentaren versehen, um auszudrücken was die Variable macht.

Da wir heutzutage Zugriff auf effizientere, schnellere und bessere Compiler haben können wir hierauf verzichten und die Variablen direkt danach benennen, was diese tun sollen.

Namensgebung - Grundregeln

Variablen:

Kleiner Geltungsbereich (Scope) -> Kurzer Variablenname

Großer Geltungsbereich -> Langer Variablenname

Klassen/Methoden

Kleiner Geltungsbereich -> Langer Name & Private

Großer Geltungsbereich -> Kurzer Name & ggf. Public

IF-Anweisungen

Die Schachtelungen von IF-Anweisungen sollten so klein wie möglich gehalten werden, um die Übersicht zu waren. Sollten mehrere Fälle von „IF / IFELSE / ELSE“ verwendet werden, sollte man auf die KISS Regel zurückgreifen und ggf. mehrere Methoden daraus extrahieren.

Feldsymbole oder Referenzen?

Dieser Punkt wird aktuell noch diskutiert, die Guidelines schlagen eine Syntax à la LOOP … REFERENCE INTO DATA(…). vor. Wenn man jedoch Feldsymbole (à la LOOP … ASSIGNING FIELD-SYMBOL(<…>).) verwendet nützt das der Laufzeit. Hier gibt es eine Verbesserung von bis zu 13%!

Schleifen vermeiden

Loops - vor allem aber Loop in Loop - sollten so gut es geht vermieden werden, da diese extremen Laufzeiten zur Folge haben. Sollte nur ein Eintrag benötigt werden, kann direkt auf die Zeile selektiert werden.

READ TABLE vermeiden

Besser als eine Schleife, um eine einzelne Zeile zu erhalten, ist READ TABLE. Noch besser ist jedoch der direkte Zugriff auf die Zeile der Tabelle. Hier ein Beispiel:

TRY. 
    DATA(row) = my_table[ key = input ].
CATCH cx_sy_itab_line_not_found.
    " Do something 
ENDTRY.

Beispiel Legacy Code

Hier nun endlich etwas Code, aber Legacy Code ist nicht wirklich „hübsch“, gut lesbar und nicht wartbar:

DATA: gt_pernr_tab TYPE TABLE OF pernr_d,
      gs_pernr     TYPE pernr_d,
      gs_pa0001    TYPE pa0001,
      gt_pa0001    TYPE TABLE OF pa0001,
      gs_pa0002    TYPE pa0002,
      gs_pa0105    TYPE pa0105.


LOOP AT gt_pernr_tab INTO gs_pernr.

  SELECT * FROM pa0001
    APPENDING TABLE gt_pa0001
    WHERE pernr EQ gs_pernr
      AND begda LE sy-datum
      AND endda GE sy-datum.

ENDLOOP.

SORT gt_pa0001 BY pernr begda.
DELETE ADJACENT DUPLICATES FROM gt_pa0001.

LOOP AT gt_pa0001 INTO gs_pa0001.

  SELECT SINGLE * FROM pa0002
    INTO gs_pa0002
    WHERE pernr EQ gs_pa0001-pernr
      AND begda LE sy-datum
      AND endda GE sy-datum.

  IF sy-subrc EQ 0.

    SELECT SINGLE * FROM pa0105
      INTO gs_pa0105
      WHERE pernr EQ gs_pa0001-pernr
        AND subty EQ '0010'
        AND begda LE sy-datum
        AND endda GE sy-datum.

    IF sy-subrc NE 0.
      MOVE-CORRESPONDING gs_pa0001 TO gs_pa0105.
      CONCATENATE gs_pa0002-vorna '.' gs_pa0002-nachn '@mail.test' INTO gs_pa0105-usrid_long.

      INSERT pa0105 FROM gs_pa0105.
    ENDIF.
  ENDIF.

ENDLOOP.

Beispiel Clean Code

Dagegen kann man Clean Code sehr gut lesen, warten und verstehen. Am besten ist es, wenn man ihn wie Prosa lesen kann. Für die Darstellung als Clean Code habe ich hier die Form einer Klasse mit Methoden gewählt:

CLASS change_mail DEFINITION FINAL CREATE PRIVATE.

  PUBLIC SECTION.
    METHODS: add_mails           IMPORTING i_personnel_numbers     TYPE pernr_tab.

  PRIVATE SECTION.
    METHODS:
      add_mail                   IMPORTING i_personnel_number      TYPE pernr_d,
      read_personal_data         IMPORTING i_personnel_number      TYPE pernr_d
                                 RETURNING VALUE(r_personnel_data) TYPE pa0002,
      does_mail_already_exist    IMPORTING i_personnel_number TYPE pernr_d
                                 RETURNING VALUE(r_exists)    TYPE boolean,
      write_new_mail_to_database IMPORTING i_personnel_data        TYPE pa0002.
ENDCLASS.

CLASS change_mail IMPLEMENTATION.
  METHOD add_mails.
    LOOP AT i_personnel_numbers ASSIGNING FIELD-SYMBOL(<personnel_number>).
      add_mail( <personnel_number> ).
    ENDLOOP.
  ENDMETHOD.

  METHOD add_mail.
    DATA(personnel_data) = read_personal_data( i_personnel_number ).

    IF does_mail_already_exist( i_personnel_number ).
      write_new_mail_to_database( personnel_data ).
    ENDIF.
  ENDMETHOD.

  METHOD read_personal_data.
    SELECT SINGLE * FROM pa0002
      INTO r_personnel_data
      WHERE pernr EQ i_personnel_number
        AND begda LE sy-datum
        AND endda GE sy-datum.
  ENDMETHOD.

  METHOD does_mail_already_exist.
    CONSTANTS: mail_subtyp TYPE subty VALUE '0010'.
    DATA: exists(1).

    SELECT SINGLE 'X' FROM pa0105
      INTO @exists
      WHERE pernr EQ @i_personnel_number
        AND subty EQ @mail_subtyp
        AND begda LE @sy-datum
        AND endda GE @sy-datum.

    IF sy-subrc EQ 0 AND exists EQ 'X'.
      r_exists = abap_true.
    ELSE.
      r_exists = abap_false.
    ENDIF.
  ENDMETHOD.

  METHOD write_new_mail_to_database.
    DATA(communication) = CORRESPONDING pa0105( i_personnel_data ).
    communication-usrid_long =
      |{ i_personnel_data-vorna }.{ i_personnel_data-nachn }@mail.test|.

    INSERT pa0105 FROM communication.
  ENDMETHOD.
ENDCLASS.

Was machen wir mit Legacy Projekten?

Die SAP empfiehlt in ihrem Styleguide für Legacy Projekte den folgenden Vier-Schritte-Plan:

  1. Holen Sie das Team an Bord. Kommunizieren und erläutern Sie den neuen Stil, und stellen Sie sicher, dass jedes Mitglied des Projektteams damit einverstanden ist. Sie müssen nicht alle Richtlinien auf einmal festschreiben. Beginnen Sie einfach mit einem kleinen unstrittigen Teilbereich und entwickeln Sie sich von dort aus weiter.

  2. Befolgen Sie in Ihrer täglichen Arbeitsroutine die Pfadfinderregel „verlasse den Campingplatz sauberer als du ihn vorgefunden hast.“ In Bezug auf Clean Code: Hinterlassen Sie den Code immer besser, als Sie ihn vorgefunden haben. Übertreiben Sie es nicht, indem Sie stundenlang „den gesamten Campingplatz aufräumen“. Wenden Sie einfach ein paar Minuten zusätzlich auf und beobachten Sie, wie sich die Verbesserungen im Zeitverlauf akkumulieren.

  3. Bauen Sie von Zeit zu Zeit saubere Inseln auf: Wählen Sie ein kleines Objekt oder eine kleine Komponente aus und versuchen Sie, dieses Objekt oder die Komponente in allen Aspekten „sauber“ zu machen. Diese Inseln demonstrieren den Nutzen von dem, was Sie tun, und bilden eine zuverlässig getestete Basis für das weitere Refactoring.

  4. Sprechen Sie drüber. Ganz gleich, ob Sie Fagan-Code-Inspektionen alter Schule aufsetzen, oder Info-Sessions bzw. Forumsdiskussionen in Ihrem bevorzugten Chat Tool veranstalten: Sie müssen über Ihre Erfahrungen und das Gelernte sprechen, damit in Ihrem Team ein gemeinsames Verständnis wachsen kann.

Wenn wir also alten Code austauschen, sollten wir das Schritt für Schritt machen und uns kleine „Inseln“ suchen, um mit der Zeit, das gesamte Coding auf Clean Code umzustellen.
Wenn man also Bugfixing betreibt, sollte man diese Stelle direkt auf Clean Code umstellen, um doppelte Arbeit zu vermeiden. Nochmal als kurze Faustregel: Bugfixing nur in Clean Code.

Also dann… brauchen wir Clean Code?

Kurze Antwort: JA!

Längere Antwort: Definitiv! Denn wir machen unseren Code dadurch lesbarer – für uns und andere. Der Code wird wartbar und wiederverwendbar und vor allem stabiler! Eine Methode behandelt, im Idealfall nur noch eine Sache und beschreibt sich selbst. Somit sind zum Beispiel Kommentare hinfällig oder bei einem Bugfix wird nicht versehentlich eine andere Stelle der Software beschädigt.

Cheat Sheet & Golden Rules

Zum Abschluss hier noch die Cheat Sheets der SAP zum Clean Code, sowie die goldenen Regeln welche die SAP dazu ausgibt. Sie sind nicht zu 100% vollständig, geben aber einen sehr guten Überblick über die Thematik. So kann sich jedes Teammitglied schnell die wichtigsten Punkte aneignen. Am besten lädt man sich das zwecks besserer Lesbarkeit direkt als Quellen herunter:

Clean ABAP Cheat Sheet

Clean ABAP the Golden Rules

Die einfachste Adresse für guten Code: SAP Gold Partner

Wie praktisch, dass wir einer sind. Unsere SAP-Division setzt sich aus hochmotivierten Expert:innen für Beratung und Entwicklung zusammen, die für unsere Kunden geilen Scheiß mit SAP SuccessFactors, HCM und 4HANA machen. Gemeinsam haben wir es geschafft, die SAP Gold Partnerschaft in nur knapp einem Jahr zu erreichen.

Wir freuen uns auf eure Ideen, Fragen und Projektvorschläge. Schickt uns einfach eine Mail an hello@camao.one.