Ports and Adapters – Die hexagonale Zwiebelarchitektur. Teil 1: Einführung

Soft­warearchitek­tur ver­fol­gt im wesentlichen zwei Ziele: die Struk­turierung von Soft­waresys­te­men und die Min­imierung von Abhängigkeit­en zwis­chen den Struk­turkom­po­nen­ten, den soge­nan­nten Bausteinen. Spätestens seit den Neun­ziger­jahren war das Mul­ti-Tier-Pat­tern, die klas­sis­che Schicht­e­nar­chitek­tur, der Stand der Kun­st. Seit eini­gen Jahren wird dieses Grund­muster zunehmend durch einen neuen De-fac­to-Stan­dard abgelöst, der als Hexag­o­nale Architek­tur bekan­nt gewor­den ist. Was hat es mit diesem mys­tis­chem Begriff auf sich, und welche Vorteile bringt dieser Architek­turstil für die Softwareentwicklung?

Im ersten Teil dieser Artikelserie geht es darum, die Begriffe zu klären und den prinzip­iellen Auf­bau ein­er Hexag­o­nalen Architek­tur zu ver­ste­hen. Doch zunächst machen wir einen kleinen Aus­flug in die Entste­hungs­geschichte dieses pop­ulären Mikroarchitekturmusters. 

Historische Entwicklung

Alistair Cockburn, 2005

Der Begriff der Hexag­o­nalen Architek­tur wurde im Jahre 2005 von Alis­tair Cock­burn geprägt, einem der Mitze­ich­n­er des Agilen Man­i­fests. Das Wort „hexag­o­nal“ beschreibt dabei lediglich die von Cock­burn gewählte grafis­che Darstel­lung der von ihm vorgeschla­ge­nen Mikroarchitektur:

Quelle: alistair.cockburn.us

Cock­burns Ansatz sieht die strik­te Tren­nung von innerem Code und äußerem Code vor, wobei innen den Anwen­dungskern meint, also den Teil des Codes, der die Geschäft­slogik enthält. Um diesen Kern herum, also außen, sind die tech­nis­chen Code­teile ange­ord­net, welche den Geschäftskern mit der äußeren Welt verbinden. Die Außen­welt, das sind beispiel­sweise Brows­er-UIs, Mobile-Apps, Mail-Clients, Daten­banken, Mes­sage-Bro­ker usw., worüber die Anwen­dung mit Benutzern oder anderen Sys­te­men Infor­ma­tio­nen austauscht.

Jeffrey Palermo, 2008

Kurze Zeit später veröf­fentlichte Jef­frey Paler­mo seinen Vorschlag zur Struk­turierung von Soft­waresys­te­men, den er Onion Archi­tec­ture nan­nte. Als Darstel­lung wählte er konzen­trische Kreise, ähn­lich den Schicht­en ein­er Zwiebel: 

Quelle: jeffreypalermo.com

Paler­mo tren­nt eben­falls tech­nis­chen von fach­lichem Code, wobei der App­lika­tionskern expliz­it weit­er unterteilt wird: das Object Mod­el fungiert als Zen­trum, um das herum sich die Object Ser­vices und Appli­ca­tion Ser­vices anord­nen. Die Object-Ser­vice-Schicht definiert dabei die tech­nis­chen Inter­faces, die vom äußeren Infra­struc­ture-Lay­er imple­men­tiert wer­den. Hier begeg­net uns das Depen­den­cy Inver­sion Prin­ci­ple (DIP).

Mike Evans, 2004

Viele Grund­la­gen der mod­er­nen Soft­wa­reen­twick­lung wur­den bere­its 2004 von Eric Evans dargelegt bzw. bestätigt. Sein Buch Domain Dri­ven Design beschreibt eine an fach­lichen Gesicht­spunk­ten aus­gerichtete Meth­ode, die einen gewis­sen inneren Auf­bau der entwick­el­ten Bausteine impliziert. Das Ziel ist auch hier, fach­lichen von tech­nis­chem Code zu trennen. 

Quelle: http://seedstack.org/docs/business/layers/

Evans schlägt eine Aufteilung nach Schicht­en mit strik­ten Ver­ant­wortlichkeit­en und definierten Abhängigkeit­en vor, wobei der Domain-Lay­er die zen­trale Geschäft­skom­po­nente darstellt. Dieser Domain Core hat selb­st keine direk­ten Abhängigkeit­en zu anderen Kom­po­nen­ten. Auch hier wieder DIP: der Domain Core definiert die Inter­faces, die vom Infra­struc­ture-Lay­er imple­men­tiert werden. 

Die Ansätze aller drei Autoren sind sich in ihrem Wesen ähn­lich. Zusam­mengenom­men definieren sie eine mod­erne Mikroar­chitek­tur, die sowohl für den altherge­bracht­en Mono­lithen als auch bei mod­er­nen Microser­vices anwend­bar ist. 

