Zurück zur WebseiteInsights

Komplexität navigieren: Von Verwirrung zu Klarheit durch klare Perspektiven

By Marco Schäck
Published in Softwareentwicklung
October 24, 2025
9 min read
Komplexität navigieren: Von Verwirrung zu Klarheit durch klare Perspektiven

Egal wer wir sind — ob Entwickler, Architekten oder Berater — in den meisten IT-Projekten werfen wir mit dem Wort “Komplexität” umher wie mit Konfetti auf einer Silvesterparty. “Das ist zu komplex”, heißt es oft. “Lass uns hier die Komplexität reduzieren.” Aber hast du schon einmal innegehalten und dich gefragt: Wovon reden wir hier eigentlich genau?

Durch Recherche und Reflexion zu dieser Frage habe ich zwei Perspektiven entdeckt, die meine Herangehensweise an Komplexität in der Softwareentwicklung verändert haben: das Verständnis dafür, wo Komplexität in unseren Systemen lebt (lokal versus global) und warum sie überhaupt existiert (essenziell versus akzidentell). Diese Rahmenwerke bieten einen strukturierteren Weg, um Komplexitätsdiskussionen und -entscheidungen anzugehen.

Dieser Post ist jedoch nicht als kompakte Erkundung nur dieser beiden Perspektiven gedacht. Stattdessen ist es eine breitere Reflexion über meine Erkenntnisse zur Komplexität — einschließlich Exkursen zu verwandten Ideen und Präventionsstrategien. Mein Ziel ist es, Einsichten zu teilen, die dir nicht nur dabei helfen, Komplexität systematischer anzugehen — sondern auch tiefere Reflexion über das Thema fördern.

Warum ich begonnen habe, “Komplexität” zu hinterfragen

Ich habe unzählige Stunden in Retros, Pair-Programming-Sessions und Architektur-Diskussionen verbracht, in denen alle wissend über Komplexität genickt haben. Dennoch vermute ich, dass wir oft über völlig verschiedene Dinge sprechen. Es ist wie eine Gruppe von Menschen, die einen Elefanten beschreiben, während sie verbundene Augen haben — jeder berührt einen anderen Teil und zieht völlig unterschiedliche Schlüsse.

Denk an das letzte Mal, als dein Team in einer endlosen Schleife von “das ist komplex”-Aussagen stecken geblieben ist, ohne Fortschritte zu machen. Kommt dir bekannt vor?

Mir fiel auf, dass wir ohne eine präzisere Art, über Komplexität zu diskutieren, immer wieder oberflächliche Gespräche führten, die nirgendwo hinführten. Egal ob wir über Refactorings debattierten oder eine Architektur planten, dieselben vagen Beschwerden kamen immer wieder auf, während die echten Probleme verborgen blieben.

Dieses Muster wurde unmöglich zu ignorieren:

  • Wir identifizierten etwas als “komplex”, konnten uns aber nicht darauf einigen, was es komplex machte
  • Lösungen verfehlten oft ihr Ziel, weil wir verschiedene Probleme lösten
  • Team-Diskussionen fühlten sich frustrierend zirkulär an, alle redeten aneinander vorbei

Das waren keine isolierten Vorfälle — sie waren Symptome eines tieferen Kommunikationsproblems, das meine Neugier weckte, tiefer zu graben, was Komplexität in unserem Kontext tatsächlich bedeutet.

📖 Ein kurzer Exkurs: Komplex vs. Kompliziert

Bevor wir tiefer eintauchen, gibt es eine wichtige Unterscheidung, die es wert ist, geklärt zu werden und die oft unsere Komplexitätsdiskussionen verwirrt: der Unterschied zwischen den Begriffen “komplex” und “kompliziert.” Während wir diese Begriffe im Alltagsgespräch als Synonym verwenden, beschreiben sie grundlegend verschiedene Phänomene.

Wenn Dinge wie Softwaresysteme kompliziert sind, haben sie viele Teile, aber die Beziehungen zwischen diesen Teilen sind vorhersagbar und gut verständlich. Denk an eine mechanische Uhr. Sie hat Hunderte von Komponenten, aber jeder Teil hat eine klare, deterministische Funktion.

