für Entwickler Altlasten im Griff · ohne Vorbehalte auf die Aufgabe einzulassen. Zwar ist eine...

15
Migrieren: AngularJS-Anwendungen auf Angular 2 portieren Mainframe-Programme auf Linux betreiben Von Swift 2 auf Swift 3 umsteigen Aktualisieren: Legacy-Webanwendungen modernisieren SaaS: Vom Desktop in die Cloud Erhalten: Crashkurs COBOL Software- Maintenance Architektur- archäologie Verstehen: Strukturiert entwickeln Softwaredokumentation pflegen Altlasten im Griff Über 7 Gigabyte für Entwickler Sponsored Software: Intel Parallel Studio XE 2017 (Testversion für Windows) Multimedia: Videos, Podcasts und Literatur Tools, Sprachen und mehr: Eclipse, GCC, GNU COBOL, Spring Boot, SonarQube etc. Mit DVD Januar/ Februar 2017 Legacy-Know-how: Berichte aus dem Projektalltag

Transcript of für Entwickler Altlasten im Griff · ohne Vorbehalte auf die Aufgabe einzulassen. Zwar ist eine...

Page 1: für Entwickler Altlasten im Griff · ohne Vorbehalte auf die Aufgabe einzulassen. Zwar ist eine solche Arbeit nicht ganz so glamourös, wie ... Auch bei der Softwareentwicklung ist

Migrieren:

AngularJS-Anwendungen auf Angular 2 portierenMainframe-Programme auf Linux betreiben

Von Swift 2 auf Swift 3 umsteigen

Aktualisieren:

Legacy-Webanwendungen modernisieren SaaS: Vom Desktop in die Cloud

Erhalten:

Crashkurs COBOLSoftware- MaintenanceArchitektur-archäologie

Verstehen:

Strukturiert entwickelnSoftwaredokumentation pflegen

Altlasten im GriffÜber 7 Gigabyte

für EntwicklerSponsored Software:Intel Parallel Studio XE2017 (Testversion fürWindows)

Multimedia: Videos, Podcasts und Literatur

Tools, Sprachen und mehr:Eclipse, GCC, GNU COBOL, Spring Boot, SonarQube etc.

Mit

DVD

Januar/Februar

2017

Legacy-Know-how:

Berichte aus dem Projektalltag

Page 2: für Entwickler Altlasten im Griff · ohne Vorbehalte auf die Aufgabe einzulassen. Zwar ist eine solche Arbeit nicht ganz so glamourös, wie ... Auch bei der Softwareentwicklung ist
Page 3: für Entwickler Altlasten im Griff · ohne Vorbehalte auf die Aufgabe einzulassen. Zwar ist eine solche Arbeit nicht ganz so glamourös, wie ... Auch bei der Softwareentwicklung ist

iX Developer 2017 – Legacy-Code 3

EDITORIAL

In der schönen neuen Welt der Frameworks und Tools gibt eskaum eine Woche, in der Entwickler nicht die Gelegenheit be-kommen, etwas Neues auszuprobieren. Sei es die aktualisierteVersion eines bekannten Angebots oder ein komplett neuesOpen-Source-Projekt – den Abenteuerlustigen stehen alle Türen offen und die Augen beginnen zu leuchten, wenn sie danach von ihren Experimenten berichten können.

Im Unternehmensalltag sind allerdings schon massenhaft Res-sourcen in alte Projekte geflossen und im besten Fall hat sichüber die Jahre ein fester Kundenstamm herauskristallisiert, denman nicht vor den Kopf stoßen sollte. Ist die Situation alsonicht so verfahren, dass nur noch Neuimplementierung hilft,muss sich die Mehrzahl der Programmierer mit Altlasten, ihrerPflege und Erweiterungsoptionen beschäftigen. Dabei muss essich nicht einmal immer um Legacy-Systeme à la Mainframehandeln: Durch die kürzer werdenden Release-Zyklen, wech-selnde Entwicklungstrends und hohe Personalfluktuation kannein Projekt schon nach einer vergleichsweise kurzen Lebens-dauer so verbaut sein, dass sich ein Einstieg nur schwer findenlässt, wie besonders Projektneulinge oft schmerzlich erfahrenmüssen. In solchen Fällen besteht die einzige Lösung darin, indie Rolle des Software-Archäologen zu schlüpfen und sich ohne Vorbehalte auf die Aufgabe einzulassen.

Zwar ist eine solche Arbeit nicht ganz so glamourös, wie ständig das nächste große Ding auf den Markt zu werfen, allerdings hat auch sie ihren Reiz, wie NASA-Ingenieurin Sun Kang Matsumoto in diesem Heft zu berichten weiß. So kommt man beispielsweise viel mit Kollegen und Nutzernin Kontakt, kann helfen, für sie wichtige Arbeitsmaterialienbrauchbar zu halten, und darüber hinaus an den eigenen detektivischen Fähigkeiten arbeiten. Wer sein Ego also hintersich lassen kann und sich nicht scheut, in das Dickicht ausSpaghetti-Code hinter der Dokumentationswüste einzutauchen,findet im vorliegenden Heft Tipps und Inspiration für den Alltag und trifft auf den einen oder anderen Kameraden.

Und falls es hilft: Lässt man sich nicht von dem großen „Neu“-Sticker ablenken, fällt auch bei den spannendsten aktuellenFrameworks auf, dass vor allem Performance-Optimierungen,Umstrukturierungen und Maintenance die Release Notes be-stimmen. Wie in einem Legacy-Projekt, oder?

JULIA SCHMIDT

Für Mit-Anpacker

Page 4: für Entwickler Altlasten im Griff · ohne Vorbehalte auf die Aufgabe einzulassen. Zwar ist eine solche Arbeit nicht ganz so glamourös, wie ... Auch bei der Softwareentwicklung ist

Gut erhaltenAuch bei der Softwareentwicklung ist NachhaltigkeitTrumpf. Um aber erhalten statt wegwerfen zu können,sollte man sich ein paar Kenntnisse der Altvorderenaneignen – etwa COBOL, Fortran und JCL.

ab Seite 36

VerstehenStilSoftware vorausschauend entwickeln 8

ArchitekturAnwendungen strukturiert aufbauen 14

Dependency ManagementAbhängigkeiten in Legacy-Systemen verwaltbar machen 18

DokumentationManuels aktuell und gebrauchstauglich halten 24

LiteraturLesenswertes vor und während der Migration 28

SoftwarequalitätTechnische Schulden in Legacy-Code finden und bewerten 30

ErhaltenInterviewIm Gespräch mit Sun Kang Matsumoto, Mitarbeiterin an NASAs Voyager-Projekt 36

MaintenanceSoftwarequalität langfristig sichern 38

VorbereitungSoftwareprojekte auf strukturelle Umgestaltung vorbereiten 42

ArchitekturarchäologieAltsysteme durch schrittweise Untersuchungen verstehen 48

COBOL-KursEinführung in die Common Business Oriented Language 53

FortranEin genauer Blick auf den Formula Translator 60

Job Control LanguageBatch-Jobs in JCL programmieren 68

AktualisierenRefactoringStrategien zum langfristigen Verbessern von Quelltextstrukturen 76

TestenTestautomatisierung in Legacy-Systemen 84

WebentwicklungModernisierung von Legacy-Webapplikationen 88

AnwendungsoberflächeGUI-Redesign nach Continuous-Delivery-Prinzipien 92

4 iX Developer 2017 – Legacy-Code

INHALT | IX DEVELOPER

Besser verstehenLegacy ist nicht

gleich legacy.Manche Altsoft-ware ist vor allem

lästig, andere wert-volles Erbe, das

leicht gepflegt werdenkann. Tipps für den Weg von Garbage

zu Heritage.

ab Seite 8

Page 5: für Entwickler Altlasten im Griff · ohne Vorbehalte auf die Aufgabe einzulassen. Zwar ist eine solche Arbeit nicht ganz so glamourös, wie ... Auch bei der Softwareentwicklung ist