Begriffsverwirrung — Hexagonal? Domain Driven? Onion?

Die Namen Hexag­o­nale Architek­tur, Zwiebel-Architek­tur und Domain Dri­ven Design (kurz DDD) wer­den im All­t­ags­ge­brauch oft syn­onym ver­wen­det. Die Beze­ich­nung Hexag­o­nal hat sich jedoch durchge­set­zt, um dieses Architek­tur­muster zu beschreiben. Zum einen ver­mut­lich, weil es so schön eso­ter­isch klingt, ohne jedoch Küchenas­sozi­a­tio­nen zu weck­en. Zum anderen ist DDD in sein­er Gesamtheit mehr als erken­nt­nis­philosophis­che Methodik zu ver­ste­hen, ist daher viel bre­it­er gefasst und geht über die reine Struk­turierung von Soft­ware-Bausteinen hinaus.

Hexagonale Architektur: Definition der Begriffe

Ports

Ein Port definiert logisch zusam­menge­hörige Use Cas­es und die dafür benötigten Schnittstellen.

In der fol­gen­den Abbil­dung sind beispiel­sweise diese Schnittstellen definiert (Beispiele über­nom­men von Cock­burn, s.o.):

  1. Daten­ver­ar­beitung
  2. Admin­is­tra­tion
  3. Events/Notifications (nach außen) 
  4. Daten­bank­abfra­gen.
Abb.: Ports definieren die Schnittstellen zur Außenwelt

Auf der linken Seite des Hexa­gons sehen wir die aktiv­en Ports (1) und (2), während (3) und (4) auf der recht­en Seite als pas­sive Ports ange­ord­net sind. Aktive und pas­sive Ports bilden zusam­men den Infra­struc­ture Lay­er, also den Teil der Anwen­dung, der mit der Außen­welt kommuniziert.

Aktive und passive Ports

Über die aktiv­en Ports, die nach außen sicht­bar sind, wer­den Änderun­gen in der App­lika­tion angestoßen. Men­schliche Benutzer oder tech­nis­che Clients greifen über die öffentlichen Schnittstellen aktiv in den Zus­tand der Anwen­dung ein. Aktive Ports wer­den auch als Primär­ports oder Inbound Ports bezeichnet.

Dahinge­gen sind die pas­siv­en Ports nicht von außer­halb der Anwen­dung zugänglich. Über sie kann nur die Anwen­dung selb­st auf andere Sys­teme lesend oder schreibend zugreifen und die erhal­te­nen Antworten inner­halb ihrer eige­nen Geschäft­sprozesse ver­ar­beit­en. Die Ver­wen­dung dieser Ports erfol­gt somit pas­siv. Über die pas­siv­en Ports kann keine direk­te Inter­ak­tion mit der Anwen­dung erfol­gen. Pas­sive Ports sind auch als Sekundär­ports oder Out­bound Ports bekan­nt.

Port = Schnittstelle + Datentransport

Die Ports ein­er Soft­ware­an­wen­dung beste­hen aus ein­er Schnittstel­len­spez­i­fika­tion und dem tech­nis­chen Teil der Imple­men­tierung, der Daten­struk­turen in die Anwen­dung hinein und hin­aus transportiert. 

ISP – Interface Segregation Principle

Pas­sive Ports bilden nicht die kom­plette Schnittstelle des exter­nen Sys­tems ab, son­dern nur genau den Teil, der von der App­lika­tion ger­ade benötigt wird. So würde der Noti­fi­ca­tion-Port (Nr. 3 in der obi­gen Abbil­dung) lediglich den Teil der Mailserv­er-API nutzen, der zum Versenden von E‑Mails benötigt wird. Das zuge­hörige DTO enthält dann auch nur die Attribute, die tat­säch­lich für den Mail­ver­sand rel­e­vant sind. Wer­den beispiel­sweise die Felder CC und BCC nicht benötigt, wird das DTO sie nicht enthalten.

Adapters

Adapter fungieren als Ver­mit­tler zwis­chen Sys­te­men mit unter­schiedlichen Schnittstellen. 

In der abstrak­ten Welt der Soft­waretech­nik dienen Adapter dazu, Daten­struk­turen aus Fremdsys­te­men in das eigene Sys­tem zu über­führen, zu adap­tieren, und zwis­chen bei­den Domä­nen hin- und her zu transformieren. 

Primäre und sekundäre Adapter

Zu einem Port gibt es einen oder mehrere Adapter. Aktive Ports enthal­ten primäre Adapter, das sind die Con­troller der Anwen­dung. Über sie gelan­gen Anweisun­gen und Dat­en von außen zum Anwen­dungskern, um dort die Geschäft­slogik auszuführen und Antworten zu erhalten.