Wenn Dinge hingegen komplex sind, zeigen sie emergente Verhaltensweisen aus den Interaktionen ihrer Teile. Das Verhalten des Ganzen kann nicht einfach durch das Verstehen der einzelnen Komponenten vorhergesagt werden. Betrachte einen Stau zur Hauptverkehrszeit. Selbst wenn du das Verhalten jedes Fahrers verstehst, kannst du nicht genau vorhersagen, wie der Verkehr fließen wird oder wo sich Engpässe bilden werden.

Warum dies für das Diskussionen rund um Komplexität wichtig ist

Diese Unterscheidung ist entscheidend, weil sie unseren Lösungsansatz prägt. Komplizierte Probleme reagieren gut auf Zerlegung und systematische Analyse. Komplexe Probleme hingegen erfordern verschiedene Strategien wie Experimente, Anpassung und die Akzeptanz, dass manche Unsicherheit nicht eliminiert werden kann.

Die Perspektiven, die ich unten teilen werde, adressieren primär Komplexität in Softwaresystemen. Jene unvorhersagbaren Eigenschaften, die entstehen, wenn Komponenten auf Weisen interagieren, die schwer vollständig zu antizipieren oder zu kontrollieren sind. Das Verständnis dieser Unterscheidung hilft uns, die richtigen Werkzeuge für jeden Problemtyp auszuwählen.

Die zwei Arten von Komplexität

Im Rahmen meiner Analyse entdeckte ich einige Ansätze, um Komplexität zu kategorisieren und darüber nachzudenken. Vor allem zu zweien von diesen habe ich mich jedoch besonders hingezogen gefühlt, da durch diese die meisten Probleme, die ich erlebte, gelöst werden konnten. Zudem erweisen sie sich als besonders nützlich, weil sie sich ergänzen. Sie umfassen:

  • Lokale & Globale Komplexität: Beantwortung der Frage, wo Komplexität in unseren Systemen lebt
  • Essenzielle & Akzidentelle Komplexität: Betrachtung, warum die Komplexität überhaupt existiert

Lass mich diese beiden Konzepte mit einem konkreten Beispiel illustrieren.

Unser Beispiel: Ein E-Commerce-Benachrichtigungssystem

Stell dir vor, du arbeitest an einer Microservice-Architektur für eine E-Commerce-Plattform. Dein Team verwaltet den Benachrichtigungsservice, der Bestellbestätigungen, Versand-Updates und Werbe-E-Mails versendet. Im Laufe der Zeit ist dieser scheinbar einfache Service zu einem Auslöser ständiger Kopfschmerzen geworden.

Lass uns dieses Szenario nutzen, um zu erkunden, wie diese Perspektiven uns helfen könnten, anders zu denken.

Die “Wo”-Frage: Komplexität in deinem System lokalisieren

Die erste Dimension konzentriert sich darauf, wo sich Komplexität in unseren Systemen manifestiert: Ob sie innerhalb einzelner Komponenten konzentriert ist oder aus deren Interaktionen entsteht. Diese Perspektive wird detailliert von Vlad Khononov in seiner Arbeit über untangling microservices und seinem Buch Learning Domain-Driven Design erkundet.

Lokale Komplexität: Der Teufel steckt im Detail

Wenn die meisten Entwickler den Begriff “Komplexität” hören, ist das wahrscheinlich das, was ihnen zuerst in den Sinn kommt. Es ist der Typ, dem wir täglich in Code-Reviews und Debugging-Sessions begegnen, was ihn zur vertrautesten und unmittelbarsten Form der Komplexität macht.

Lokale Komplexität residiert innerhalb einzelner Komponenten, Funktionen oder Services. Es ist die verworrene bedingte Logik, die verschachtelten Schleifen, die Daten verarbeiten, oder die Zustandsmaschinen, die Workflows handhaben. Betrachte diese Funktion aus unserem Benachrichtigungsservice:

// Beispiel für lokale Komplexität
fun formatNotification(user: User, event: Event, preferences: Preferences) {
  if (event.type == "order" && user.tier == "premium") {
    if (preferences.email && preferences.promotions) {
      // 15 weitere Zeilen bedingter Logik...
    }
  }
  // Du verstehst die Idee...
}

Funktionen wie diese sind lokal komplex — schwer zu verstehen, zu testen und isoliert zu modifizieren. Doch dieser nach innen gerichtete Fokus auf einzelne Komponenten erzählt nur die halbe Geschichte.

Globale Komplexität: Das Netz, das wir weben

Globale Komplexität entsteht aus den Interaktionen zwischen Komponenten, Services und Systemen. Es geht nicht darum, dass ein einzelnes Stück kompliziert ist; es geht darum, wie die Stücke zusammenpassen und sich gegenseitig beeinflussen.

Um diese Perspektive klarer zu illustrieren, betrachte, wie unser Benachrichtigungsservice eine typische Bestellbestätigung orchestriert:

fun sendOrderConfirmation(orderId: String) {
  val order = orderService.getOrder(orderId)
  val user = userService.getUser(order.userId)
  val preferences = preferencesService.getPreferences(user.id)
  
  if (shouldSendEmail(user, preferences)) {
    val template = templateService.getTemplate("order_confirmation")
    val personalizedContent = personalizationService.customize(template, user, order)
    emailService.send(personalizedContent, user.email)
    
    auditService.logNotification(user.id, "email_sent")
    metricsService.recordEvent("notification.sent", "email")
  }
}

Jeder einzelne Service-Aufruf mag einfach sein, aber die koordinierte Choreografie zwischen sechs verschiedenen Services führt zu globaler Komplexität. Die Herausforderungen entstehen nicht aus der individuellen Logik, sondern aus Netzwerklatenz, Service-Ausfällen, eventueller Konsistenz und kaskadierenden Fehlern.

Die “Warum”-Frage: Den Ursprung der Komplexität verstehen

Die zweite Dimension untersucht, warum Komplexität existiert — ob sie ein unvermeidlicher Teil des Problems ist, das wir lösen, oder ob wir sie durch unsere Designentscheidungen geschaffen haben. Diese Unterscheidung wurde ursprünglich von Fred Brooks in seinem einflussreichen Essay “No Silver Bullet” artikuliert.

Essenzielle Komplexität: Das unvermeidbare Herz des Problems

Essenzielle Komplexität ergibt sich direkt aus dem Problem, das wir lösen. Es ist die inhärente Schwierigkeit, die im Problem selbst liegt, nicht in unserer Lösung. Egal wie geschickt wir beim Design sind, diese Komplexität kann nicht eliminiert werden — nur verwaltet, gekapselt oder an eine andere Ebene verlagert werden.

In unserem E-Commerce-Beispiel stellen Geschäftsregeln wie diese essenzielle Komplexität dar:

  • Premium-Kunden erhalten andere Benachrichtigungsformate als Standard-Kunden
  • Bestimmte Produktkategorien erfordern regulatorische Warnungen in spezifischen Regionen
  • Benutzer können komplexe Präferenz-Kombinationen haben (E-Mail aber keine SMS, Werbe-E-Mails aber keine Versand-Updates)

Diese Regeln existieren, weil das Geschäft sie benötigt. Du kannst sie in deinem Code klarer ausdrücken, aber du kannst sie nicht wegwünschen.

Akzidentelle Komplexität: Selbst geschaffene Probleme

Akzidentelle Komplexität entsteht aus den Werkzeugen, Frameworks, Designentscheidungen und Implementierungsdetails, die wir wählen. Es ist Komplexität, die wir eingeführt haben, die aber nicht notwendig für das Problem ist, das wir lösen.

Häufige Quellen akzidenteller Komplexität in unserem Benachrichtigungsservice könnten sein:

  • Over-Engineering: Verwendung komplexer Muster, obwohl einfache ausreichen würden
  • Technische Schulden: Ansammlung von Workarounds und Quick-Fixes über die Zeit
  • Schlechte Service-Grenzen: Services aufteilen auf eine Weise, die unnötige Koordination schafft
  • Framework-Overhead: Komplexität, die von den Werkzeugen stammt, die wir gewählt haben