Gezielt aktualisierenFrüher war zwar nicht alles schlechter, aber in denletzten Jahren haben sich Verfahrensweisen und Technologien der Softwareentwicklung teils signifikantverändert. Darum lohnt es sich, bei der Migration auch gleich zu aktualisieren, aber an den richtigenStellen.

ab Seite 76

Klug migrierenAuch in diesem Jahrtausend geschriebene Softwarekann überholt sein und muss sich den Stempel„Legacy“ gefallen lassen. Vor allem geht es dabei um überholte Versionen von Programmiersprachenoder Frameworks – der Schritt von Perl 5 auf Perl 6oder Angular auf Angular 2 kann mit Fug und Rechtals Migrationsprojekt bezeichnet werden.

ab Seite 104

SaaSVom Desktop in die Cloud 96

ModularitätMicroservices – gerade in Legacy-Szenarien geeignet 100

MigrierenMigrationStrategien im Vergleich 104

SwiftUpdate für den Objective-C-Nachfolger 110

MainframeGroßrechnerprogramme mit TXSeries auf Linux betreiben 114

PerlPerl-5-Code auf Perl 6 umstellen 120

Framework-UpdateWebanwendungen von AngularJS nach Angular 2 migrieren 122

EinsetzenErfahrungsberichtMigration eines Legacy-Systems 132

PraxisMigration hochverfügbarer Applikationsserver im Live-Betrieb 136

Legacy-IntegrationAlte und neue Systeme kombiniert einsetzen 141

SonstigesEditorial 3

DVD-Inhalt 6

Impressum 6

Inserentenverzeichnis 6

iX Developer 2017 – Legacy-Code 5

Artikel mit Verweisen ins Webenthalten am Ende einen Hinweis

darauf, dass diese Webadressen auf dem Server der iX abrufbar sind.Dazu gibt man den iX-Link in der URL-Zeile des Browsers ein. Dannkann man auch die längsten Links bequem mit einem Klick ansteuern.Alternativ steht oben rechts auf der iX-Homepage ein Eingabefeld zur Verfügung.

Alle Links: www.ix.de/ix1617SSS

Page 6: für Entwickler Altlasten im Griff · ohne Vorbehalte auf die Aufgabe einzulassen. Zwar ist eine solche Arbeit nicht ganz so glamourös, wie ... Auch bei der Softwareentwicklung ist

Hinweis für Käufer der digitalen Versionen• PDF- und iPad-Version: In der iX-App finden Sie einen

Button zum Download des DVD-Images.• PDF-E-Book: Folgen Sie im Browser der unter „Alle Links“ angegebenen URL.

Alle Links: www.ix.de/ix1617006 x

6 iX Developer 2017 – Legacy-Code

SERVICE | DVD-INHALT/IMPRESSUM/INSERENTEN

Auf der Heft-DVDSponsored Software: Intel Parallel Studio XE 2017Das Softwarepaket enthält branchenführende C++- und Fortran-Compiler, Bibliotheken, High-Performance-Python-Pakete, Vektorisie-rungs- und Threading-Advisor, Speicher- und Threading-Debugger, Performance-Profiler und vieles mehr, was zum Erstellen schnellen, verlässlichen und parallelen Codes nötig ist.

Die nicht funktionsbeschränkte Test versionkönnen Sie 30 Tage lang nutzen.

Tools, Sprachen und mehrEclipse Neon, Babel,

Modernizr, Perlito, Sonar-Qube, Spring Boot, GCC,GNU COBOL

Konferenz-VideosKatie Roberts: Dismantling Monolithsusing Microservices and

Continuous Delivery atthe BBC (enterJS 2016)

Robin Böhm: AngularJS-Projekte sicher auf Angular 2

migrieren (enterJS 2016)Matt Zeunert: Effective JavaScript

Debugging with Chrome’s Developer Tools (enterJS 2016)

Marija Selakovic: Performance Issues and Optimizations in JavaScript (enterJS 2016)Bastian Spanneberg: Deployments automatisieren und testen mit Ansible und Serverspec (Continuous Lifecycle 2015)Bjørn Stachmann, René Preißel: Branch-Modelle in Git (Continuous Lifecycle 2015)Dave Farley: Acceptance Testing for Continuous Delivery (Continuous Lifecycle 2015)Dr. Thomas Schank, Max Albrecht: Fast and Resilient Integration Testing (Continuous Lifecycle 2015)Thorsten Maier: Code Quality in Practice (Continuous Lifecycle 2015)

SoftwareArchitekTOUR-Podcasts• Modularisierte Architektur für große Systeme• Architekturanalyse und -bewertung• Softwareanalyse mit Graphendatenbanken• Microservices und Self-contained Systems• Architektur-Refactoring• Architekturdokumentation• Architektur-Reviews

LiteraturLanglebige Software-Architekturen – Technische Schulden analysie-ren, begrenzen und abbauen (Buchauszug)Carola Lilienthal zeigt in ihrem Buch, welche Fehler in Softwarepro -jekten bei der Umsetzung der Architektur vermieden werden sollten undwelche Prinzipien einzuhalten sind, um langlebige Architekturen zu entwerfen oder bei bestehenden Systemen zu langlebigen Architekturenzu gelangen.

MAGAZIN FÜR PROFESSIONELLE INFORMATIONSTECHNIK

iX Developer Januar/Februar 2017 – Altlasten im GriffPostfach 61 04 07, 30604 Hannover; Karl-Wiechert-Allee 10, 30625 Hannover

Redaktion: Telefon: 0511 5352-387, Fax: 0511 5352-361, E-Mail: [email protected]

Chefredakteur: Jürgen Seeger (js) -386

Konzeption und redaktionelle Leitung:Julia Schmidt (jul) -378, E-Mail: [email protected]

Autoren dieser Ausgabe: Martin Aschoff, Jörg Basedow, Thorsten Behrens, Sebastian Eggers, Dirk Ehms, Wolfgang Engelhaupt, Rupert Friedrich, Gertrud Grünwied, Stephan Kaps, Christian Kirsch, Stefan Lieser, Carola Lilienthal, Frank Mahler, Frank Müller, Oliver Müller, Jens Ortwerth, Lisa-Leyla Öztürkoglu, Thorsten Rinne, Golo Roden, Thomas Ronzon, Julia Schmidt

Abbildungen: © Can Stock Photo Inc.: shut0 (S. 100), 3355m (S. 76), 5xinc (S. 48), Arogant (S. 114), Grushin (S. 122), Icefront (S. 92), Lagui (S. 68), Mediagram (S. 18, 104, 132), NataKu (S. 28) Penywise (S. 103), Photology75 (S. 14), RuslanOmega (S. 88), StephanieFrey (S. 60), bennyartist (S. 30, 110), cherijon (S. 53), christi180884 (S. 42), czalewski (S. 136), gavran333 (S. 38), kornienko (S. 24), kostiuchenko (S. 84), kuppa (S. 141), serkucher (S. 8), westergren (S. 120)

Redaktionsassistenz: Michael Mentzel (mm) -153, Carmen Lehmann (cle) -387

Korrektorat: Kathleen Tiede, Hinstorff Verlag, Rostock

Layout und Satz: Enrico Eisert, Matthias Timm, Hinstorff Verlag, Rostock

Titelidee: iX

Verlag: Heise Medien GmbH & Co. KG, Postfach 61 04 07, 30604 Hannover; Karl-Wiechert-Allee 10, 30625 Hannover; Telefon: 0511 5352-0, Telefax: 0511 5352-129

Geschäftsführer: Ansgar Heise, Dr. Alfons Schräder

Mitglied der Geschäftsleitung: Beate Gerold

Verlagsleiter: Dr. Alfons Schräder

Anzeigenleitung: Michael Hanke (-167), E-Mail: [email protected], www.heise.de/mediadaten/ix