Die sekundären Adapter der pas­siv­en Ports wer­den vom Anwen­dungskern genutzt, um externe Sys­teme einzu­binden. Beispiel­sweise andere Microser­vices, Mes­sag­ing- oder Stor­agesys­teme, wie die fol­gen­den Grafik ver­an­schaulicht. Sekundäradapter imple­men­tieren immer genau den Teil der exter­nen Schnittstelle, der vom Anwen­dungskern benötigt wird – auch hier wieder ISP.

Abb.: Primäre und sekundäre Adapter

Zu beacht­en ist hier­bei die Rich­tung der Abhängigkeit­en: Primäre Adapter rufen den Anwen­dungskern, dieser ken­nt jedoch die primären Adapter nicht. Sekundäre Adapter wer­den vom Anwen­dungskern benutzt, ken­nen diesen jedoch nicht. Die Aufrufhier­ar­chie im Hexa­gon geht also von links nach rechts, wie fol­gen­des Bild veranschaulicht:

Abb.: Die Aufrufhier­ar­chie im Hexagon

Application

Der Appli­ca­tion-Ring im Hexa­gon markiert die Verbindungss­chicht zwis­chen der Infra­struk­tur und dem Domainkern. Hier resi­diert die App­lika­tion­slogik. Das ist der Teil der Logik, der nicht die Geschäft­sregeln bein­hal­tet, wohl aber die Use Cas­es anstößt und koor­diniert. Außer­dem sind hier die tech­nis­chen Belange der App­lika­tion abge­bildet, beispiel­sweise Secu­ri­ty, Log­ging, Daten­bank­transak­tio­nen oder Ses­sion­han­dling. Bei der vielz­i­tierten Tren­nung von Tech­nik und Fach­lichkeit kommt dem Appli­ca­tion­lay­er eine entschei­dende Bedeu­tung zu. Es sind in erster Lin­ie die Appli­ca­tion Ser­vices, die seine Sub­stanz bilden.

Application Service

Appli­ca­tion Ser­vices definieren die Ein­stiegspunk­te für Use Cas­es und koor­dinieren deren Abläufe. 

Abb.: Der Appli­ca­tion Lay­er mit seinen Appli­ca­tion Services

Ein Appli­ca­tion Ser­vice wird von einem Primäradapter, beispiel­sweise einem REST-Adapter oder Mes­sage-Con­sumer, aufgerufen. Der Appli­ca­tion Ser­vice gibt den Aufruf an den Domain Core weit­er und stößt dadurch den zuge­höri­gen Use Case an. Zusam­men­hän­gende Use Cas­es wer­den vom Appli­ca­tion Ser­vice koor­diniert, einzelne Ergeb­nisse wer­den zu Antworten aggregiert und erforder­liche Events, z.B. Mail­ver­sand oder Noti­fi­ca­tions für andere Ser­vices, wer­den angetrig­gert. Wo nötig, kom­mu­niziert der Appli­ca­tion Ser­vice mit den Sekundäradaptern der pas­siv­en Ports, um beispiel­sweise Enti­tys aus einem Daten­bankrepos­i­to­ry oder von ein­er ent­fer­n­ten REST-API zu laden, die für den Ablauf des Use Cas­es benötigt wer­den. Wie bere­its erwäh­nt, enthält der Appli­ca­tion Ser­vice selb­st keine Geschäftslogik.

Domain Core

Der Domain Core ist der fach­liche Kern der App­lika­tion. Er enthält die fach­liche Imple­men­tierung der Use Cas­es mit der auszuführen­den Geschäft­slogik. Außer­dem die Imple­men­tierung der Fachk­lassen, das soge­nan­nte Domain Mod­el.

Abb. Der Domain Core im Hexa­gon mit Domain Mod­el und Domain Ser­cvices

Domain Model

Das Domain Mod­el bein­hal­tet im Wesentlichen die in dem fach­lichen Kon­text – Bound­ed Con­text im DDD-Jar­gon – iden­ti­fizierten Entitäten. Diese bilden den eigentlichen Kern der App­lika­tion. Hier ist das Wis­sen der Fach­domäne enthal­ten, soweit es zur Durch­führung der abzu­bilden­den Use Cas­es erforder­lich ist. Im Domain Dri­ven Design ist das Domain Mod­el das Herzstück der Soft­ware. Appli­ca­tion- und Infra­struc­ture­lay­er wer­den um den Domainkern herum ange­ord­net und sind vom diesem abhängig, nicht umgekehrt. Die Darstel­lung als konzen­trisches Hexa­gon ist dazu geeignet, dieses wichtige Grund­prinzip eingängig zu visu­al­isieren. Die Geschäft­slogik ist im Rich Domain Mod­el in den Meth­o­d­en der Fachk­lassen imple­men­tiert. Teile der Logik, die kein­er Fachk­lasse direkt zuge­ord­net wer­den kön­nen, wer­den in den Domain Ser­vices implementiert.

Domain Services