Das Erkennen akzidenteller Komplexität ist entscheidend, weil es das ist, was wir tatsächlich reduzieren können, ohne die Geschäftsanforderungen zu beeinträchtigen.

Die Perspektiven kombinieren: Ein 2x2-Framework

Als ich die Erkundung jeder Perspektive für sich abgeschlossen hatte, fragte ich mich, ob ihre Kombination zusätzliche Einsichten schaffen könnte, die keine allein bieten konnte. Dieses Experiment der Verbindung der “Wo”- und “Warum”-Dimensionen offenbarte vier logische Kombinationen, jede mit verschiedenen Charakteristika der Komplexität und spezifischen Strategien zu ihrer Bewältigung:

1. Lokal + Essenziell: Kern-Domänen-Logik

Wenn Geschäftsregeln im Code implementiert werden, erhältst du typischerweise komplexe Domänen-Logik innerhalb einzelner Funktionen oder Klassen. Das Ziel ist nicht die Eliminierung, sondern ordnungsgemäße Kapselung und klarer Ausdruck. Ich würde Domain-Driven Design Prinzipien als Strategien zur Beherschung dieser inhärenten Komplexität zu betrachten.

2. Lokal + Akzidentell: Unnötige Code-Komplexität

Wenn wir unnötige Komplexität innerhalb von Komponenten schaffen, enden wir mit Code, der komplexer ist als nötig. Das resultiert typischerweise aus vorzeitiger Optimierung oder falsch angewandten Mustern. Refactorings, Vereinfachung und das Entfernen unnötiger Abstraktionen könnten es wert sein, als Ansätze zur Bewältigung dieser Komplexität erkundet zu werden.

3. Global + Essenziell: Inhärente System-Koordination

Wenn Domänen-Anforderungen Koordination über mehrere Komponenten hinweg erfordern, stehst du vor der inhärenten Komplexität der Orchestrierung von Services oder der Verwaltung verteilter Zustände. Klare Grenzen, ereignisgesteuerte Architekturen und eventuell konsistente Muster könnten helfen, die Koordinationsherausforderungen zu bewältigen, die natürlich in verteilten Systemen entstehen.

4. Global + Akzidentell: Architektur-Schulden

Wenn schlechte Designentscheidungen unnötige systemweite Komplikationen schaffen, erhältst du Komplexität, die durch fehlerhafte Service-Grenzen, geteilte Datenbanken oder angesammelte technische Entscheidungen eingeführt wurde. Service-Redesign, klare Schnittstellen und systematische Entkopplungsstrategien könnten Ansätze sein, die für die Bewältigung von Komplexität, die aus architektonischen Entscheidungen anstatt aus Domänen-Anforderungen stammt, zu betrachten sind.

Ein Ansatz, um Komplexitätsperspektiven zu kombinieren & kategorisieren
Ein Ansatz, um Komplexitätsperspektiven zu kombinieren & kategorisieren

Was diese Perspektiven für mich verändert haben

Seit der Erkundung dieser Konzepte denke ich, dass man präziser über Komplexität denken und sprechen kann. Meiner Meinung & Erfahrung nach kann das in mehreren Kontexten hilfreich sein.

Architektur- und Design-Diskussionen werden zielgerichteter. Anstatt vage zu sagen “dieses Service-Design scheint zu komplex”, könnte man tiefere Fragen erkunden wie:

“Kommt diese Komplexität aus dem tatsächlichen Geschäftsproblem, oder haben wir sie geschaffen, weil wir Service-Grenzen an den falschen Stellen gezogen haben?”

Oder bei der Bewertung von Design-Alternativen können Teams betrachten:

“Wenn wir diese Services konsolidieren, reduzieren wir die Gesamtkomplexität — oder verschieben wir nur globale & lokale Komplexität?”