Druck: Dierichs Druck + Media GmbH, Kassel

DVD-Herstellungsleitung: Klaus Ditze

Verantwortlich: Textteil: Jürgen Seeger; Anzeigenteil: Michael Hanke

iX Developer Januar/Februar 2017 – Altlasten im Griff: Einzelpreis € 12,90, Österreich € 14,20, Schweiz CHF 25,80, BeNeLux: € 14,80, Italien: € 16,80

Eine Haftung für die Richtigkeit der Veröffentlichungen kann trotz sorgfältiger Prüfung durch die Redaktion vom Herausgeber nicht übernommen werden. Kein Teil dieser Publikation darf ohne ausdrückliche schriftliche Genehmigung des Verlages verbreitet werden; das schließt ausdrücklich auch die Veröffentlichung auf Websites ein.

Printed in Germany

© Copyright by Heise Medien GmbH & Co. KG

Die Inserentenbytec www.bytec.de 148

dpunkt www.dpunkt.de 11

h.o.-COMPUTER Intel GmbH www.hocomputer.de 2

Rheinwerk Verlag www.rheinwerk-verlag.de 45

Event-AnzeigenMakerCon Make, heise Developer, dpunkt.Verlag 63

Developer Konferenzen heise Developer, iX, dpunkt.Verlag 67

JavaLand JavaLand GmbH, heise Developer 79

Kerberos – LDAP – Active Directory iX, heise Events 91

Connected Living ConnFerence 2017 Connected Living e.V., heise Events 109

Social Engineering iX, heise Events 115

Software Quality Days Software Quality Lab, iX 129

Open Source Monitoring mit Icinga 2, LibreOffice iX, heise Events 147

Die hier abgedruckten Seitenzahlen sind nicht verbindlich. Redaktionelle Gründe könnenÄnderungen erforderlich machen.

Page 7: für Entwickler Altlasten im Griff · ohne Vorbehalte auf die Aufgabe einzulassen. Zwar ist eine solche Arbeit nicht ganz so glamourös, wie ... Auch bei der Softwareentwicklung ist

iX Developer 2017 – Legacy-Code 7

VerstehenLegacy ist nicht gleich legacy. Manche Altsoftware ist vor allem lästig, andere wertvolles Erbe, das leicht gepflegt werden kann. Tipps für den Weg von Garbage zu Heritage.

Software vorausschauend entwickeln 8

Anwendungen strukturiert aufbauen 14

Abhängigkeiten in Legacy-Systemen verwaltbar machen 18

Softwaredokumentation aktuell und gebrauchstauglich halten 24

Lesenswertes vor und während der Migration 28

Technische Schulden in Legacy-Code finden und bewerten 30

Page 8: für Entwickler Altlasten im Griff · ohne Vorbehalte auf die Aufgabe einzulassen. Zwar ist eine solche Arbeit nicht ganz so glamourös, wie ... Auch bei der Softwareentwicklung ist

E in geflügeltes Wort unter Entwicklern besagt, dass schwerverständlicher Code ein erstrebenswertes Ziel sei – schließ-lich werde so deutlich, wie schwer es war, ihn zu schrei-

ben. Da Entwickler allerdings häufig in die Lage kommen, mit„fremdem“ Code arbeiten zu müssen, sollte das Streben nachhoher Verständlichkeit im eigenen Interesse liegen. Der US-ame-rikanische Informatiker Donald E. Knuth prägte in den 1980er-Jahren in dem Zusammenhang das Konzept des literarischenProgrammierensˇ[a].

Allzu oft scheitert der Vorsatz an (zumeist zeitlichen) Vorga-ben des Kunden oder des Vorgesetzten. Die Haltung, man werdenicht für schönen, sondern lediglich für funktionierenden Codebezahlt, ist weitverbreitet. Hilfreich wäre daher eine Taktik, mitder Entwickler erst gar nicht in eine Situation geraten, in der sieum zusätzliche Zeit zum Aufräumen bitten müssen.

Die Aufgabenstellung verstehen

Zunächst gilt es, das eigentliche Problem zu verstehen und zudessen Kern vorzudringen. Als Beispiel für den vorliegendenArtikel soll die Validierung einer IBAN dienen. Vermutlich ha-ben Entwickler beim Lesen der Aufgabe eine ungefähre Vorstel-lung, was zu programmieren ist. Bei genauerer Überlegung wirdjedoch klar, dass die konkreten Anforderungen gänzlich unklarsind. Vermutlich ist eine Funktion validateIban gesucht, die eineIBAN nach dem Erhalt auf Korrektheit überprüft. Allerdings er-geben sich selbst in solch einem vergleichsweise einfachen Fallzahlreiche Fragen:

• Genügt es, das Format zu kontrollieren oder ist die IBAN auchinhaltlich zu prüfen?

• Ist die Existenz der eingegeben Nummer zu klären? Eine IBANkann schließlich ein gültiges Format aufweisen und formal kor-rekt, aber nicht vergeben sein.

• Was erwartet das Programm als Ergebnis der Funktion? Sollder Rückgabewert vom Typ Boolean sein oder gibt die Funk-tion void zurück und wirft im Fehlerfall eine Ausnahme?

• Wie steht es um eine automatische Korrektur? Falls sich bei-spielsweise Zahlendreher eindeutig erkennen ließen, könntedie Funktion sie nicht automatisch korrigieren?

Die Antworten auf die Fragen wirken sich nicht nur auf die Im-plementierung, sondern auch auf die Namensgebung aus: Gibtdie Funktion beispielsweise eine korrigierte IBAN zurück, passtder Name validateIban nicht, da er nahelegt, dass ausschließlicheine Überprüfung stattfindet und die Funktion deren Ergebniszurückgibt.

Um herauszufinden, was überhaupt gewünscht ist, bietet essich an, zunächst so zu tun, als ob es die gewünschte Funktionbereits gäbe, und sich zu überlegen, wie Anwender sie benutzen.Entwickler sollten sich also in die Rolle desjenigen versetzen, derdie Funktion zukünftig verwenden muss und mit dem API-Designbeginnen statt mit der Implementierung. Den Anwender in denVordergrund zu rücken, weist einige Vorteile auf. Allen voransteht, dass das Ergebnis möglichst intuitiv benutzbar und die APIspäter nicht an die Implementierung anzupassen ist. Das Vorgehenist unter der Bezeichnung Client first Design bekannt und wirdunter anderem von Krzysztof Cwalina und Brad Abrams in ihremBuch „Framework Design Guidelines“ beschrieben [1].

8 iX Developer 2017 – Legacy-Code

VERSTEHEN | STIL

Software vorausschauend entwickeln

GebrauchsprosaGolo Roden

Wartet man lang genug, wird jeder Code Legacy. Folglichstellt sich die Frage, wie er sich

so schreiben lässt, dass er langfristigwartbar bleibt. Einer der wichtigsten Faktoren ist die Verständlichkeit: Fehltsie, lässt sich die Funktion schwer nach-vollziehen, und eine Weiterentwicklunggestaltet sich schwierig.

Page 9: für Entwickler Altlasten im Griff · ohne Vorbehalte auf die Aufgabe einzulassen. Zwar ist eine solche Arbeit nicht ganz so glamourös, wie ... Auch bei der Softwareentwicklung ist

Schnell ersichtlich ist vermutlich, dass die Eingabe als Zei-chenkette vorliegt, wobei es unterschiedliche gültige Formategibt. Beispielsweise kann „DE78 6001 0070 0946 4157 06“genauso gelten wie „de78600100700946415706“. Damit trägtman unterschiedlichen Formatierungen Rechnung, beispiels-weise in E-Mails und im Briefverkehr. Praktisch wäre es aller-dings, die IBAN in einem ersten Schritt in eine einheitlicheForm zu bringen. Naheliegend ist die Verwendung von Groß-buchstaben und das Entfernen jeglicher Leerstellen. Das Ergeb-nis der Funktion sollte für die beiden genannten Fälle daher„DE78600100700946415706“ lauten.