Die Domain Ser­vices enthal­ten über­greifende Geschäft­slogik, die nicht eins-zu-eins auf Fachk­lassen abge­bildet wer­den kann. Domain Ser­vices wer­den vom Appli­ca­tion-Lay­er, genauer von den dor­ti­gen Appli­ca­tion Ser­vices aufgerufen. Ein Domain Ser­vice darf direk­ten Zugriff auf die Infra­struc­ture haben und deren pas­sive Adapter benutzen. Hier­bei ist allerd­ings abzuwä­gen, ob der Aufruf bess­er in den Appli­ca­tion Lay­er zu ver­lagern wäre. 

Im ersten Fall kann der Domain Lay­er ein IoC-Inter­face bere­it­stellen, um den Adapter von der Domain zu entkoppeln. 

Im zweit­en Fall würde der Appli­ca­tion Ser­vice mit der Infra­struc­ture kom­mu­nizieren und das Ergeb­nis des Aufrufs in adäquater Form an die Domain übergeben. Diese Vari­ante sollte bevorzugt einge­set­zt wer­den, es sei denn, prak­tis­che Gründe sprechen für den anderen Weg.

Fern­er ist darauf zu acht­en, dass keine Infra­struc­ture-Depen­den­cys in die Domain hineinge­zo­gen wer­den, beispiel­sweise durch Ref­eren­zen auf DTOs als Aufruf­pa­ra­me­ter oder Rück­gabe­w­erte eines Adapters. Diese müssen vor der Über­gabe zunächst inner­halb der Adapter-Ebene auf Domain-Enti­tys gemappt wer­den. Die Map­pin­glogik fungiert dabei als Anti-Cor­rup­tion-Lay­er (ACL) nach DDD, der dafür sorgt, dass das Mod­el des Domain Cores nicht durch fremde Mod­els extern­er APIs „ver­schmutzt“ wird.

Abhängigkeiten und Aufrufhierarchien

Das Hexa­gon beste­ht aus zwei logis­chen Hälften, der linken aktiv­en Seite und der pas­siv­en recht­en. In der Abbil­dung ist zu erken­nen, dass Aufrufe nur in eine Rich­tung gehen, und zwar von links nach rechts. Niemals würde ein pas­siv­er Adapter einen Appli­ca­tion Ser­vice aufrufen, oder dieser einen aktiv­en Adapter. Solche Calls sind strikt verboten. 

Die Abhängigkeit­en der Schicht­en gehen immer von außen nach innen. Allerd­ings gibt es keine Depen­den­cy von dem pas­siv­en Teil des Infra­struc­ture Lay­ers zum Appli­ca­tion Layer. 

Abb.: Aufrufhier­ar­chien und Abhängigkeit­en zwis­chen den Grund­bausteinen des Hexagons

Somit lässt sich zusammenfassen:

  • Jed­er Baustein kann eine Depen­den­cy zum Domain Mod­el haben. 
  • Die Benutzung der Geschäft­slogik bleibt jedoch den Appli­ca­tion Ser­vices vorbehalten.
  • Appli­ca­tion Ser­vices kön­nen sowohl die Sekundäradapter der pas­siv­en Ports als auch die Domain Ser­vices benutzen.
  • Domain Ser­vices kön­nen Sekundäradapter benutzen, diese kön­nen durch IoC entkop­pelt werden.
  • Das Domain Mod­el hat kein­er­lei Abhängigkeit­en zu anderen Bausteinen.

Ausblick

Im näch­sten Teil tauchen wir tiefer in die einzel­nen Ringe hinein und definieren deren Inhalte im Detail. Weit­er­hin müssen wir eini­gen Fra­gen nachge­hen, die bei der Umset­zung eines Architek­tur­musters immer wieder gestellt wer­den. Zum Beispiel, was ist Busi­ness­logik, was ist Anwen­dungslogik, und worin unter­schei­den sie sich? In welchem Teil des Hexa­gons wer­den Domain­ob­jek­te erzeugt, die sich aus Dat­en von mehreren Drittsys­tem zusam­menset­zen? Wo wer­den Quer­schnitts­the­men wie Daten­va­li­dierung, Excep­tion­han­dling, Log­ging usw. implementiert? 

Es bleibt also span­nend. Bis dahin, hap­py engineering 🙂

„Pat­terns are use­ful because it gives soft­ware pro­fes­sion­als a com­mon vocab­u­lary with which to communicate. “

Jef­frey Palermo

Stiller Killer #1: Technische Schulden

Im Soft­wa­reen­twick­lung­sprozess kön­nen Zustände entste­hen, die den Pro­jek­ter­folg verzögern, gefährden oder gän­zlich unmöglich machen. Schon manch­es Pro­jekt musste wegen man­gel­nden Bewusst­seins über solche Risiken wieder­holt neu aufge­set­zt oder gän­zlich begraben wer­den. Selb­st dann jedoch bleiben die Ursachen häu­fig im Ver­bor­ge­nen. Ich nenne sie daher stille Killer – die unerkan­nte, zuweilen auch nur unter­schätzte Gefahr.