Code-Reviews könnten ebenfalls profitieren. Anstatt einfach eine Funktion als “komplex” zu kennzeichnen, könnte man konstruktiveres Feedback geben:

“Diese Funktion handhabt mehrere Geschäftsregeln, was etwas lokale Komplexität schafft. Wir könnten Muster wie das Strategy-Pattern einführen, um das handhabbarer zu machen?”

Die breiteren Vorteile, die ich sehe

Zusammenfassend: Basierend auf meiner Analyse, Reflexion und Nutzung dieser Perspektiven glaube ich, dass mehrere Vorteile entstehen, wenn Teams Komplexität systematischer angehen. Sie umfassen:

  • Schärfere konzeptionelle Klarheit. Das Verständnis der Unterscheidung zwischen “komplex” und “kompliziert” sowie der verschiedenen Typen und Perspektiven der Komplexität bietet eine klarere Sicht auf das, womit wir es tatsächlich zu tun haben. Diese konzeptionelle Grundlage ist wesentlich für bessere Entscheidungen.
  • Strategischerer Aufwand und realistischere Erwartungen. Teams können ihre Energie dort fokussieren, wo es am wichtigsten ist — Reduktion akzidenteller Komplexität während sie akzeptieren, dass essenzielle Komplexität Teil der Problemdomäne ist. Dieser Ansatz hilft bei der systematischeren Bewertung von Trade-offs und hilft Teams zu erkennen, ob sie wirklich Komplexität reduzieren oder sie nur im System umverteilen.
  • Bessere Kommunikation führt zu zielgerichteten Lösungen. Ein spezifisches Vokabular für verschiedene Arten von Komplexität zu haben hilft Teams zu diskutieren, was sie tatsächlich zu lösen versuchen und zu entscheiden, ob Komplexität es wert ist, dokumentiert, refaktoriert oder als gegeben akzeptiert zu werden, was natürlich zu zielgerichteteren architektonischen und Code-Level-Lösungen führt.

Erwartungen realistisch halten

Seien wir realistisch — wird jeder magisch zu dieser Denkweise wechseln? Wahrscheinlich nicht. Zumindest nicht sofort. Teams dazu zu bringen, ihre Art zu ändern, wie sie über Probleme sprechen, ist notorisch schwierig. Und nicht jeder wird diese Unterscheidungen so nützlich finden wie ich. Team-Dynamiken, Zeitdruck und vorhandenes Wissen sowie Kommunikationsmuster beeinflussen alle, ob Ansätze wie dieser tatsächlich haften bleiben.

Aber selbst wenn es den Ansatz deines ganzen Teams nicht über Nacht transformiert, könnte dieses Framework dir helfen, bessere Fragen zu stellen oder zielgerichtetere Lösungen vorzuschlagen. Manchmal reicht das aus, um ein Gespräch in eine produktivere Richtung zu lenken.

Eine Notiz zur Prävention: Über das Verwalten von Komplexität hinaus

Während diese Perspektiven uns helfen, existierende Komplexität zu verstehen und zu diskutieren, ist es genauso wichtig, unnötige Komplexität daran zu hindern, überhaupt erst eingeführt zu werden. Die Frameworks, die ich geteilt habe, konzentrieren sich darauf, Komplexität zu navigieren, sobald sie vorhanden ist, aber wie vermeiden wir es, akzidentelle Komplexität von Anfang an zu schaffen?

Hier sind einige proaktive Strategien — allerdings sicherlich keine erschöpfende Liste:

Entscheidungstransparenz und Dokumentation
Das Explizitmachen architektonischer Entscheidungen hilft Teams, vergangene Fehler nicht zu wiederholen und bietet Kontext für zukünftige Änderungen. Architecture Decision Records (ADRs) sind hier besonders wertvoll. Mein Kollege hat einen Post über ADRs geschrieben, der ihre Grundlagen abdeckt. Wenn Entscheidungen als ADRs dokumentiert werden, können Teams besser bewerten, ob sie essenzielle oder akzidentelle Komplexität einführen — oder ob es überhaupt notwendig ist, die Alternativen bedenkend.