Passende Namen finden

Durch die Entscheidung, eine einheitlich formatierte IBAN zu-rückzugeben, entfällt allerdings die Option, einen logischenRückgabewert zu verwenden, der angibt, ob die IBAN gültig istoder nicht. Da magische Werte wie eine leere Zeichenkette zuvermeiden sind, bleibt für den Hinweis auf ungültige Eingabenlediglich der Einsatz von Ausnahmen [b]. Das bedeutet aberauch, dass der Name validateIban für die Funktion nicht mehrpassend ist, da sie nun nicht länger zum reinen Überprüfendient.

Einen ersten Hinweis auf einen Namen liefert das Anliegen,die IBAN in ein einheitliches Format zu bringen. Vorausgesetzt,dass der Großteil der Funktionsaufrufe mit gültigen Werten statt-findet, erscheint die Validierung sogar nur noch als Nebeneffekt.Der Fokus der Funktion verschiebt sich, weshalb der Name an-zupassen ist. Dass das Vorgehen durchaus berechtigt ist, lässt sichdaran ablesen, dass die Funktion Fehler als Ausnahmen behan-delt. Das sind sie auch unter der Annahme, dass der Großteil derEingaben gültig sein wird.

Als Funktionsname bietet sich daher etwas wie ibanUniforman. Allerdings lässt sich an der Stelle ein Antipattern ablesen:Da eine Funktion eine Tätigkeit darstellt, sollte sie mit einemVerb benannt sein. Einheitliche Formatierung bekommt häufigdie Bezeichnung „kanonisch“, weshalb sich der Name canoni-calizeIban anbietet. Er gibt deutlich an, welchen Zweck dieFunktion erfüllt: Eine IBAN in eine kanonische Form zu brin-gen, wobei klar ist, dass das für ungültige Eingaben nicht funk-tionieren kann, was zu Ausnahmen führt.

Mancher mag einwenden, dass das alles vorgegeben seinmüsste, schließlich sind Produktverantwortliche oder Architek-ten mit Derartigem betraut. In vielen Fällen steht aber niemandzur Verfügung, der jegliche Entscheidung auf der Detailebenetreffen kann. Insofern müssen Entwickler wohl oder übel selbstnachdenken.

Code erzählt eine Geschichte

1986 stellte Jon Bentley in „Communications of the ACM“ dieFrage, wann man das letzte Mal einen gemütlichen Abend da-mit verbracht habe, ein gutes Stück Code zu lesen [c]. Vermutlichhaben wenige Entwickler jemals ein solches Vergnügen genos-sen. Allerdings wirft die Frage einen interessanten Gedankenauf: Wenn sich die Funktionsweise von Code von einem Men-schen erklären lässt, müsste sich Code nicht selbsterklärend for-mulieren lassen? Schließlich handelt es sich in beiden Fällen umSprache, wenn im zweiten Fall auch um eine abstrahierte Form.

Abwegig ist die Idee nicht, denn das Prinzip des Single Levelof Abstraction fordert letztlich nicht anderes: Code, der sich ineiner Funktion befindet, sollte einen gemeinsamen fachlichen

Detailgrad aufweisen. Es wäre nach dem Prinzip falsch, in einerFunktion sowohl auf ein Bitfeld zuzugreifen als auch die Aus-gabe für den Anwender aufzubereiten. Beides bewegt sich aufunterschiedlichen Abstraktionsniveaus.

Außerdem würde das Vorgehen das Single Responsibility Prin-ciple verletzen, das besagt, dass sich eine Codeeinheit lediglichum eine Aufgabe kümmern soll. Das Manipulieren eines Bitfeldsund das Aufbereiten einer Bildschirmausgabe stehen (höchstwahr-scheinlich) in keinem fachlichen Zusammenhang und sind daherzu trennen.

Bis jetzt ist die Signatur der Funktion bekannt. Sie erwarteteine Zeichenkette, gibt eine zweite zurück und löst gegebenen-falls Ausnahmen aus. Versucht man, das Grundgerüst als Java -Script-Code aufzuschreiben, ergibt sich direkt das nächste Pro-blem mit dem Parameternamen:

const canonicalizeIban = function (iban) {// ...

};

Nennt man ihn iban, impliziert das, dass es sich um eine (vali-dierte) IBAN handelt. Theoretisch könnte der Parameter aller-dings eine ungültige Zeichenkette wie foobar enthalten. AlsAusweg bietet sich der Name text an, der allerdings zu allge-mein ist und lediglich eine andere Bezeichnung für string dar-stellt. Jegliche fachliche Information ginge verloren. Besser wä-re an der Stelle potentialIban, da der Name impliziert, dass essich wahrscheinlich um eine IBAN handelt. Eine gangbare Al-ternative könnte ibanCandidate sein. Damit sieht der Funktions-rahmen wie folgt aus:

const canonicalizeIban = function (potentialIban) {// ...

};

Denkt man nun über den Funktionsrumpf nach, könnte man di-rekt nachschlagen, wie der Algorithmus zum Validieren einerIBAN funktioniert [d]. Im Zuge dessen würde man erfahren,dass eine IBAN eine Prüfsumme enthält, die sich zum Validie-ren berechnen lässt [e]. Schreibt man den Algorithmus danachdirekt in einem Rutsch auf, entsteht Code wie in einem entspre-chenden Beispiel auf StackOverflow: fachlich vermutlich kor-rekt, aber unverständlich und nicht ohne größere Mühe nach-vollziehbar [f].

Erinnert man sich hingegen an den Wunsch, dass Code eineGeschichte erzählen soll, hilft das folgende Gedankenspiel: An-genommen, ein Bekannter ruft an und fragt, woran eine gültigeIBAN zu erkennen sei. In dem Fall wäre die erste Frage vermut-lich, ob die fraglich Zeichenkombination überhaupt wie eineIBAN aussieht. Erst danach lohnt es sich, überhaupt im Detailüber das Format und die Prüfsumme nachzudenken. Eventuell

iX Developer 2017 – Legacy-Code 9

const canonicalizeIban = function (potentialIban) {if (!potentialIban) {throw new Error('Potential IBAN is missing.');

}

const canonicalizedIban = canonicalize(potentialIban);

if (!looksLikeIban(canonicalizedIban)) {throw new Error('IBAN looks wrong.');

}

if (!isValidIban(canonicalizedIban)) {throw new Error('IBAN is invalid.');

}

return canonicalizedIban;};

Listing 1: canonicalizeIban

Page 10: für Entwickler Altlasten im Griff · ohne Vorbehalte auf die Aufgabe einzulassen. Zwar ist eine solche Arbeit nicht ganz so glamourös, wie ... Auch bei der Softwareentwicklung ist

ist es empfehlenswert, die Eingabe zunächst in ihre kanonischeForm zu bringen. Überträgt man die Überlegung auf die zuschreibende Funktion canonicalizeIban, erhält man den in Lis-tingˇ1 gezeigten Code.

Verständlicher Code

Obwohl der Code in der Form nicht läuft, ist er vom fachlichenStandpunkt aus durchaus verständlich. Zudem ist er kurz undkompakt und enthält keine Überraschungen. Alternativ hätteman den Code wie in Listingˇ2 schreiben können.

Auf den ersten Blick fällt unter anderem auf, dass der Codedurch Abkürzungen deutlich schlechter lesbar ist. So geben etwadie Variablennamen pot und can ohne Kontext wenig Aufschlussüber ihre Funktionen. Zwar erfordern die ausgeschriebenen Va-rianten mehr Schreibarbeit, Lesern fällt die Übersetzung aller-dings leichter. Darüber hinaus verschlechtert die mehrfache Ver-schachtelung die Lesbarkeit.

Um das zu vermeiden, ist es hilfreich, auf else zu verzichtenund statt des Positivfalls die Negativfälle zu testen. Zudem hel-fen Leerzeilen, die Struktur des Codes besser zu erkennen, wieder direkte Vergleich der beiden Listings zeigt.