Der Begriff der Tech­nis­chen Schulden ist heute den meis­ten, die in irgen­dein­er Weise mit Soft­wa­reen­twick­lung zu tun haben, geläu­fig. In agilen Entwick­lerteams wird er für gewöhn­lich regelmäßig the­ma­tisiert. Unter dem Kürzel TD für Englisch Tech­ni­cal Debt wird die Tech­nis­che Schuld gerne bemüht, um Verzögerun­gen oder Fehler­rat­en zu begrün­den. Und obwohl das Konzept der Tech­nis­chen Schuld in den let­zten Jahren auf unrühm­liche Weise pop­ulär gewor­den ist, gibt es einen Umstand, der allzu leicht überse­hen wird: die Entste­hung von Tech­nis­ch­er Schuld ist unvermeidbar.

Technische Schuld ist ein Naturgesetz

Wir sind nicht hellsichtig, und Zeit ist ein knappes Gut (glauben wir)

Soviel wir uns auch bemühen, wir kön­nen nicht jedes Detail vorausah­nen. Auch kön­nen wir uns nicht beliebig viel Zeit nehmen, immer die ide­ale Lösung zu entwer­fen. So manche weitre­ichende Entschei­dung tre­f­fen wir inner­halb von Sekun­den. Manch­mal bemerken wir zu spät, dass uns zur opti­malen Umset­zung schlicht Wis­sen fehlte.

Architektur erodiert

Und dann ist da noch etwas, das Dr. Car­o­la Lilien­thal als Architek­turero­sion beze­ich­net. Damit ist gemeint, dass selb­st ein ide­al­er Entwurf mit der Zeit seine Gültigkeit ver­lieren kann, und aus ein­er ehe­mals guten mit der Zeit eine weniger gute Imple­men­tierung wird. Eine Architek­tur, die gestern noch per­fekt war, ist heute falsch, da die Umstände sich verän­dert haben. Geset­zliche Vor­gaben oder neue Ver­fahren im Zahlungsverkehr sind gute Beispiele dafür. Davon abge­se­hen entste­ht Ero­sion häu­fig auch dadurch, dass ver­schiedene Entwick­ler mit unter­schiedlichem Hin­ter­grund und Sachver­stand den Code mit der Zeit in ein Sta­di­um zunehmender Unord­nung über­führen. Kurz gesagt: der Code wird mit der Zeit immer schlechter. In vie­len Soft­ware­pro­jek­ten ist das eine fest­ste­hende Tatsache.

frei nach C. Lilien­thal: der „Kor­ri­dor geringer Tech­nis­ch­er Schuld“

Es ist also schw­er, TDs in Soft­waresys­te­men ganz zu ver­mei­den. Wohl aber gibt es Möglichkeit­en, die Schä­den zu min­imieren, indem wir ihr Aus­maß inner­halb eines „Kor­ri­dors geringer tech­nis­ch­er Schulden“ (Lilien­thal) hal­ten. Doch zunächst wollen wir ver­suchen zu umreißen, was Tech­nis­che Schuld für uns bedeutet.

Definition

Tat­säch­lich gibt es zur Def­i­n­i­tion von TD unter­schiedliche Betra­ch­tungsweisen. Die ein­fach­ste ist, jed­wede unsachgemäße Pro­gram­mierung als TD zu beze­ich­nen. Unsachgemäß bedeutet hier: den Stand der Kun­st mis­sach­t­end oder schlichtweg schäbig programmiert. 

Eine andere Auf­fas­sung besagt, dass es immer eine bewusste Entschei­dung sein muss, Tech­nis­che Schulden aufzunehmen – schlechte Pro­gram­mierung oder man­gel­hafte Architek­tur fie­len dann also nicht darunter. Hier wür­den wir kurzfristig und bewusst ein TD akzep­tieren, um ein anderes Ziel, beispiel­sweise einen bevorste­hen­den Releaseter­min, schneller zu erreichen. 

Als Ver­all­ge­meinerung bei­der Sicht­en ließe sich definieren, dass Tech­nis­che Schuld poten­ziell jed­er Zus­tand ein­er Soft­ware ist, der eine naht­lose Weit­er­en­twick­lung ver­hin­dert.

Gründe und Auswirkungen

Aufgeschobene Refactorings

Sobald durch eine anste­hende Erweiterung die Umstruk­turierung beste­hen­der Teile der Soft­ware erforder­lich wird, dro­ht ein Schulden­loch. Ob es sich auf­tut und wir hine­in­fall­en hängt davon ab, ob das Refac­tor­ing durchge­führt wird oder nicht. Wenn ja, ist alles gut – keine TD aufge­baut. Wenn wir es nicht tun, haben wir von dem Moment an eine TD, die mit jedem kom­menden Change Request ihre Zin­sen ein­fordern wird. Der Zins­eszin­sef­fekt ist bekan­ntlich ein expo­nen­tielles Phänomen. Das gilt auch in der Softwareentwicklung.