Kontinuierliche architektonische Verbesserung
Regelmäßige Diskussions- und Review-Prozesse verhindern, dass sich Komplexität unbemerkt ansammelt. Die Thoughtworks Technology Podcast Serie über den Architecture Advice Process beschreibt eine Methodologie, die alle Team-Mitglieder in architektonische Entscheidungen einbezieht, nicht nur wenige Ausgewählte. Dieser Ansatz fördert eine Kultur der kontinuierlichen Verbesserung anstatt reaktiven Komplexitätsmanagements.

Fitness-Funktionen und automatisierte Leitplanken
Automatisierte Werkzeuge können viele Formen akzidenteller Komplexität verhindern. Architektur-Test-Tools wie ArchUnit kodieren Designentscheidungen als ausführbare Tests und fangen Verletzungen ab, bevor sie sich festsetzen. Ähnlich stellen Tools wie Konsist, ktLint, ESLint oder Prettier sicher, dass Teams bei vereinbarten Coding-Standards bleiben und die graduelle Drift verhindern, die oft zu unnötiger Komplexität führt.

Dieses ganze Thema automatisierter Komplexitätsprävention verdient tiefere Erkundung — etwas, was ich in einem zukünftigen Post zu behandeln plane.

Diese Präventionsstrategien ergänzen die Perspektiven, die wir diskutiert haben und ermutigen zum Denken über das bloße Verwalten existierender Komplexität hinaus. Das Ziel ist nicht, alle Komplexität zu eliminieren — essenzielle Komplexität wird immer existieren — sondern absichtlicher bezüglich der Komplexität zu sein, die wir durch unsere Entscheidungen einführen.

Einen Versuch wert?

Ich bin neugierig, ob diese Perspektiven mit deiner Erfahrung mitschwingen. Versuch vielleicht, ein System zu betrachten, das dein Team als “komplex” betrachtet, durch diese Linsen:

  1. Wähle etwas aus, womit dein Team kämpft
  2. Versuche zu kategorisieren, welche Komplexität du siehst
  3. Bemerke, ob es ändert, wie du über potenzielle Lösungen denkst

Was sind deine Erfahrungen?

Ich bin wirklich neugierig, wie andere über Komplexität denken und diskutieren. Obwohl diese Konzepte für meine Arbeit wertvoll waren, weiß ich trotzdem, dass jedes Team und jeder Kontext verschiedene Herausforderungen mit sich bringen.

Daher meine Frage: Welche mentalen Modelle verwendest im Anblick von Komplexität? Hast du effektive Wege entdeckt, Komplexität mit deinen Teams zu diskutieren — oder stehst du vor Herausforderungen, die sich einer Kategorisierung zu widersetzen scheinen?

This post was originally published in English on Medium.com.


Tags

#softwareentwicklung#clean-code#komplexität
Previous Article
Komplexität meistern – Wie Prozessautomatisierung gelingt
Marco Schäck

Marco Schäck

Fullstack Entwickler

Inhalte

1
Warum ich begonnen habe, "Komplexität" zu hinterfragen
2
📖 Ein kurzer Exkurs: Komplex vs. Kompliziert
3
Die zwei Arten von Komplexität
4
Unser Beispiel: Ein E-Commerce-Benachrichtigungssystem
5
Die "Wo"-Frage: Komplexität in deinem System lokalisieren
6
Die "Warum"-Frage: Den Ursprung der Komplexität verstehen
7
Die Perspektiven kombinieren: Ein 2x2-Framework
8
Was diese Perspektiven für mich verändert haben
9
Die breiteren Vorteile, die ich sehe
10
Erwartungen realistisch halten
11
Eine Notiz zur Prävention: Über das Verwalten von Komplexität hinaus
12
Einen Versuch wert?
13
Was sind deine Erfahrungen?

Ähnliche Beiträge

Software Architektur dokumentieren
January 27, 2025
3 min
© 2025, All Rights Reserved.

Links

StartseiteLösungenBPM-TrainingKarriereInsights

Social Media