Wie skaliert man organisatorisch erfolgreich in der IT? Der Artikel erläutert die bekannten Probleme bei der organisatorischen Skalierung und erklärt, wie man sie löst.
Vertikalisierung ist ein Architekturansatz für organisatorische Skalierung.
Es gibt daher zwei sinnvolle Ziele, die den Aufwand einer Vertikalisierung rechtfertigen:
- Ein Unternehmen stößt an Skalierungsgrenzen und möchte (nur) bei der Softwareentwicklung organisatorisch skalieren können (Effizienzmodell).
- Oder ein Unternehmen möchte noch weiter gehen und seine Organistionsstruktur grundsätzlich verändern, um neben der Effizienz die Effektivität zu steigern .
In diesem Teil 1 der Artikelserie zu erfolgreicher Vertikalisierung beschäftigen wir uns mit dem ersten Ziel, Effizienz. Im Folgenden erkläre ich, wie man zu einem Schnittmuster für eine technische und fachliche Architektur kommen kann, das optimal auf die organisatorische Skalierung der Softwareentwicklung einzahlt.
Wie beschleunigt man Softwareentwicklung?
Gehen wir also erst einmal von diesem ersten Ziel aus: Ein Unternehmen möchte bei der Umsetzung seiner Vorhaben schneller werden: Wie kann ein Team schneller ein Software-Produkt entwickeln?
- Automatisierung: Alles, was häufig wiederholt getan werden muss und automatisierbar ist, führt durch Automatisierung zu weniger Fehlern und ist viel schneller. Häufig ist es darum auch langfristig kostengünstiger.
- hohe Nachhaltigkeit: Hohe Softwarequalität beschleunigt auf lange Sicht die Arbeit der Entwickelnden. Verständliche, überschaubare, entkoppelte Software adressiert, dass Code sehr oft gelesen, aber nur einmal geschrieben wird. Ein Team wird schneller, wenn der Code leicht verständlich ist und bleibt.
Wenn wir das Handwerk weitgehend optimiert haben, bleibt nur eine Antwort auf die Frage nach mehr Beschleunigung: Mehr Softwareentwickelnde!
Die Gefahr von vielen parallel arbeitenden Softwareentwickelnden ist aber, dass Abhängigkeiten zwischen ihnen bestehen, sowohl handwerklich als auch fachlich.
Handwerkliche Abhängigkeit auflösen
Die handwerkliche Verlangsamung durch das parallele Arbeiten vieler Entwickelnder an einem System ist bekannt: Viele Entwickelnde müssen sich bei Codeänderungen, Deployments, Architekturentscheidungen, Prozessentscheidungen etc. koordinieren und abstimmen. Dabei nimmt die Zahl der Kommunikationswege bei Teamwachstum exponentiell zu, weshalb auch koordinative Maßnahmen immer aufwendiger und fehleranfälliger werden. Einigkeit ist schwieriger zu erzielen, Subgruppenbildung erfolgt ab ca. 8 Personen zwangsläufig, um die Koordination beherrschbar zu halten. Fehler einzelner Entwickelnder bremsen immer gleich die Arbeit einer großen Gruppe von anderen Entwickelnden.
Die Zerlegung in mehrere Services ist die Antwort auf die handwerkliche Kopplung so vieler Entwickelnder. Es erfolgt ein Rewrite von großen Systemen in Form von mehreren Services: Nun entstehen kleine Teams, die keinen Applikationscode, keine Betriebsaufgaben und keine Deployment-Prozesse miteinander teilen müssen. Somit sind sie handwerklich unabhängig und der handwerkliche Koordinationsaufwand existiert nicht mehr.
Aber damit hat man nur die handwerkliche Unabhängigkeit für die Teams geschaffen. Es ist nicht sichergestellt, dass die Teams auch fachlich unabhängig agieren können. Die Teams können unabhängig Code ändern und deployen, aber wenn die Umsetzung von Features häufig erfordert, dass mehrere Teams ihren Code ändern müssen, warten die Teams aufeinander und müssen sich wieder koordinieren. Dies hat gravierende Auswirkungen bei unabhängigen Teams, da nun jegliche Planung immer mehrere Teams umfassen kann, was deren Koordination in einem Zeitplan erfordert. Dies verursacht intensiven Koordinationsaufwand - die bekannte "Meetinghölle".
Sobald wir das beobachten, haben wir es mit dem Antipattern des verteilten Monolithen zu tun: Es ist zwar innerhalb eines Teams handwerklich jetzt möglich, sehr schnell zu arbeiten, weil man sich nicht mehr mit vielen Entwickelnden koordinieren muss bzw. behindern kann. Aber es entsteht enormer Planungs- und Organisationsaufwand, um Features zu planen und fertigzustellen. Die Geschwindigkeitszunahme bei der organisatorischen Skalierung fällt dementsprechend gering aus oder kehrt sich ins Negative. Dieses Antipattern entsteht z.B. fast zwangsläufig, wenn die Zerlegung eines Monolithen anhand von Datenzuständigkeiten gemacht wird.
Fachliche Abhängigkeiten auflösen
Der entscheidende Kniff einer Vertikalisierung ist darum, neben den technischen Abhängigkeiten vor allem die fachlichen Abhängigkeiten möglichst weitgehend zu vermeiden. Wenn eine Firma organisatorisch skalieren möchte, muss sie ab einer kritischen Größe organisatorische Untereinheiten bilden. Klassisch unterteilen Firmen ihre Organisationsstruktur in Tätigkeitsbereiche des Geschäftsmodells: "Einkauf", "Logistik", "Marketing", "Faktura" etc. Entscheidend für eine fachliche Trennung von Systemen sind hier verschiedene Gruppen von Nutzenden, die sehr unterschiedliche fachliche Qualifikationen und Use Cases (Tätigkeiten) haben. Sie sind untereinander schwach gekoppelt. Dementsprechendes gilt auch für die von ihnen genutzten technischen Systeme. In diesem Fall liegt eine fachliche Trennung mit wenig Abhängigkeiten noch auf der Hand.
Sobald eine Gruppe von Nutzenden bedient wird, die in der gleichen Organisations(ober/unter)einheit arbeitet, verwendet man häufig genau ein System: z.B. ein Logistik-System, ein Einkaufs-System, ein Faktura-System, etc., denn nun liegen die Use Cases - aufgrund der organisatorischen Aufteilung nach Tätigkeiten - alle fachlich nah beieinander. Nun weiter fachlich ohne viele Abhängigkeiten zu schneiden ist schwierig (s.o.: Gefahr des verteilten Monolithen).
Ich muss jetzt einen Weg finden, Use Cases/Features einer einheitlichen Nutzendengruppe so in Services/Teams zu arrangieren, dass sie mit hoher Wahrscheinlichkeit komplett allein in einem Team bearbeitet werden, damit ich die Kopplung zwischen den Teams vermeide.
Frühere Ansätze in der Enterprise Architektur versuchen eine Zerlegung von Software anhand von Prozessen, Funktionen, Geschäftsobjekten, Geschäftsfähigkeiten vorzunehmen. Ziele dieses Ansatzes sind Redundanzvermeidung und klare Trennungen von Daten und den darauf möglichen Operationen. Innerhalb des Wirkungsbereichs eines Teams (1 bis wenige Services) sind diese Ziele sehr wertvoll. Sie erhalten die Wartbarkeit und Veränderbarkeit. Problematisch wird es, wenn man dieses Prinzip über die Grenzen von Teams und deren Systemen ausdehnen möchte. Es wird weitgehend unterschätzt, wie viel Zeit und Aufwand durch Koordination von Organisationseinheiten entsteht. Insbesondere, wenn viele Abhängigkeiten zwischen vielen Teams bestehen, benötigt Redundanzfreiheit als ein zentrales Architekturziel intensive Koordination bei Entwicklung und Betrieb. Teams müssen sich gegenseitig häufig Anforderungen stellen und die Systeme mehrerer Teams sind im Betrieb aneinander gekoppelt.
In einer Microservice-Architektur, die der Entwicklungsbeschleunigung durch Parallelisierung dienen soll, ist der Ansatz der Redundanzvermeidung als oberste Direktive daher kontraproduktiv, weil er eine starke Kopplung zwischen Teams und damit eine deutliche Verlangsamung bedeutet. Wenn das Ziel Geschwindigkeit ist, muss man Redundanzvermeidung innerhalb von Teams also anders bewerten. Man braucht einen Ansatz, der Entkopplung bei Makroarchitekturentscheidungen (i.e. teamübergreifend) höher gewichtet als Redundanzvermeidung.
Beispiel zu Redundanzvermeidung vs. Entkopplung
In einer microservicebasierten E-Commerce-Plattform müsste eigentlich jedes Team, welches Artikeldaten verwendet - das gilt in der Regel für alle - diese von einem "Artikel"-System zur Laufzeit holen. Lieferte dieses Artikel-System anderen Services zur Laufzeit dann die Daten, entspräche das einer Art "geteilter Datenbank". Das würde bedeuten, alle Teams würden bzgl. der Modellierung/Repräsentation etc. miteinander bei jedem neuen Feature und dem gesamten Betrieb um das "Artikel"-System-Team und dessen Ressourcen konkurrieren. Jedes Team würde so jedes andere blockieren.
Vermutlich würden bestimmte Teams aus Laufzeitgründen spezielle Datenrepräsentationen anfordern, weil sie nur wenige Daten einer Artikel-Variante benötigen (z.B. die Daten einer Warenkorbposition), während andere komplette Artikelstrukturen mit Varianten und zig Attributen benötigen (z.B. eine Artikeldetail-Seite).
Um die Dopplung von Fachlogik zu vermeiden, würde das "Artikel"-System noch viele Aussagen zu Artikeln anbieten. Und es würde die fachlichen Fragen der anderen Teams zu einem Artikel beantworten, die sich aus den Daten ableiten lassen. Dies ließe die Kopplung aller Teams, die dieses System zur Laufzeit nutzen, noch größer werden, denn das Team mit dem "Artikel"-System müsste alle Fachlichkeiten der anderen Teams verstehen und umsetzen. Wollte man die Redundanzvermeidung auf die Spitze treiben, müsste jedes Team jede neue Funktion, die auf Artikeldaten basiert, vom "Artikel"-System-Team umsetzen lassen, um sicher zu sein, dass keine Redundanzen entstehen. Als weitere Folge müsste das "Artikel"-System-Team auch Logik-Änderungen koordinieren: Versionierung von Schnittstellen und deren Verwaltung ist teuer.
Der Effekt in diesem Beispiel ist offensichtlich: Die Teams der anderen Microservices können zwar handwerklich unabhängig arbeiten, aber bei Betrieb, Planung, Entwicklung, Priorisierung usw. sind sie durch ein zentrales Artikel-System extrem stark aneinander gekoppelt.
Bei einer Vertikalisierung nimmt man dagegen Redundanzen billigend in Kauf. Wichtiger ist, dass Teams möglichst häufig Use Cases/Features vollkommen unabhängig von anderen bauen können.
D.h. in unserem Beispiel gäbe es zwar ein Team, was ein "Artikel"-System baut, aber es böte die Artikeldaten nur zur Replikation über eine asynchrone Schnittstelle an. Konsumierende Systeme wären dadurch nicht zur Laufzeit abhängig und könnten daher auch überflüssige Daten in einer Replikation akzeptieren, da die Passgenauigkeit der Replikationsformate auf das Laufzeitverhalten keinen Einfluss hat. Geschäftslogik rund um die replizierten Artikeldaten würde im Ernstfall mehrfach entwickelt. Wäre allerdings der fachliche Schnitt gut gewählt, träte dieser Fall nicht sehr häufig auf. Und in der Regel würde sogar deutlich, dass sich mit der Zeit doch immer wieder feine Unterschiede bei scheinbar gleichen Logiken ergeben.
Man würde diese Vorteile natürlich mit Effekten von Redundanz bezahlen: Daten existieren mehrfach in verschiedenen Systemen. Geschäftslogik wird manchmal mehrfach entwickelt. Und Konsistenzgarantien würden geringer: Nicht jedes System hätte immer zu einem Zeitpunkt die gleichen Daten wie alle anderen im Zugriff. Die Teams können und müssen für ihre Features Redundanzeffekte gegen die Entkopplungsvorteile abwägen: Die Nutzbarkeit darf nicht kritisch leiden. Das bedeutet, dass Redundanzen zwischen Teams ein grundsätzliches Konzept sind, um die Koordinationsaufwände zwischen Teams zu minimieren. Dahinter steckt die Erfahrung, dass die Koordinationsverluste meistens deutlich schmerzhafter sind als die Verluste durch Redundanzen - wenn man die Schnitte der Systeme gut wählt.
Vertikalisierung
Aber wie findet man jetzt die fachlichen Schnitte für die Teams und ihre Systeme?
Wir wollen eine fachliche Trennung der Teams erreichen, die die Wahrscheinlichkeit von Kopplungen bei der Arbeit verhindert. Das betrifft sowohl das Pflegen bestehender Features, als auch die Entwicklung von neuen Features. Des Weiteren soll die fachliche Trennung der Teams auch für die Zukunft stabil sein. D.h. wir wollen erreichen, dass der fachliche Schnitt der Systeme in der Zukunft aufgrund veränderter Anforderungen mit möglichst geringer Wahrscheinlichkeit verändert werden muss.
Die zentrale Idee der Vertikalisierung ist, von den Problemsituationen der Nutzenden als Schnittmuster auszugehen. Das bedeutet, nicht von Geschäftsfähigkeiten und ihren Daten als Schnittmuster auszugehen. D.h. ein Team ist nicht mehr primär für eine "Geschäftsfähigkeit" oder bestimmte "Daten" verantwortlich, sondern es ist primär für alle Features verantwortlich, die eine konkrete Problemmotivation der Nutzenden adressieren.
Dazu müssen die Probleme, Motivationen, Bedürfnisse der Nutzendengruppe analysiert und in Teile zerlegt werden, die alle Use Cases abdecken.
Im E-Commerce besteht eine Nutzenden-Motivation darin, etwas zu "suchen". Das können bestimmte Artikel oder Dienstleistungen sein. Wenn etwas Attraktives gefunden wird, wechselt die Motivation der Nutzenden von "suchen" über zu "entscheiden", ob es das Richtige ist. Wenn es nicht das Richtige ist, wechselt sie vielleicht wieder zurück zu "suchen". Wenn es das Richtige ist, verändert sich die Motivation zu "kaufen". Dementsprechend kann man Teams und Systeme nach diesen Motivationen schneiden.
Die Annahme an dieser Stelle ist, dass die Problemmotivationen der Nutzenden über die Zeit stabil bleiben, auch wenn die Kontexte, wie sie von den Nutzenden erlebt und von den Teams gelöst werden, sich stark ändern können. Damit wird insbesondere dem Problem Rechnung getragen, dass die fachliche Architektur (und damit auch die organisatorische und die technische) trotz aller heutiger Dynamik eine hohe Stabilität erreicht. Ob ein Team z.B. ein Nutzendenproblem bisher nur in einem Web-User Interface löst oder plötzlich ein Conversational-User Interface oder eines mit Gesten verwendet: Die Problemmotivation der Nutzenden bleibt gleich. Somit bleiben auch die zugehörigen Use Cases bei dem gleichen Team.
Eine weitere Folge dieses Ansatzes ist, dass ein Team die gesamte technische Lösung für den Use Case-Raum besitzen muss. Sonst müsste es sich wieder bei der Featureentwicklung mit anderen Teams koordinieren. Darum vermeiden wir horizontale Schnitte. Ein Team besitzt für seine Use Cases alle zugehörigen User Interfaces, die Geschäftslogik und die Datenbanken. Das technische Konzept der Self-Contained Systems passt genau dazu. Aus diesem Grund verwenden wir es als technische Vorgabe, um die Entkopplung auch auf technischer Ebene zu forcieren.
Fokussieren sich Teams auf Motivationsfelder einer Nutzendengruppe, sind die Use Cases sehr kohärent und ein Team kann sie unabhängig umsetzen. Jetzt sind die Teams nicht nur handwerklich weitgehend unabhängig, sondern auch langfristig fachlich entkoppelt, obwohl sie die gleichen Nutzenden bedienen. Und sie können sogar neue Use Cases ertesten, ohne sich mit den anderen Teams fachlich zu überschneiden. Das zahlt sich besonders aus, wenn man die Organisationsstruktur an diesen Teams orientiert, doch damit beschäftigen wir uns erst in Teil 3...
Im folgenden Teil 2 geht es um die konkreten Schnitte anhand der Motivationen der NutzerInnengruppe im Bereich des E-Commerce.
Noch mehr Informationen zum Thema gibt es auf unserer Übersichtsseite zur Vertikalisierung.