Häu­fig wird mit der Umset­zung von Erweiterun­gen begonnen, ohne vorher zu prüfen, ob die beste­hende Architek­tur den neuen Anforderun­gen stand­hält. Nötige Refac­tor­ings wer­den so erst spät erkan­nt, wenn über­haupt. Nach zwei oder drei Erweiterungszyklen ist das Dilem­ma oft­mals bere­its so weit fort­geschrit­ten, dass Maß­nah­men zur Behe­bung sehr deut­lich zu Buche schla­gen wür­den. Die Lösung heißt dann allzu oft „Augen zu und durch“ – und so manövri­ert sich das Pro­jekt selb­st immer tiefer in den Schulden­berg hinein. In dieser Phase begin­nen die Schmerzen groß genug zu wer­den, um Aufmerk­samkeit „von oben“, also des Man­age­ments oder des Kun­den zu gewin­nen. Die Frage lautet dann, warum selb­st kleine Fea­ture-Erweiterun­gen „so lange“ dauern. 

Featuritis

Es ist eine Bin­sen­weisheit, dass viele Soft­ware­pro­jek­te mit Bazil­lus Fea­tu­ri­tis infiziert sind. Das giftige Stof­fwech­sel­pro­dukt dieses Erregers ist TD. Eine der Schwierigkeit­en bei der Diag­nose von TD ist die lange Inku­ba­tion­szeit. Während funk­tionale Soft­warefehler meist frühzeit­ig zutage treten, wirken sich TDs eine Zeit lang eher unter­schwellig aus. Wenn ihre unschö­nen Symp­tome wie erschw­erte Wart­barkeit und steigende Defect-Rat­en sich endlich zeigen, ist das Soft­ware­pro­dukt bere­its der­art herun­tergewirtschaftet, dass ein Gegen­s­teuern nicht mehr ohne deut­lichen Mehraufwand zu bew­erk­stel­li­gen ist. Das liegt daran, dass TD ein Aspekt der inneren Soft­ware­qual­ität ist, beziehungsweise ein Man­gel der­sel­ben. Die innere Qual­ität eines Pro­duk­ts ist von außen nicht direkt sicht­bar. Sie wird nur dann wahrgenom­men, wenn sie so dürftig ist, dass sie zu Fehlfunk­tio­nen oder steigen­den Aufwän­den führt.

Das Gesetz von Conway

Nach dem vielz­i­tierten Gesetz von Con­way, englisch Conway‘s Law, entsprechen die Struk­turen eines Soft­waresys­tems zwangsläu­fig denen der Organ­i­sa­tion, die es her­vorge­bracht hat. Ver­schiedene Stu­di­en haben dieses Pos­tu­lat aus den Achtziger­jahren bestätigt. Das bedeutet am konkreten Beispiel, ein zen­tral­is­tisch-hier­ar­chisch organ­isiertes Unternehmen wird sich mit Microser­vice-Architek­turen, wo Dezen­tral­isierung, Ereignis­s­teuerung, Infor­ma­tion­saus­tausch und Entkop­plung eine große Rolle spie­len, ver­mut­lich schw­er­tun. Tech­nis­chen Schulden fall­en in dieselbe Kat­e­gorie. Steile Hier­ar­chien, Führung in Form von Kom­man­dostruk­turen anstatt Ser­vant Lead­er­ship sowie fach­liche und soziale Inkom­pe­tenz begün­sti­gen den Nährbo­den für das Wach­s­tum von TD. 

Wege aus der Schuldenfalle

Schuldeingeständnis

Ist ein Pro­jekt ein­mal in einen Zus­tand tech­nis­ch­er Über­schul­dung einge­treten, gibt es den­noch Wege hin­aus. Sie erfordern im ersten Schritt ein Schuldeingeständ­nis der Beteiligten: Prod­uct-Own­er, Devel­op­ment-Team und Pro­jek­tleitung müssen sich über ihre jew­eili­gen Anteile im Klaren sein und gemein­sam an der Lösung arbeit­en. Ein Coach kann dabei hil­fre­ich sein. Vielle­icht gibt es einen fähi­gen Scrum Mas­ter, der die Parteien dabei unter­stützt, einen gang­baren Weg zu finden.

Schadenanalyse