Aus all dem lassen sich einige Regeln ableiten, die größten-teils in „Framework Design Guidelines“ enthalten sind. Dazuzählen das Verbot von Abkürzungen, der Fokus auf eine mög-lichst geringe Einrückung und kurze, in sich abgeschlosseneEinheiten mit wenig Komplexität.

Als nächstes ist die Funktion canonicalize zu schreiben. Inihr sind alle Zeichen außer den alphanumerischen zu entfernenund der Ausdruck in Großbuchstaben umzuwandeln. Den An-forderungen nach scheint der Einsatz eines regulären Ausdruckseine gute Wahl (Listingˇ3).

Obwohl der Code technisch korrekt ist, geht durch die direkteImplementierung des regulären Ausdrucks die Information überdie Intention verloren, alle Zeichen außer die alphanumerischenzu entfernen. Das führt dazu, dass andere Entwickler beziehungs-weise der Verfasser selbst nach einigen Monaten nur versuchenkönnen, aus der Implementierung auf die Intention zu schließen.Es lässt sich nicht mehr überprüfen, ob die ursprüngliche fach-liche Absicht technisch korrekt implementiert wurde. Lagert mandie Aufgabe jedoch in eine eigene Funktion aus, wird der Codenicht nur lesbarer, sondern es bleibt auch die Intention erhalten(Listingˇ4).

Modulare Umsetzung

Die Funktion keepAlphanumeric lässt sich nun vergleichweiseeinfach implementieren:

const keepAlphanumeric = function (text) {return text.replace(/[^A-Za-z0-9]/g, ,');

};

An der Stelle empfiehlt es sich, den Parameternamen von po-tentialIban auf text zu ändern, da die Funktion so allgemein undgenerisch ist, dass sie für jeden beliebigen Text funktioniert. Sieließe sich sogar in ein kleines eigenständiges Modul auslagern.Auf den ersten Blick mag das übertrieben erscheinen, allerdingserspart man künftigen Bearbeitern dadurch den Aufwand, einenpassenden regulären Ausdruck zu implementieren, zu testen, zudokumentieren und so weiter.

Genau den Ansatz verfolgt die Node.js-Community, indemsie ausgesprochen kleine Module entwickelt und veröffentlicht,die sich je nach Bedarf wie Bausteine zusammensetzen lassen.Da das Modul limit-alphanumeric die gewünschte Aufgabe er-ledigt, lässt sich der Aufruf entsprechend anpassen [g].

Auch die Funktion looksLikeIban lässt sich auf die Art im-plementieren, sofern man das Format auf das deutscher IBANsbeschränkt:

const looksLikeIban = function (potentialIban) {return /^[A-Z]{2}[0-9]{20}$/.test(potentialIban);

};

Bemerkenswert ist, wie kurz die einzelnen Funktionen jeweilssind. Zudem sind Intention und Implementierung vergleichswei-se verständlich. Das Muster lässt sich nach Belieben fortsetzen,wobei der Kern stets darin besteht, Intention und Implementie-rung sauber zu trennen. Erstere drückt den Funktionsnamen undLetztere den Funktionsinhalt aus. Beim Verletzen des Prinzipsund zu starkem Fokus auf der Implementierung lässt sich derCode schwieriger nachvollziehen.

Und nicht nur das: Durch die Kürze und Einfachheit der ein-zelnen Funktionen ist es ein Kinderspiel, sie zu testen und zudokumentieren. Vergleicht man sie mit dem Code aus der er-wähnten Frage auf StackOverflow, wird der Unterschied beson-ders deutlich [f].

Komplexere Logik implementieren

Das Schöne an der bislang gezeigten Vorgehensweise ist, dasssie auch für komplexere Logik funktioniert, wie sich am Beispielder noch umzusetzenden Funktion isValidIban zeigen lässt. IhreAufgabe besteht darin, die Landeskennung einer IBAN zu prüfenund anschließend die Prüfsumme zu berechnen. Was zunächstaufwendig klingt, lässt sich – wendet man die Vorlage der tele-

10 iX Developer 2017 – Legacy-Code

VERSTEHEN | STIL

const canonicalizeIban = function (pot) {if (potentialIban) {const can = canonicalize(pot);if (looksLikeIban(can)) {if (isValidIban(can)) {return can;

} else {throw new Error('IBAN is invalid.');

}} else { throw new Error('IBAN looks wrong.');

}} else {throw new Error('Potential IBAN is missing.');

}};

Listing 2: Alternative Fassung von canonicalizeIban

const canonicalize = function (potentialIban) {if (!potentialIban) {throw new Error('Potential IBAN is missing.');

}

return potentialIban.replace(/[^A-Za-z0-9]/g, '').toUpperCase();

};

Listing 3: canonicalize

const canonicalize = function (potentialIban) {if (!potentialIban) {throw new Error('Potential IBAN is missing.');

}

return keepAlphanumeric(potentialIban).toUpperCase();};

Listing 4: canonicalize potentialIban

Page 11: für Entwickler Altlasten im Griff · ohne Vorbehalte auf die Aufgabe einzulassen. Zwar ist eine solche Arbeit nicht ganz so glamourös, wie ... Auch bei der Softwareentwicklung ist

fonischen Erklärung an – kompakt umsetzen. Im ersten Schrittgilt es, die IBAN in ihre einzelnen Bestandteile zu zerlegen. Da-nach lässt sich die Landeskennung leicht durch eine gesonderteFunktion überprüfen. Auch das Berechnen der Prüfsumme isteinfach: Die Bestandteile der IBAN sind auf eine bestimmte Artzu addieren und im Anschluss eine Division mit Rest durch 97(da die Prüfsumme zwischen 2 und 98 liegen kann) durchzufüh-ren. Ist der Rest gleich 1, stimmt die Prüfsumme.

Auffällig ist, dass die Funktion isValidIban wie looksLike -Iban die Regel verletzt, dass eine Funktion mit einem Verb be-nannt sein soll. Der Grund hierfür ist, dass die beiden Funktio-nen als logische Entscheider fungieren: Sie erledigen keineAufgabe, sondern geben lediglich einen Wahrheitswert als Er-gebnis zurück. Daher ist es sinnvoll, ihre Namen als logischeAussagen zu formulieren. Häufig beginnt der Name solcherFunktionen mit is, can oder has. Wie looksLikeIban zeigt, lässtsich davon aber abweichen, wenn es sich anbietet und die Ent-wickler das grundlegende Konzept weiterhin verfolgen.

Im Übrigen ist isValidIban die erste Funktion, bei der der Ein-satz eines externen Moduls von Vornherein sinnvoll ist. Da dasRechnen mit den einzelnen IBAN-Bestandteile den Werte -bereich von Zahlen in JavaScript unter Umständen übersteigt,benötigt man ein Modul wie big-integer zum Rechnen mit gro-ßen Werten (siehe Listingˇ5).

Die übrigen Funktionen sind schnell geschrieben. Aufgabevon getIbanDetails in Listingˇ6 ist es, die IBAN zu zerlegen.

Da die Landeskennung als Zeichenkette eine numerischeDarstellung besitzt, ist zudem eine Funktion getNumericIban-CountryCode nötig, die ihrerseits wiederum vergleichweise ein-fach gehalten ist:

const getNumericIbanCountryCode = function (countryCode) {return ,' +(countryCode.charCodeAt(0) - 55) +(countryCode.charCodeAt(1) - 55);

};

Zuletzt ist eine Funktion isValidCountryCode zu implementie-ren, deren Aufgabe darin besteht, in einer Liste nachzuschlagen,ob die angegebene Landeskennung existiert. Eine derartige Listesteht im npm-Modul country-list zur Verfügung, die Implemen-tierung lässt sich daher mit wenig Code erledigen [h]:

const isValidCountryCode = function (potentialCountryCode) {return countryList().getCodes().includes(potentialCountryCode);

};

Eine zentrale Erkenntnis an der Stelle ist, dass sich jeglicheKomplexität beherrschbar und überschaubar gestalten lässt, in-dem man sie in kleinere Einheiten zerlegt. Selbstverständlichentstehen auf dem Weg viele kleine Funktionen, aber im Gegen-satz zu einer einzigen großen mit prinzipiell gleichem Inhaltmuss man sie nicht auf einen Schlag lesen und verstehen. Jenach Interesse können Entwickler gezielt an der einen oder an-deren Stelle in die Details eintauchen, indem sie einen Funk -tionsaufruf auflösen. Das Vorgehen arbeitet vor allem mit derIdee, dass Funktionen ein Abstraktionsmittel darstellen, das dazudienen kann, Implementierung zu verbergen und durch Intentionzu ersetzen.

Deklarativ, nicht imperativ

Das alles erinnert stark an die sogenannte deklarative Program-mierung, in der Entwickler das gewünschte Ergebnis formulie-ren statt den Weg dorthin, wie es bei einem imperativen Ansatzder Fall ist. Daher haben sich für deklarative und imperative

iX Developer 2017 – Legacy-Code

Page 12: für Entwickler Altlasten im Griff · ohne Vorbehalte auf die Aufgabe einzulassen. Zwar ist eine solche Arbeit nicht ganz so glamourös, wie ... Auch bei der Softwareentwicklung ist

Programmierung die Bezeichnungen Was- und Wie-Program-mierung eingebürgert.

Selbst auf dem Niveau einer einzelnen Schleife lässt sich derUnterschied zwischen beiden Herangehensweisen hervorragendveranschaulichen. Ist beispielsweise für eine Liste von Zahlendie Summe der zugehörigen Quadratzahlen zu berechnen, lässtsich das deklarativ elegant durch den Einsatz von map und re-duce erledigen, indem lediglich das gewünschte Ergebnis be-schrieben wird:

const sum = [ 1, 2, 3, 4, 5 ].map(n => n * n).reduce((sum, n) => sum + n);

Die imperative Implementierung in Listingˇ7 wirkt hingegenaufwendig, da beispielsweise anzugeben ist, wie man durch dasArray der Zahlen iterieren möchte. Der nun längere Code enthältDetails, die für die eigentliche Aufgabe nicht relevant sind. Soist beispielsweise die Existenz der Zählvariablen i der Tatsachegeschuldet, dass eine for-Schleife eine Laufvariable braucht. Inder eigentlichen Anforderung kommt i hingegen nicht vor, eshandelt sich folglich um ein technisches Artefakt, das durch-scheint, weil das Abstraktionsniveau des Codes aus fachlicherSicht nicht hoch genug ist.

Außerdem ergibt sich bei der deklarativen Variante ein ange-nehmer Nebeneffekt: Da die Implementierung nicht vorgibt, wiedie Liste der Zahlen zu durchlaufen ist, könnte die Laufzeitum-gebung entscheiden, das in einer anderen Reihenfolge zu erle-digen als in der imperativen Version, beispielsweise rückwärtsoder durcheinander. Das kann sinnvoll sein, wenn das Iterieren

rückwärts durch eine Schleife performanter ist als vorwärts. Mitder gleichen Begründung wäre sogar denkbar, dass die Laufzeit-umgebung die Ausführung von map und reduce parallelisiert,was für die for-Schleife hingegen zumindest nicht ohne aufwen-dige Analyse des Codes und gegebenenfalls Unterstützung durchden Entwickler möglich ist. Programmierer Joel Spolsky be-schreibt diesen Effekt in seinem Blogeintrag „Can Your Program-ming Language Do This?“ [i].

Simpel versus komplex

Letztlich läuft alles auf den Wunsch hinaus, Komplexität zu ver-meiden und simplen Code zu schreiben. Zwar fordert das auchdas weitverbreitete KISS-Prinzip, das besagt, dass Aufgabendurch eine möglichst einfache Lösung zu erfüllen sind. Aller-dings stellt sich in dem Zusammenhang die Frage, was der Be-griff „einfach“ genau meint. Im Englischen lässt sich etwas bes-ser zwischen simple und easy unterscheiden, im Deutschenwerden die Wörter simpel und einfach allerdings häufig gleich-gesetzt.

Das führt zu der (falschen) Annahme, dass triviale Lösungen,die man ohne großes Nachdenken erreichen kann, stets vorzu-ziehen seien. Mit dem Argument retten sich Programmierer häu-fig, wenn es darum geht, vermeintlich exotische Sprachkon-strukte wie Lambda-Ausdrücke oder Entwurfsmuster zu meiden.Oftmals gipfelt das beispielsweise in Code, der aus endlosen if-else-Kaskaden besteht, statt das wesentlich elegantere und pfle-geleichtere Strategy-Entwurfsmuster zu nutzen.

Um dem Problem aus dem Weg zu gehen, lassen sich abervier konkretere Forderungen stellen, die greifbarer als das KISS-Prinzip sind, und das sogenannte CUTE-Prinzip ausmachen:• Comprehensive: Code sollte verständlich im Hinblick auf die

eigentliche Problemstellung und die Domäne sein und mög-lichst wenig technische Konstrukte enthalten, die lediglichwegen einer fehlenden Abstraktion erforderlich sind. Zudemsind die fachlichen Konzepte explizit auszudrücken, um dieIntention einzufangen und zu bewahren. Die Implementierungrückt dadurch in den Hintergrund, sodass sich aus dem Codebesser ergibt, warum er geschrieben wurde und welchen Zwecker verfolgt.

• Unidimensional: Code sollte eine niedrige Komplexität auf-weisen und nach Möglichkeit auf Verschachtelung, Schleifenund ähnliches verzichten. Je linearer sich der Lesefluss ge-stalten lässt, desto besser. Praktischerweise lässt sich die For-derung durch den Einsatz gängiger Metriken stützen – diezyklomatische Komplexität oder der Halstead-Index etwa[2], [3].

• Terse: Code sollte kurz, kompakt und auf den Punkt gebrachtsein. Weniger Code lässt sich nicht nur schneller lesen, nach-vollziehen und verstehen, sondern er enthält auch wenigerFehler als langer. Außerdem fallen Code-Reviews leichter undTests sind schneller geschrieben, da weniger Code zu testenist. Ein gutes Hilfsmittel besteht im Auslagern logischer Funk-tionseinheiten in eigene Funktionen mit passendem Namen.

• Elegant: Code sollte im mathematischem Sinne „schön“ seinund passende Sprachkonstrukte verwenden. Beispielsweise sinddie Funktionen map und reduce häufig nicht bekannt oder gän-gig, gehören aber zum Alltag eines jeden funktionalen Ent-wicklers. Es handelt sich also nicht um exotische Sprachkon-strukte, für die es entschuldbar wäre, sie nicht zu kennen.

Insbesondere die letzte Forderung ist schwer zu konkretisieren,da Eleganz per se im Auge des Betrachters liegt. Sie sollen aller-dings auch keine strikten, messbaren Regeln darstellen, sondern

12 iX Developer 2017 – Legacy-Code

VERSTEHEN | STIL

const getIbanDetails = function (iban) {return {countryCode: iban.substr(0, 2),countryCodeNumeric: getNumericIbanCountryCode(iban.substr(0, 2)),checkSum: iban.substr(2, 2),bankCode: iban.substr(4, 8),accountNumber: iban.substr(12, 10)

};};

Listing 6: getIbanDetails

const numbers = [ 1, 2, 3, 4, 5 ];

let sum = 0;

for (let i = 0; i < numbers.length; i++) {const square = numbers[i] * numbers[i];

sum += square;}

Listing 7: Quadratzahlenberechnung imperativ implementiert

const isValidIban = function (potentialIban) {if (!potentialIban) {throw new Error('Potential IBAN is missing.');

}

const potentialIbanDetails = getIbanDetails(potentialIban);

if (!isValidCountryCode(potentialIbanDetails.countryCode)) {return false;

}

const dividend = bigInteger(potentialIbanDetails.bankCode +potentialIbanDetails.accountNumber +potentialIbanDetails.countryCodeNumeric +potentialIbanDetails.checkSum);

const divisor = 97;

return dividend.mod(divisor).value === 1;};

Listing 5: potentialIban

Page 13: für Entwickler Altlasten im Griff · ohne Vorbehalte auf die Aufgabe einzulassen. Zwar ist eine solche Arbeit nicht ganz so glamourös, wie ... Auch bei der Softwareentwicklung ist

vielmehr einen Leitfaden bieten, an dem man sich in die richtigeRichtung entlanghangeln kann.

Fazit

Verständlicher Code ist mit vertretbarem Aufwand erreichbar.Das beschriebene Vorgehen gibt einige Richtlinien an die Handund zeigt, wie sich die Ideen an einem konkreten Beispiel um-setzen lassen. Es lässt sich auf andere Probleme aus der Praxisübertragen, die weitaus komplexer sind. Das Muster ist stetsdas gleiche: Komplexität muss zerlegt werden, Intention istwichtiger als Implementierung. Wer diesen beiden Grundge-danken folgt, kann gut les- und wartbaren Code schreiben, oh-ne dafür gesondert um Erlaubnis bitten zu müssen.

Allerdings erfordert das Vorgehen einiges an Disziplin.Nicht nur, dass Entwickler sich regelmäßig daran erinnern müs-sen, wie wichtig die Intention ist, auch die Suche nach passen-den Namen ist unter Umständen eine langwierige Aufgabe. Ab-kürzungen zahlen sich selten aus, viel mehr bergen sie häufigzusätzliche Kosten – entweder weil immer wieder über Sinnund Zweck des Codes nachzudenken ist oder weil der Aufräum-aufwand später um vieles höher ist als der initiale Entwicklungs-aufwand. (jul)

Literatur[1] Krzysztof Cwalina, Brad Abrams; Framework Design

Guidelines: Conventions, Idioms, and Patterns for Reusable.NET Libraries; Addison-Wesley Professional, 2008

[2] Thomas McCabe, A Complexity Measure, IEEE Transactionson Software Engineering; December 1976

[3] Maurice Howard Halstead, Elements of software science; Elsevier, 1977

Golo Rodenist Gründer, CTO und Geschäftsführer der the

native web GmbH, ein auf native Webtechnologien

spezialisiertes Unternehmen. Für die Entwicklung

moderner Webanwendungen bevorzugt er JavaScript

und Node.js und hat mit „Node.js & Co.“ das erste

deutschsprachige Buch zum Thema geschrieben.

iX Developer 2017 – Legacy-Code

[a] Literarisches Programmieren wikipedia.org/wiki/Literate_programming[b] Magische Zahlen wikipedia.org/wiki/Magische_Zahl_(Informatik)[c] Programming pearls: a literate program dl.acm.org/citation.cfm?id=315654[d] IBAN berechnen iban.de/berechnung_iban.html[e] IBAN-Prüfsumme iban.de/iban-pruefsumme.html[f] IBAN-Validierung auf StackOverflow stackoverflow.com/questions/21928083/iban-validation-check[g] Modul limit-alphanumeric npmjs.com/package/limit-alphanumeric[h] Modul country-list npmjs.com/package/country-list[i] Can Your Programming Language Do This? joelonsoftware.com/items/2006/08/01.html

Onlinequellen

Alle Links: www.ix.de/ix1617008 x

Page 14: für Entwickler Altlasten im Griff · ohne Vorbehalte auf die Aufgabe einzulassen. Zwar ist eine solche Arbeit nicht ganz so glamourös, wie ... Auch bei der Softwareentwicklung ist

D ie Frage, wie sich die Codearchitektur strukturieren lässt,scheint zunächst nicht einfach zu beantworten. Zu unter-schiedlich sind Anwendungen, zu differenziert deren je-

weilige Umgebungen, als dass es das eine pauschale Vorgehens-muster gäbe. Selbstverständlich gibt es Richtlinien, an denenman sich orientieren kann, allerdings beschreiben sie stets nureinen Ausschnitt des Gesamtbildes.

Elementar für die Entwicklung moderner Web- und Cloud-Angebote sind in dem Kontext die als 12 Factor Apps bekanntenRegeln des Cloud-Dienstleisters Heroku [a]. Sie sind im Fol-genden mit 12FA abgekürzt. Ohne auf all ihre Aspekte im Detaileingehen zu wollen, zeigen die Empfehlungen rasch, in welcheRichtung man gedanklich gehen muss: So sind insbesondere dieForderung nach statuslosen Prozessen zentral, die sich schnellstarten und sauber beenden lassen. 12FA verlangt das expliziteModellieren sämtlicher Abhängigkeiten – sei es mittels Paket-verwaltung oder als externe, über eine Konfiguration beschrie-bene und angeforderte Dienste. Ein so entstandenes Modell isteine Grundlage für den Einsatz von Microservices (siehe dazuauch S. 100).

Auf den ersten Blick scheinen Letztere lediglich eine neueBezeichnung für das zu sein, was vor rund 15 Jahren der BegriffSOA beschrieb, nämlich Webservices. Tatsächlich geht der Ge-danke von Microservices aber weit über die Ideen der Service-oriented Architecture beziehungsweise REST (RepresentationalState Transfer) hinaus, denn er beschreibt Details wie die Infra-struktur und den Umgang mit ihr als Teil des Services.

Der englische Entwickler Martin Fowler beantwortet die Fra-ge nach einer Erklärung der Microservice wie folgt [b]:

„In short, the microservice architectural style is an approach todeveloping a single application as a suite of small services, eachrunning in its own process and communicating with lightweightmechanisms, often an HTTP resource API. These services arebuilt around business capabilities and independently deployableby fully automated deployment machinery. There is a bare mi-nimum of centralized management of these services, which maybe written in different programming languages and use differentdata storage technologies.“

Unabhängig von dieser Definition gibt es den Ansatz, vomeigentlichen Wort auszugehen. Ein Service ist eine unabhängige,isolierte Einheit, die eine bestimmte Aufgabe für ihre Umge-bung erfüllt. Sie agiert dabei autonom vom Rest ihrer Umweltund ist idealerweise statuslos. Wenn man so will, ist eine Funk-tion die kleinste denkbare Einheit eines Service.

Alle bislang genannten Bedingungen erfüllt beispielsweisedie in nahezu jeder Plattform verfügbare Funktion zur Berech-nung des Sinus: Sie ist unabhängig, isoliert vom Rest der Platt-form lauffähig, agiert autonom, ohne Abhängigkeit von anderenFunktionen, und ist üblicherweise statuslos implementiert. Den-noch spricht man in dem Fall von einer Funktion und nicht voneinem Service oder Dienst.

Dienste sind eigenständige Prozesse

Der Grund dafür ist, dass Dienste eigenständige Prozesse dar-stellen. Eine Funktion hingegen kommt zusammen mit vielenweiteren Funktionen in seinem Kontext zum Einsatz. Würde

14 iX Developer 2017 – Legacy-Code

VERSTEHEN | ARCHITEKTUR

Anwendungen strukturiert aufbauen

Meisterlich konstruiertGolo Roden

Für die Struktur von Code in der objektorientierten Programmierung (OOP) eignetsich der Einsatz der SOLID- Prinzipien. Sie geben fünfgrundlegende Regeln vor, an denen sich Entwickler beim Entwurf von Quelltexten orientieren sollen. Doch wiesteht die Sache bei der Anwendungsarchitektur?

Page 15: für Entwickler Altlasten im Griff · ohne Vorbehalte auf die Aufgabe einzulassen. Zwar ist eine solche Arbeit nicht ganz so glamourös, wie ... Auch bei der Softwareentwicklung ist

man die Funktion als eigenständigen Prozess modellieren, ließesich tatsächlich von einem Dienst reden.

Der Begriff micro ist dagegen nicht so klar umrissen, aller-dings macht er klar, dass es um eine kleine Einheit geht. Mitklein geht üblicherweise der Begriff fokussiert einher, dennwenn sich Entwickler auf wenig beschränken müssen, lassensich ohnehin nicht mehr zahlreiche Themen zugleich angehen.

Zusammengefasst beschreibt der Gedanke von Microservicesalso kleine, eigenständige Einheiten, die isoliert und unabhängigvoneinander arbeiten und sich auf eine einzige Aufgabe konzen-trieren. Das erinnert ein bisschen an das Konzept der Lego-Bau-steine, die für sich genommen klein sind und nur eine einzigeAufgabe erfüllen. In beiden Fällen entsteht das eigentliche Po-tenzial aus der Kombinierbarkeit der Elemente.

SOLID für Architektur

Schlägt man den Bogen zu den aus der objektorientierten Weltbekannten SOLID-Prinzipien (siehe Kasten „SOLID im Über-blick“), fällt auf, dass sich einige von ihnen hervorragend auf denArchitekturansatz übertragen lassen. Am stärksten gilt das ver-mutlich für das S, also das Single Responsibility Principle (SRP).Es besagt, dass jede Klasse nur eine einzige Verantwortung be-ziehungsweise Aufgabe haben sollte. Der US-amerikanische In-formatiker Robert C. Martin, unter anderem Autor des Buches„Clean Code“, hat das Prinzip wie folgt umrissen: „There shouldnever be more than one reason for a class to change.“

Der Satz lässt sich auf Dienste übertragen: Sie sollten nur ei-ne einzige Verantwortlichkeit im fachlichen Sinne haben undsich nur ändern, wenn es ihre fachliche Kernkompetenz betrifft.Insofern gilt SRP auch für Dienste und ist ein erster Indikatordafür, wie Software zu schneiden sein könnte, um einen Mono-lithen in kleinere Einheiten zu zerlegen.

Der Fokus auf den fachlichen Aspekten spielt bei all dem ei-ne zentrale Rolle, da ein Dienst primär eine fachliche Einheitdarstellt und erst sekundär eine technische. Genau das meintauch Martin Fowler, wenn er schreibt: „services are built aroundbusiness capabilities.“

SRP allein genügt jedoch nicht. Ein weiterer Grund fürschlechten, unüberschaubaren und damit langfristig kaum wart-baren Code ist die typische Redundanz, die man in vielen Pro-jekten vorfindet. Das dafür gültige Rezept ist nicht Bestandteilder SOLID-Prinzipien und heißt „Don’t Repeat Yourself“(DRY). Es besagt, dass man immer, wenn man etwas zum zwei-ten Mal umsetzt, überlegen sollte, wie sich beide Vorkommengemeinsam handhaben lassen.

Auf Code einer Klasse bezogen, heißt das, dass man sich wie-derholenden Code in eine gemeinsame Methode auslagern sollte.Bei der Architektur einer Anwendung bietet es sich an, Funktio-nen, die an unterschiedlichen Stellen genutzt werden, in einengesonderten Dienst (oder zumindest ein gemeinsam verwendetesModul) auszulagern. Man kann sogar soweit gehen, zu sagen,dass sich DRY auch auf die Infrastruktur anwenden lässt: In demMoment, in dem man einen zweiten Server so aufsetzt, dass ereinem anderen ähnelt, sollte man darüber nachdenken, den Vor-gang zu automatisieren. Damit wird er nicht nur skalierbar, son-dern vor allem auch reproduzierbar und ist zudem dokumentiert.Das Vorgehen verhindert Snowflake Server und führt zu einemhöheren Grad an automatisiert verwalteter Infrastruktur [c].

Zu guter Letzt muss noch gelten, dass Dienste unabhängigvoneinander fungieren. Das lässt sich mit SRP und DRY nochnicht garantieren, allerdings gibt es dafür einen weiteren Bau-stein der SOLID-Prinzipien: Das I steht für das Interface Segre-

gation Principle und besagt, dass eine Schnittstelle nur das ab-solute Minimum an zwingend notwendigem Funktionsumfangbereitstellen sollte, um eine Aufgabe zu erledigen. Alles darüberhinaus gehört in eine gesonderte Schnittstelle.

Bei Diensten verhält es sich genauso: Bietet jeder über seineSchnittstelle nur genau die fachlichen Fähigkeiten an, die zumBewältigen einer Aufgabe erforderlich sind, werden Dienste un-abhängiger voneinander und lassen sich im Bedarfsfall zugleichleichter kombinieren, ohne unnötigen Ballast mitzuschleppen.Robert C. Martin hat das wie folgt formuliert: „Clients shouldnot be forced to depend upon interfaces that they do not use.“

Mit anderen Worten bedeutet das, dass ein Dienst, der auf ei-nen anderen zurückgreift, sich dadurch nicht an weitere Funk-tionen binden sollte, sondern nur an das, was tatsächlich fachlichrelevant ist.

Beispiele für Lego-Denken

In der Praxis gibt es zahlreiche konkrete Beispiele, die die zuvorgenannten Entscheidungskriterien demonstrieren. Interessanter-weise sind viele von ihnen älter als der Begriff der Microser-vices, was lediglich zeigt, dass die zugrunde liegenden Ideen undKonzepte wesentlich älter sind, als es der Hype vermuten lässt.

Eins der Beispiele sind die zahlreichen Werkzeuge, die unixo -ide Betriebssysteme auf der Kommandozeile zur Verfügung stel-len. Die Kommandos laufen als eigenständige Prozesse, erfüllengenau eine Aufgabe und lassen sich flexibel kombinieren. Ge-nau das zeichnet die Mächtigkeit der Kommandozeile im Ge-gensatz zu den meisten grafischen Benutzeroberflächen aus. Eine aktuellere Umsetzung liefern unterschiedliche Anwen-dungen des Toolanbieters HashiCorp, die sich zum Verwalteneiner Cloud-Infrastruktur verwenden lassen. Sie erfüllen eineabgesteckte Aufgabe und überlappen sich fachlich nicht. Theo-

iX Developer 2017 – Legacy-Code 15

SOLID im ÜberblickDie SOLID-Prinzipien sind fünf Regeln für die objektorientierte Pro-grammierung. Sie wurden von Robert C. Martin geprägt und verfol-gen das Ziel, die Wartbarkeit von Code zu verbessern.

Sie lassen sich wie folgt zusammenfassen:

• Single-Responsibility-Prinzip (SRP): Die Regel besagt, dass es für je-de Klasse nur einen Grund für Veränderung geben darf – anders aus-gedrückt, jede Klasse sollte nur genau eine fachliche Verantwort-lichkeit abdecken.

• Open-Closed-Prinzip (OCP): Klassen sollten sich erweitern lassen,ohne dass sich dadurch ihr Verhalten ändert. Eine Klasse sollte alsooffen für Erweiterung, aber geschlossen hinsichtlich der Änderungensein.

• Liskovsches Substitutionsprinzip (LSP): Dass sich eine abgeleiteteKlasse stets so zu verhalten habe, wie die zugrunde liegende Ba-sisklasse, fordert das LSP. Das Ziel ist, jederzeit an Stelle der Su-perklasse eine Subklasse ohne semantische Änderungen verwen-den zu können.

• Interface-Segregation-Prinzip (ISP): Große Interfaces sind auf meh-rere kleine aufzuteilen, sodass ein Verwender nur genau jene In-terfaces implementieren muss, die tatsächlich notwendig sind.

• Dependency-Inversion-Prinzip (DIP): Abhängigkeiten sollten stetsvon konkreten zu abstrakten Modulen verlaufen, sodass konkreteImplementierungen leichter auszutauschen sind und sich vonei-nander entkoppeln lassen.