Sind sich PO, PL/Kunde und Dev-Team einig, kann der neg­a­tive Effekt von TD zumin­d­est abgemildert, manch­mal sog­ar beseit­igt wer­den. Jeden­falls gilt es nun, beherzt zu han­deln. Zunächst muss das Aus­maß der Tech­nis­chen Schulden umris­sen wer­den, damit die Aufwände zu ihrer Behe­bung einge­plant wer­den kön­nen. Nicht sel­ten gibt es hier­bei uner­wartete Ergeb­nisse: mehrere Wochen bis Monate an Kon­so­li­dierungsar­beit­en sind für ein Pro­jekt schw­er zu verkraften. Mut, eine der fünf agilen Tugen­den, ist hier beson­ders gefragt. In ein­er solchen Lage müssen wir uns fol­gen­des immer vor Augen hal­ten: erstens haben wir diesen Zus­tand selb­st verur­sacht, und zwar kollek­tiv auf allen Ebe­nen. Zweit­ens, wenn wir den Mut jet­zt nicht auf­brin­gen, den Zus­tand zu verbessern, wird es später noch härter.

Coaching

Eben­falls schwierig wird es, wenn die oben genan­nten Con­way-Gründe vor­liegen. Dann haben wir es mit einem echt­en Härte­fall zu tun. Unter solchen Umstän­den ist es uner­lässlich, einen erfahre­nen Coach hinzuzuziehen, der Führung und Entwick­lung miteinan­der in Ein­klang brin­gen hilft.

Vorsicht ist besser als Nachsicht: Technische Schulden einplanen

Um die Entste­hung von TD möglichst von vorn­here­in zu ver­hin­dern, gilt es, die dro­hen­den Schulden­löch­er rechtzeit­ig zu erken­nen. Ide­al­er­weise prüfen wir die beste­hende Imple­men­tierung, bevor wir mit der Weit­er­en­twick­lung begin­nen. Das schließt auch die Makroar­chitek­tur mit der Kom­mu­nika­tion zwis­chen beteiligten Sys­te­men mit ein. In diesem Fall kön­nten die Refac­tor­ing-Aufwände bere­its in der Pla­nung berück­sichtigt werden. 

Ver­säu­men wir, eine Vor­analyse durchzuführen oder ist es nicht möglich, kön­nen wir immer noch während der Entwick­lung TDs iden­ti­fizieren. Nun ist zu prüfen, wie wir damit umge­hen wollen.

Die edel­ste Option wäre, die TDs sofort zu beheben und zu akzep­tieren, dass sich der Pla­nung­shor­i­zont entsprechend nach hin­ten ver­schiebt. Das kann eine agile Pro­jek­tor­gan­i­sa­tion leicht verkraften. Im klas­sis­chen Man­age­men­tum­feld kann es jedoch passieren, dass wir damit aller­gis­che Reak­tio­nen aus­lösen. Hier wäre ein Kom­pro­miss, die TD zunächst zu akzep­tieren, um das Fea­ture – not­falls mit Workarounds um die TD herum – plan­mäßig funk­tion­al fer­tigzustellen. Nun aber müssen wir sofort ein Tick­et erstellen, um die TD anschließend so schnell wie möglich ein­pla­nen und auflösen zu kön­nen. Eine gute Release­pla­nung sollte diesen Fall auch im klas­sis­chen Umfeld abfed­ern können. 

Organisatorische Maßnahmen

Qualität mit einplanen

Organ­isatorische Maß­nah­men betr­e­f­fen alle Ebe­nen der Pro­jek­tor­gan­i­sa­tion. Am Ende jedoch läuft es darauf hin­aus, dass die Pro­jek­tleitung für das Gelin­gen oder Scheit­ern Sorge tra­gen muss. Die PL bes­timmt maßge­blich die Pro­jek­tkul­tur, und damit auch, wie mit Her­aus­forderun­gen umge­gan­gen wird, ger­ade wenn sie Geld kosten. Nicht zulet­zt bes­timmt sie, welche Qual­itäts­maßstäbe anzule­gen sind und wie die Sicherung der Soft­ware­qual­ität erfol­gen soll.

Project Staffing

Zunächst ein­mal sollte unser Pro­jekt mit fähi­gen Men­schen aus­ges­tat­tet sein: wir benöti­gen beispiel­sweise ein Prod­uct-Man­age­ment-Team und einen Führungsstab, die wis­sen, wie Soft­wa­reen­twick­lung­sprozesse funk­tion­ieren. Wir brauchen ein Devel­op­ment-Team, das auf dem Stand der Tech­nik und bere­it ist, sich stetig weit­erzuen­twick­eln. Und wir soll­ten einen Pro­jek­t­coach engagieren, beispiel­sweise einen Scrum Mas­ter, der dafür sorgt, dass die Organ­i­sa­tion der Zusam­me­nar­beit im Pro­jekt rund läuft. 

Wenn die Qual­ität­sziele gesteckt sind und das Pro­jekt gut gestafft ist, sind die Grund­la­gen geset­zt. Nun gibt es eine Rei­he von Tools, um die innere Qual­ität des Soft­ware­pro­duk­ts zu gewährleisten.

Architekturwissen vom Elfenbeinturm ins Entwicklerteam 

Eines der wichtig­sten Werkzeuge ist die Fähigkeit des Entwick­lerteams, die Soft­ware mit Hil­fe ein­er zweck­mäßi­gen Makro- und Mikroar­chitek­tur gut zu struk­turi­eren. Dadurch wird die langfristige Wart­barkeit gewährleis­tet, und Tech­nis­che Schulden kön­nen leichter ver­mieden oder behoben werden. 

Eine beliebte Falle ist hier­bei, die Soft­ware-Architek­tur als eine eigene, von der Pro­gram­mier­ar­beit getren­nte Diszi­plin zu betra­cht­en. Es emp­fiehlt sich jedoch vielmehr, dafür zu sor­gen, dass die Entwick­ler über pro­fun­des Architek­tur­wis­sen ver­fü­gen und dieses in ihrer täglichen Arbeit stetig anwen­den. Mit der dazuge­höri­gen Diszi­plin lässt sich dann die Architek­turero­sion in Gren­zen halten.

Poka-Yoke-Entwicklungsprozess – Fehler besser vorher vermeiden statt später beheben

Um durch Unacht­samkeit oder Toll­patschigkeit entste­hende Tech­nis­che Schuld von vorn­here­in zu berück­sichti­gen, emp­fiehlt es sich, den Entwick­lung­sprozess so zu gestal­ten, dass alleine dadurch die Entste­hung von TD bere­its eingeschränkt wird. Dieses Vorge­hen ist als Poka Yoke bekan­nt. Beispiel­sweise imple­men­tieren wir Domain Dri­ven Design als fes­ten Bestandteil des Prozess­es. Dadurch wird sichergestellt, dass Änderun­gen in der Fach­domäne adäquat in der Soft­ware abge­bildet wer­den. Als näch­stes sollte Clean Code gelebt wer­den. Dadurch min­imiert sich das Aufnehmen von Tech­nis­chen Schulden von selb­st. Für den pas­sion­ierten Clean Coder gehört kon­tinuier­lich­es Refac­tor­ing zu den Stan­dard­mit­teln, um ger­ade auch die Tech­nis­chen Schulden klein zu hal­ten. Eben­so sind effek­tiv durchge­führte Codere­views ein fes­ter Bestandteil der Clean-Code-Praxis.

SonarQube für statische Codeanalyse nach der SQALE-Methode

Als tech­nis­ches Tool hat sich in vie­len Pro­jek­ten Sonar­Qube etabliert, das in die Build-Pipeline inte­gri­ert wer­den kann, um mit Hil­fe von sta­tis­ch­er Code­analyse unter anderem das Aus­maß an TD zu bemessen. Dabei wird der Grad der Tech­nis­chen Schuld in Man­nta­gen beziffert. 

Allerd­ings dür­fen wir uns nicht darüber hin­wegtäuschen lassen, dass Sonar­Qube seman­tis­che Qual­ität­skri­te­rien wie Les­barkeit und Ver­ständlichkeit des Source­codes genau­so wenig messen kann wie dessen sach­liche Kor­rek­theit. Wichtige Design­prinzip­i­en wie das Sin­gle Respon­si­bil­i­ty Prin­ci­ple sind eben­falls schw­er automa­tisiert zu prüfen. Daher ist Sonar­Qube zwar eine gute Unter­stützung, sollte jedoch keines­falls als alleinige Quelle der Wahrheit dienen.

Fazit

Fassen wir zusammen:

  • Tech­nis­che Schuld entste­ht laufend während der Entwick­lung. Das ist natür­lich und ganz normal.
  • Obwohl die Tech­nis­che Schuld als Buzz­word in der Soft­wareszene hin­länglich bekan­nt ist, find­en ihre Ursachen und Auswirkun­gen wenig Beach­tung. Allzu oft wer­den sie ignoriert.
  • Die besten Pro­gram­mier­er und Architek­ten kön­nen Tech­nis­che Schulden nicht abwen­den, wenn der Entwick­lung­sprozess und/oder die Unternehmen­skul­tur wirk­same Maß­nah­men konterkarieren.
  • Ein großer Anteil tech­nis­ch­er Schulden lässt sich bere­its durch rein organ­isatorische Maß­nah­men unterbinden. Neben ein­er fähi­gen Pro­jek­t­mannschaft müssen somit auch kul­turelle und prozes­suale Voraus­set­zun­gen geschaf­fen werden.
  • Soft­ware­pro­jek­t­coach­ing ist ein pro­bates Mit­tel, um die Ver­schul­dung von Anfang an klein zu hal­ten, oder ein Soft­ware­pro­jekt aus der TD-Sack­gasse herauszuführen.

In diesem Sinne, hap­py engineering 🙂

Wikipedia: Tech­nis­che Schulden