JAX https://jax.de/ Java, Architecture & Software Innovation Tue, 28 Jul 2026 14:32:33 +0000 de-DE hourly 1 https://wordpress.org/?v=6.9.5 Taktisches DDD für wartbaren KI-generierten Code https://jax.de/blog/taktisches-ddd-ki-generierter-code/ Thu, 23 Jul 2026 13:56:41 +0000 https://jax.de/?p=210668 Copiloten generieren in Sekunden lauffähigen Code, doch wer übernimmt die Verantwortung für fachliche Integrität, Wartbarkeit und Architektur? Dieser Artikel zeigt, warum taktisches Domain-Driven Design im KI-Zeitalter zum unverzichtbaren Ordnungsprinzip wird und wie es als Leitplanke für generierten Code dient.

The post Taktisches DDD für wartbaren KI-generierten Code appeared first on JAX.

]]>

Hinweis: Dieser Podcast und dieses Video wurden mithilfe von KI erstellt. Dabei wurden die Originalinhalte und technischen Erkenntnisse des Autors des Blogbeitrags adaptiert.

Der aktuelle State of the Art im Software Engineering ist untrennbar mit Künstlicher Intelligenz verbunden. Wo früher erhebliche Zeit in das Durchforsten von Bibliotheks-Dokumentationen, das Schreiben von repetitivem Boilerplate-Code oder das mühsame Aufsetzen technischer Grundgerüste floss, liefern Copiloten heute in Sekundenschnelle erste Lösungsvorschläge. Das reine Beherrschen einer Programmiersprache, also das Wissen um die exakte Grammatik und die verfügbaren Standard-APIs, verliert deutlich an Bedeutung, da die Maschine diese Zusammenhänge auf Abruf bereitstellt. Der Begriff Copilot steht in dieser Serie stellvertretend für KI-gestützte Coding-Agents, sei es GitHub Copilot, JetBrains Junie, Cursor, Claude Code oder ein anderes Werkzeug. Die Argumentation gilt für alle gleichermaßen, weil sie auf der gemeinsamen Funktionsweise und nicht auf einem konkreten Produkt aufbaut.

Stay tuned

Regelmäßig News zur Konferenz und der JAX-Community erhalten

[mc4wp-simple-turnstile]

 

Vom Coder zum Architekten und Prüfer

Mit diesem Wandel verschiebt sich der Fokus. Die präzise Spezifikation rückt ins Zentrum der Arbeit. Es keimt die verlockende Vision auf, direkt von einer fachlichen Beschreibung zur fertigen technischen Lösung zu springen. Doch Vorsicht ist geboten: Die KI glänzt zwar durch syntaktisch korrekten und lauffähigen Code, nutzt diese Fähigkeit aber oft, um Lücken in einer ungenauen Spezifikation „kreativ” (und potenziell fachlich falsch) zu schließen. Der Mensch verlässt zunehmend die Rolle des Schreibenden und wird zum Architekten und Prüfer. Um diese Rolle auszufüllen, ist ein tieferes Verständnis der fachlichen Konzepte und architektonischen bzw. Softwaredesignvorgaben notwendiger denn je.

Agilität in Zeiten der Generierung

Trotz der Geschwindigkeit der KI bleibt Agilität der entscheidende Erfolgsfaktor. Warum?

  • Perfekte Anforderungen sind eine Illusion: Es war noch nie möglich, zu Projektbeginn perfekte Anforderungen zu erheben.
  • Lernen durch Tun: Der eigentliche Erkenntnisgewinn über fachliche Zusammenhänge entsteht erst während des Entwicklungsprozesses. Und Agilität ist die Disziplin, die daraus entstehendes Wissen kontinuierlich in das Projekt zurückführt.
  • Permanenter Wandel: Anforderungen und Rahmenbedingungen ändern sich heute schneller als je zuvor. Change kommt verstärkt von außen, die Welt dreht sich weiter.

Wir müssen Systeme also ständig anpassen und überprüfen. Genau hier wird Softwaredesign zur Voraussetzung von Agilität: Erst anpassbare und erweiterbare Strukturen machen das ständige Reagieren auf neue Erkenntnisse und veränderte Rahmenbedingungen wirtschaftlich tragfähig. Das gilt auf allen Ebenen der Architektur, im Rahmen dieser Artikelserie aber besonders für das Softwaredesign der Geschäftsdomäne.

Warum Tests und reine Fachprüfung allein nicht ausreichen

Es wäre jedoch zu kurz gegriffen, die neue Rolle des Menschen allein auf das Abgleichen von fachlichen Anforderungen mit den KI-Ergebnissen zu reduzieren. Ein funktionierendes System ist mehr als die Summe seiner generierten Funktionen. Es benötigt eine innere Ordnung. Wer sich blind auf die KI verlässt, erhält zwar lauffähige Bausteine, verliert aber das Systemverständnis für deren Zusammenspiel. In dieser Lücke zwischen „Code, der irgendwie funktioniert” und „nachhaltigem Softwaredesign” offenbart sich das Risiko der KI-gestützten Entwicklung.

Oft wird die Testabdeckung als Rettungsanker ins Feld geführt. Doch hier ist Vorsicht geboten: Eine 100%ige Testabdeckung ist in der Praxis eine Illusion und oft gar nicht sinnvoll. Vor allem aber heilt sie kein schlechtes Design. Wenn ein Test fehlschlägt, ist das lediglich ein Symptom. Ohne ein tiefes Verständnis des zugrunde liegenden Softwaredesigns lässt sich kaum beurteilen, ob wir lediglich ein technisches Detail korrigieren müssen oder ob das gesamte Konzept in sich widersprüchlich ist und angepasst werden muss. Ein Designdefizit führt zu handfesten Risiken: eine strukturelle Erosion des Codes, der Verlust des mentalen Modells beim Entwickler und eine schleichende logische Inkonsistenz, die durch oberflächliche Tests nicht abgefangen wird.

DIE KUNST DER SOTWARE-ARCHITEKTUR

Architecture & Design-Track entdecken

 

Taktisches DDD als Ordnungsprinzip

Hier tritt das taktische Domain-Driven Design (taktisches DDD) auf den Plan. Während das strategische DDD den Blick auf die großen fachlichen Zusammenhänge richtet, bietet das taktische DDD konkrete Entwurfsmuster für die Implementierung innerhalb einer Domäne.

Kurz gefasst ist taktisches DDD ein formaler Werkzeugkasten, um fachliche Regeln (Invarianten) direkt in die Struktur des Codes zu gießen. Statt Logik willkürlich über das System zu verstreuen, nutzt es spezifische Bausteine:

  • Entities: Objekte mit einer eindeutigen Identität, die einen Lebenszyklus durchlaufen (z. B. ein „Kunde”).
  • Value Objects: Unveränderliche Objekte, die durch ihre Werte definiert sind (z. B. eine „IBAN” oder ein „GeldBetrag”). Sie sind der Schlüssel zur Vermeidung technischer Primitive.
  • Aggregates: Eine Gruppe von Objekten, die als Einheit betrachtet werden. Das sogenannte Aggregate Root garantiert als einzige Zugriffsschnittstelle, dass alle fachlichen Regeln innerhalb dieser Gruppe jederzeit eingehalten werden.
  • Domain Services: Ort für Fachlogik, die nicht sinnvoll in eine einzelne Entity oder ein Value Object passt.

Diese Bausteine sorgen für Strukturen, die das Verständnis und die Wartbarkeit spürbar verbessern. Eine detaillierte Analyse dieser Bausteine und weiterer Bausteine sowie deren Implementierung im Zusammenspiel mit KI folgt in den Folgeartikeln dieser Serie anhand eines konkreten Praxisbeispiels.

Bevor wir dann die Vorteile von taktischem DDD im Rahmen KI-gestützter Softwareentwicklung etwas genauer betrachten, noch ein Blick auf einen verwandten, aktuell stark diskutierten Aspekt.

Der „Battle of Truth”: Domänenmodell vs. Spezifikation

In der aktuellen Architektur-Community tobt ein spannender Streit: Was ist die eigentliche Single Source of Truth? Die Fronten unterteilen sich dabei wie folgt:

  • Die Spec-Sovereignty-Fraktion: Sie argumentiert, dass die Spezifikation (z. B. in Form von strukturierten Markdown-Modellen, Mermaid-Diagrammen oder Agent-Blueprints) die Wahrheit ist. Der Java-Code ist (bald) nur noch ein „Artefakt”, das von einer KI aus der Spec generiert wird.
  • Die Code-Sovereignty-Fraktion (Klassisches DDD): Sie beharrt darauf, dass nur der Code die Wahrheit ist, da nur er die tatsächliche Laufzeit-Realität abbildet. Eine Spec kann lügen (ggf. einfach nur veraltet sein); Code tut es nicht.

Warum die Code-Souveränität das stabilere Fundament bleibt

Es spricht vieles dafür, dass das Primat des Codes bestehen bleibt – nicht aus Technikverliebtheit, sondern aus drei fundamentalen Gründen:

  • Realität zur Laufzeit: Der Code ist das einzige Artefakt, das tatsächlich ausgeführt wird. Alles davor ist Intention, alles danach Beobachtung. Eine Spezifikation beschreibt, was sein soll; der Code beschreibt, was ist.
  • Deterministische Verarbeitung: Ein Compiler liefert bei gleicher Eingabe immer dasselbe Ergebnis. Generative Modelle tun das nicht, nicht einmal annähernd. Diese Lücke zwischen Spec und Laufzeit muss durch den Code selbst geschlossen werden, durch die Art, wie er strukturiert ist. Taktisches DDD verfolgt dabei eine klare Grundhaltung: Die Fachlichkeit soll möglichst explizit im Code abgebildet werden, nicht zwischen den Zeilen mitgedacht oder in Konventionen verborgen. Value Objects und Aggregates sind die konsequente Umsetzung dieser Haltung: Sie kapseln Invarianten so, dass die fachliche Wahrheit nicht in einem variablen LLM-Output liegt, sondern im typisierten, kompilierten Code.
  • Präzision in begrenzten Kontexten: Eine textuelle Spezifikation kann Mehrdeutigkeiten enthalten, ohne dass es jemandem auffällt. Ein Typsystem kann das nicht, Money und BigDecimal sind verschiedene Dinge, und der Compiler zwingt zur Entscheidung. Diese Präzisionstiefe lässt sich allerdings nur dort erreichen, wo der Kontext überschaubar bleibt. Genau das leisten DDD-Bausteine: Ein Aggregate definiert eine klar abgegrenzte Verantwortungsgrenze, innerhalb derer sowohl Mensch als auch KI mit hoher Genauigkeit arbeiten können. Ohne solche Grenzen verliert sich jede Implementierung, egal ob von Hand geschrieben oder generiert, in der Komplexität des Gesamtsystems. Im Sinne des taktischen DDD ist der Code die exakteste und detailreichste Abbildung des Fachmodells. Hier werden Invarianten und Business-Regeln, Strukturen und Abläufe in einer Tiefe definiert, die eine rein textuelle Spezifikation kaum erreichen kann.

Die empirischen Belege für diese Grenzen aktueller LLMs, Nicht-Determinismus, Reasoning Failures, Kontext-Erosion, fassen wir im Kasten „Wissenschaftliche Evidenz: Warum die KI klare Strukturen braucht” zusammen.

Wissenschaftliche Evidenz: Warum die KI klare Strukturen braucht

Die im Haupttext genannten LLM-Schwächen sind empirisch belegt. Drei aktuelle Befunde sind für die DDD-Argumentation besonders relevant:

  • Nichtdeterminismus trotz Temperature 0 [2]: Selbst bei Temperature 0, also der Einstellung, die theoretisch Determinismus garantiert, variieren Cloud-LLM-Ausgaben signifikant. Implikation: Reproduzierbarkeit muss durch deterministische Abbildung der Fachlichkeit im Code erzwungen werden, unter anderem durch Typen, Tests, Guard-Clauses und Aggregate-Grenzen, nicht durch Prompt-Tuning.
  • Lost-in-the-Middle bei langen Kontexten [3]: Informationen in der Mitte langer Prompts werden de-priorisiert. Implikation: Kontextfenster klein halten. Also exakt das, was die Kapselung in DDD-Bausteinen erreicht. Aggregate-Grenzen sind dafür das prominenteste Beispiel; Value Objects, Domain Services und andere Bausteine wirken auf derselben Ebene.
  • Reasoning-Failures bei mehrstufiger Logik [4]: LLMs operieren statistisch, nicht deduktiv. Reasoning-Modelle und Agent-Ketten haben das Risiko von Schlussfolgerungs-Fehlern deutlich reduziert, eliminiert haben sie es nicht. Implikation: Der Prompt ist die Anweisung, der Code ist die Wahrheit. Geschäftsregeln können und sollen in Prompts vorkommen. Verbindlich werden sie aber erst dort, wo Aggregates und Assertions sie maschinell erzwingen. Mit jeder Erweiterung verschiebt sich das Gesamtzusammenspiel, und nur die im Code geprüften Regeln sichern die Konsistenz über die Zeit.

Engineering statt Illusion: Das Zusammenspiel der Wahrheiten

Dabei darf man sich keinen Illusionen hingeben: Weder eine perfekte Spezifikation noch eine lückenlose Testabdeckung sind in der Praxis wirklich erreichbar. Dennoch bleiben beide Säulen für qualitativ hochwertiges Software-Engineering unverzichtbar.

Eine saubere, strukturierte Spezifikation ist nach wie vor das Fundament jeder professionellen Entwicklung, sie ist die Quelle der Absicht. Doch so präzise sie auch sein mag, es bleibt aus den genannten Gründen (Nicht-Determinismus der KI, logische Unschärfen, Schwierigkeiten beim Umgang mit großen Kontexten) schwierig, die Spezifikation als die einzige Wahrheit anzusehen. Ebenso verhält es sich mit Tests: Eine 100%ige Abdeckung ist oft eine ökonomische und theoretische Illusion. Dennoch sind Unit-Tests kein optionaler Luxus, sondern essenzielles Engineering. Sie bilden die pragmatische Brücke, um die Kluft zwischen der Spezifikation und der tatsächlichen Ausführung so gering wie möglich zu halten.

Wenn wir akzeptieren, dass weder die Spec unfehlbar noch der Test lückenlos ist, rückt ein dritter Faktor ins Zentrum: Aufbau und Struktur des Codes. Nur weil wir nicht mehr jede Zeile von Hand schreiben, verliert Softwaredesign nicht an Bedeutung, im Gegenteil. Der strukturelle Aufbau beeinflusst maßgeblich die entscheidenden Qualitätsparameter, wie Änderbarkeit und Erweiterbarkeit.

Es ist ein gefährlicher Trugschluss zu glauben, dass Wartbarkeit im Zeitalter der KI „kostenlos” oder vernachlässigbar sei. Ein schlecht strukturierter „Big Ball of Mud” lässt sich auch mit KI-Unterstützung nur unter hohem Risiko und hohem Aufwand anpassen. Die kognitive Last beim Refactoring und die Gefahr von Seiteneffekten steigen ohne sauberes Design überproportional an, unabhängig davon, wer oder was die Tastatur bedient.

Da eine lückenlose formale Verifikation meist am Aufwand scheitert oder an theoretische Grenzen stößt, bleibt der Code die einzige finale Instanz der Wahrheit. Er ist die detailreichste Abbildung der Fachlichkeit. Aber nur dann, wenn die Codebasis selbst aufgeräumt und nachvollziehbar strukturiert ist. Ohne diese strukturelle Qualität funktioniert weder klassische Wartung noch KI-gestützte Weiterentwicklung. Für die Geschäftslogik fachlich getriebener Domänen liefert taktisches DDD diese Qualität auf besonders wirksame Weise: Es gibt dem Code eine Struktur, die ihn kontrollierbar, prüfbar und vor allem langfristig änderbar macht. In der Welt des DDD bleibt der Code damit das „Living Document”, das die Realität der Domäne widerspruchsfrei abbildet.

Stay tuned

Regelmäßig News zur Konferenz und der JAX-Community erhalten

[mc4wp-simple-turnstile]

 

Reasoning-Modelle und Agenten: Was sich ändert, was bleibt

Man könnte einwenden: Die hier beschriebenen LLM-Schwächen seien Stand 2024, längst eingeholt durch Reasoning-Modelle und agentische Systeme. Tatsächlich hat sich vieles bewegt. Modelle mit explizitem Reasoning-Schritt zeigen in formalen Logikaufgaben, mathematischen Beweisen und mehrstufigen Schlussfolgerungen deutlich bessere Ergebnisse als die Generation davor. Agentische Systeme können Code ausführen, Tests laufen lassen, Fehler beobachten und ihren Ansatz korrigieren. Sie sind nicht mehr auf einen einzigen Generierungsschritt ohne Rückkopplung angewiesen. Das verändert die DDD-Argumentation an einer Stelle und stärkt sie an einer anderen.

Verändert hat sich der Punkt der reinen Reasoning-Defizite. Ein modernes Reasoning-Modell wird einen Aggregate-Konsistenz-Check, der sich aus drei verschachtelten Invarianten ergibt, in vielen Fällen korrekt durchdenken. Das war 2024 noch eine zuverlässige Fehlerquelle, 2026 nicht mehr durchgängig. Auch die Halluzination einzelner API-Aufrufe ist seltener geworden, weil agentische Systeme ihre Annahmen gegen die echte Codebasis prüfen können.

Gestärkt hat sich dagegen das Kontextargument, und das ist die wichtigere Beobachtung. Agentische Systeme arbeiten typischerweise mit deutlich größeren Kontextfenstern und über längere Zeiträume hinweg. Sie lesen Dateien, planen mehrstufige Änderungen, schreiben über Modulgrenzen hinweg. Dass moderne Kontextfenster eine Million Tokens umfassen, ändert daran weniger als erwartet: Was zählt, ist nicht die theoretische Größe, sondern der effektive Aufmerksamkeitsspielraum, in dem verlässlich verschachtelt geschlussfolgert werden kann. Genau in diesem Modus wird die Strukturqualität der Codebasis zum Engpass. Ein Agent, der eine unstrukturierte Codebasis analysieren soll, verzweigt sich in Abhängigkeiten, bis sein effektiver Kontext kollabiert. In einer sauber geschnittenen Aggregate-Landschaft kann er dagegen präzise innerhalb einer Verantwortungsgrenze arbeiten. Je autonomer die KI wird, desto teurer wird schlechtes Design, nicht billiger.

Hinzu kommt ein zweiter Effekt: Reasoning-Modelle und Agenten sind teuer. Pro Aufruf, pro Token, pro Werkzeugzyklus. Wer ihnen einen sauber strukturierten Kontext liefert, bekommt das Ergebnis in einem Bruchteil der Schritte. Taktisches DDD wird damit nicht nur zur Qualitäts-, sondern auch zur Kosten-Frage.

Die Pointe bleibt also dieselbe, aber sie verschiebt sich. War taktisches DDD 2024 vor allem ein Korrektiv für schwache Generierung, ist es 2026 ein Verstärker für starke Autonomie. Eine gute KI in einem schlechten Design produziert schneller schlechte Software. Eine gute KI in einem DDD-konformen Design produziert schneller gute Software. Der Hebel ist immer die Struktur des Codes. Nur die Geschwindigkeit, mit der dieser Hebel wirkt, ist gewachsen.

Strategische Relevanz: Taktik nur für das Herzstück

Die bisherige Argumentation verdeutlicht: Softwareentwicklung ist ein evolutionärer Prozess, der von ständiger Anpassung und der permanenten Prüfung (insbesondere auch der Prüfung von abstrakten Qualitätsparametern wie Anpassbarkeit, Erweiterbarkeit oder Wartbarkeit) lebt. Damit dieser Prozess nicht im Chaos versinkt, muss der Code eine Struktur aufweisen, die Anpassbarkeit, Erweiterbarkeit und Nachvollziehbarkeit ermöglicht. Doch hier ist Differenzierung gefragt, denn nicht jeder Code verdient den gleichen Grad an gestalterischer Akribie.

Im Kontext von Domain-Driven Design unterscheiden wir zwischen verschiedenen Subdomains. Während Generic Subdomains (z. B. Finanzbuchhaltung) oft mit Standardlösungen oder Supporting Subdomains (z. B. einfache Daten-CRUD-Module) mit einfacheren Mitteln wie Low-Code-Tools abgedeckt werden können, ist die Core Subdomain das eigentliche Herzstück. Hier liegt die Logik, die den Wettbewerbsvorteil des Unternehmens ausmacht.

Genau hier entfaltet taktisches DDD seine volle Stärke. Es eignet sich besonders für

  • Komplexe Businesslogik: Systeme mit tief verschachtelten Regeln und hohen Anforderungen an die Datenintegrität.
  • Langlebige Software: Projekte, die über Jahre gewartet und deren Fachmodelle stetig erweitert werden müssen.
  • Kritische Systeme: Überall dort, wo ein falscher Zustand (Invalid State) fatale wirtschaftliche oder technische Folgen hat.

Konkrete Anwendungsfelder, in denen sich der Einsatz immer wieder bewährt hat:

  • Finanzsysteme: Sicherstellung von Transaktions-Invarianten, die niemals durch KI-generierte Seiteneffekte korrumpiert werden dürfen.
  • Logistik-Plattformen: Modellierung von hochdynamischen Zuständen, bei denen die Konsistenz über Zeit- und Ortsdaten nur durch strikte Aggregate-Grenzen gewährleistet werden kann.
  • Versicherungs-Engines: Abbildung komplexer Tarife, bei denen die Fachlogik so tief im Modell verwoben ist, dass eine einfache CRUD-Struktur sofort zu teuren Berechnungsfehlern führen würde.
  • E-Government: Absicherung formal-juristischer Prüflogiken. Taktisches DDD verhindert, dass KI-generierter Code rechtsverbindliche Invarianten in Verwaltungsverfahren (z. B. Bewilligungskriterien) durch unzulässige Heuristiken aufweicht.
  • Energy & Utilities: Verwaltung dezentraler Energieströme und Bilanzkreise. Hier garantieren klare Domänengrenzen, dass KI-optimierte Laststeuerung nicht gegen physikalische Netzrestriktionen oder komplexe regulatorische Abrechnungsmodelle verstößt.

Taktisches DDD ist jedoch keine „Silver Bullet”. Für einfache Web-Apps, reine Daten-Pipelines oder Prototypen mit kurzer Lebensdauer kann der Overhead von Aggregates und Value Objects zu groß sein. In solchen Fällen ist ein simplerer, datenzentrierter Ansatz oft wirtschaftlicher. Wer DDD auf ein triviales Problem wirft, baut eine „Over-Engineering”-Falle, in der die Komplexität des Designs die Komplexität des Problems übersteigt.

Drei Hebel: Wie taktisches DDD den Copiloten führt

Taktisches DDD adressiert die typischen Schwächen generativer Erzeugung durch drei Klassen von Mechanismen: strukturelle, semantische und absichernde, die jeweils ein anderes Risiko der KI-gestützten Entwicklung abfangen.

  • Strukturelle Mechanismen: Modularisierung statt Vermischung. Ohne explizite strukturelle Vorgaben füllt die KI das Design-Vakuum mit dem, was statistisch wahrscheinlich ist, oft prozedurale Klassen mit vermischten Verantwortungen und enge, starre Abhängigkeiten zwischen fachlich eigentlich unabhängigen Prozessen. Aggregates erzwingen fachliche Modularisierung und definieren klare Verantwortungsgrenzen; Domain Events brechen starre Strukturen auf, indem Folgeprozesse nicht direkt aufgerufen, sondern als isolierte Handler auf veröffentlichte Ereignisse (z. B. OrderPlaced) reagieren. Side-Effect-Free Functions, also nebenwirkungsfreie Methoden, die nur Werte zurückgeben, ohne den Zustand des Aggregates zu verändern, trennen Berechnung von Zustandsänderung (Command-Query Separation) und verhindern, dass die KI Logik und Seiteneffekte zu unentwirrbaren Methoden vermischt. Der Begriff stammt von Eric Evans und beschreibt eine zentrale Grundhaltung des taktischen DDD: Wo es möglich ist, soll Verhalten vorhersagbar bleiben, indem es nichts verändert. Aus genau dieser Haltung leiten sich auch Value Objects ab, die ihre Unveränderlichkeit nicht zufällig haben, sondern als konsequente Anwendung desselben Prinzips. Diese strukturelle Klarheit hat einen praktischen Nebeneffekt: Da Fachlogik in dedizierten Bausteinen gekapselt ist, muss dem Copiloten nur ein kleiner, hochrelevanter Codeausschnitt präsentiert werden. Das minimiert „Lost-in-the-Middle”-Effekte und Halluzinationen.
  • Semantische Mechanismen: Bedeutung statt generischer Begriffe. KI greift ohne Steuerung auf technische Primitive und generische Methodennamen zurück. Drei Prinzipien wirken dem entgegen: Die Ubiquitous Language sorgt dafür, dass die Fachsprache konsequent bis in den Quellcode getragen wird. Die KI verwendet ClaimAdjustment statt updateStatus. Intention-Revealing Interfaces benennen den Zweck einer Operation so, dass die fachliche Absicht ohne Implementierungsblick erkennbar ist (acceptTermsAndConditions() statt processData()) – das schafft die Erwartungshaltung, gegen die der Reviewer die KI-Lösung sofort prüfen kann. Value Objects eliminieren die sogenannte Primitive Obsession: Wenn IBAN und String verschiedene Typen sind, kann die KI sie nicht mehr verwechseln, und Geschäftsregeln wandern in das Typsystem selbst.
  • Absichernde Mechanismen: Garantien statt Hoffnung. Statt darauf zu vertrauen, dass die KI fachliche Regeln korrekt umsetzt, werden diese als harte Zusicherungen im Code verankert. Assertions formulieren Invarianten explizit. Der Reviewer erkennt sofort, welche Garantie der Code gibt, und Verstöße fallen in Tests früh auf. Der entscheidende Zweitnutzen bei KI-Unterstützung: Bei späteren Erweiterungen liest die KI diese Invarianten automatisch aus dem Code, ohne dass sie jedes Mal im Prompt mitgegeben werden müssen. Die Logik schützt sich selbst vor unbedachten Änderungen durch den Copiloten.
  • Strukturelle Erwartungshaltung als gemeinsamer Effekt: Diese drei Mechanismen wirken zusammen wie ein Filter für den Review. Jeder DDD-Baustein definiert eine spezifische Erwartung. In einem Value Object ausschließlich Berechnungen und Validierungen, in einem Aggregate die Durchsetzung von Invarianten, in einem Application Service nur Koordination. Sobald die KI dieses Profil verletzt, etwa eine Datenbankverbindung in ein Value Object mischt, wird die strukturelle Abweichung sofort sichtbar. Das Design fungiert damit als Aufmerksamkeitslenker: Sie nimmt dem Reviewer die Arbeit ab, alles prüfen zu müssen, und fokussiert ihn auf die fachliche Korrektheit dort, wo sie wirklich entschieden wird.

Stay tuned

Regelmäßig News zur Konferenz und der JAX-Community erhalten

[mc4wp-simple-turnstile]

 

Fazit: Design als Überlebensstrategie im KI-Zeitalter

Wer den Copiloten in der Core Domain von der Leine lässt, ohne ihm die strukturellen Vorgaben des taktischen DDD (oder anderer detaillierter Designprinzipien) zu geben, baut unter Umständen in Rekordgeschwindigkeit eine technische Sackgasse. Wir haben gesehen, dass KI inzwischen vieles beherrscht, was vor wenigen Jahren noch als Grenze galt: komplexere Reasoning-Ketten, präzisere API-Nutzung, autonomes Arbeiten in größeren Kontexten. Was sie weiterhin nicht hat, ist ein Bewusstsein für architektonische bzw. Design Integrität, langfristige Änderbarkeit und fachliche Invarianten.

Hochwertiges Softwaredesign ist daher im Zeitalter der generativen KI kein optionales Extra für Ästheten, sondern ein zentraler wirtschaftlicher Hebel. Taktisches DDD wirkt dabei auf drei Ebenen:

  • Es macht generierten Code durch klare Strukturen deutlich besser prüfbar.
  • Es sichert die Integrität des Kern-Geschäftsmodells gegen statistische Unschärfen ab.
  • Es garantiert, dass wir auch morgen noch Verantwortung für unsere Systeme tragen können, statt im Schuldenberg KI-generierten Codes zu versinken.

Die Rolle des Entwicklers wandelt sich dabei grundlegend: Er schreibt weniger Zeilen, trägt aber eine höhere Verantwortung für die strukturelle Korrektheit. Und je autonomer der Copilot wird, desto mehr entscheidet die Qualität des Softwaredesigns darüber, ob seine Geschwindigkeit zum Vorteil oder zum Problem wird. Wer diese Leitplanken beherrscht, nutzt die KI nicht nur als Schreibhilfe, sondern als hocheffizienten Werkzeugmacher für ein robustes digitales Fundament.

Von der Theorie in die Praxis. Wie diese theoretischen Bausteine in einem modernen Tech-Stack konkret zum Leben erweckt werden, zeigen die Folgeartikel dieser Serie. Teil 2 übersetzt anhand eines praktischen Beispiels das fachliche Modell in Code und zeigt, welche Eigenschaften der taktischen DDD-Bausteine sie zur belastbaren Grundlage für KI-gestützte Entwicklung machen. Die Teile 3 und 4 schließen den Bogen zum Werkzeug: Wie wird das Softwaredesign als persistente Vorgabe im Projekt verankert, wie sehen konkrete Aufträge an den Copiloten aus, und wie macht ein automatisch erzeugtes Strukturdiagramm sichtbar, was die KI tatsächlich gebaut hat?


Links & Literatur

[1] Evans, Eric: „Domain-Driven Design: Tackling Complexity in the Heart of Software”, Addison-Wesley, 2003

[2] Atıl, Berk et al.: „Non-Determinism of “Deterministic” LLM System Settings in Hosted Environments” in: Proceedings of the 5th Workshop on Evaluation and Comparison of NLP Systems (Eval4NLP), S. 135–148, Mumbai, Indien. Association for Computational Linguistics, 2025: https://aclanthology.org/2025.eval4nlp-1.12/

[3] Salvatore, Nikolaus; Wang, Hao; Zhang, Qiong: „Lost in the Middle: An Emergent Property from Information Retrieval Demands in LLMs”, arXiv:2510.10276, 2025: https://arxiv.org/abs/2510.10276

[4] Song, Peiyang; Goodman, Noah et al.: „Large Language Model Reasoning Failures”, arXiv:2602.06176, 2026

The post Taktisches DDD für wartbaren KI-generierten Code appeared first on JAX.

]]>
AI braucht Design: Die Rolle von Spezifikationen in Enterprise AI https://jax.de/blog/enterprise-ai-braucht-design-spezifikationen-softwareentwicklung/ Fri, 12 Jun 2026 10:05:17 +0000 https://jax.de/?p=210525 KI macht Softwareentwicklung schneller – aber nicht automatisch besser. Der Artikel zeigt, warum präzise Spezifikationen und gutes Design zur Grundlage verlässlicher Enterprise-AI-Systeme werden.

The post AI braucht Design: Die Rolle von Spezifikationen in Enterprise AI appeared first on JAX.

]]>
„Design before Implementation” gehört zu den zentralen Prinzipien guter Softwareentwicklung – und wurde in vielen Projekten in den letzten Jahren vernachlässigt. Mit dem Aufkommen von AI-gestützter Entwicklung entsteht nun oft der Eindruck, man könne aus lose formulierten Geschäftsregeln direkt funktionierende Software erzeugen. Das Gegenteil ist jedoch der Fall: Gerade wenn AI Teil realer Anwendungen wird, steigen die Anforderungen an Klarheit, Struktur und präzise Entwürfe. Der Beitrag zeigt, warum detaillierte Spezifikationen und sauberes Systemdesign nicht an Bedeutung verlieren, sondern im Zeitalter von Enterprise AI wichtiger werden als je zuvor.

Um es gleich vorwegzusagen: Von sauberem Design profitiert jede Softwareentwicklung, ob mit oder ohne KI. Wenn dafür eine „Renaissance“ entsprechender Prinzipien notwendig ist, dann ist sie ohnehin überfällig und der Zeitpunkt perfekt. In vielen Projekten, die nach iterativen/agilen Verfahren arbeiten, sind klassische Techniken leider auf der Strecke geblieben. Design gehört aus zwei Gründen dazu.

Stay tuned

Regelmäßig News zur Konferenz und der JAX-Community erhalten

[mc4wp-simple-turnstile]

 

Zum einen, weil „agil“ gern als Ausrede für das Weglassen jeglicher Dokumente – also auch jeglicher Designdokumente – herhalten musste. Das ist eine Missinterpretation, die letzten Endes den Begriff „Software Craftsmanship“ hervorgebracht hat [1]. Abläufe und Strukturen in nicht trivialer Software bedürfen detaillierter Beschreibungen, die mit dem Auftraggeber abgestimmt sind. Was dem Handwerker seine Bauzeichnung ist und dem Musiker seine Partitur, ist dem Software Craftsman seine Softwarespezifikation.

Zum anderen – und das macht auch Anhängern des präzisen Entwurfs das Leben schwer –, weil es kaum ein Werkzeug gibt, das sich komfortabel für eine iterative Entwicklung der Entwürfe eignet. Zum Beispiel hat kein einziges bekanntes UML-Tool einen Änderungsmodus mit einer hybriden Darstellung von bestehendem, neuem und gelöschtem Inhalt wie ihn etwa Microsoft Word hat. Das ist ein Hauptgrund, warum MS Word über Jahrzehnte hinweg Grundlage für iterative Systemspezifikationen war. Andererseits ist Word kein Modellierungswerkzeug, sondern „nur“ ein Textverarbeitungsprogramm. Wenn eine KI die Kontrollstrukturen einer Ablaufbeschreibung verstehen soll – Schleifen, Verzweigungen, Ausnahmen usw. –, dann ist eine Freitextbeschreibung die falsche Basis.

Renaissance heißt also nicht einfach zurück zu den alten Methoden und Werkzeugen, sondern die Prinzipien des Software-Engineerings, die sich über Jahrzehnte bewährt haben, mit einer neuen Generation von Werkzeugen umzusetzen, die den heutigen Anforderungen an Agilität und KI-Integration gerecht werden. Selbst Vibe Coder merken das zunehmend und bringen den Begriff „Spec-driven Development“ als Trendthema auf [2], wobei dort aktuell andere Schwerpunkte gesetzt werden.

Warum Design wichtig ist

Mit Design bzw. Entwurf ist hier primär gemeint: eine detaillierte Beschreibung von Abläufen in den System-Use-Cases einer Software. Die Beschreibungen sind einerseits möglichst fachorientiert und natürlichsprachlich formuliert, andererseits aber so präzise, dass sie ohne Risiko von Fehlinterpretationen in Code übersetzt werden können. Sie fußen dabei auf einem Vokabular von Geschäftsobjekten und deren Beziehungen untereinander. Beides – die Geschäftsobjekte und die Abläufe – sind verbindliche Grundlagen für alle Stakeholder, vom Auftraggeber über die Entwickler bis zu den Testern. Das sind seit jeher zentrale Konzepte von OOA/OOD und Domain-Driven Design. Wenn es um UI-Funktionalität geht, kommen außerdem Oberflächenspezifikationen hinzu (z. B. Wireframes), die in diesem Artikel aber außen vor bleiben.

Solche Softwareentwürfe sind in ihrer Funktion einer Bauzeichnung beim Bau oder Umbau eines Hauses nicht unähnlich. Und wer jetzt einwendet, dass Häuser im Gegensatz zu Software nicht ständigen Änderungen unterliegen, der möge an Solaranlagen, Wallboxen, Wärmepumpen, Glasfaser usw. denken. Ein Segen, wenn dann gute, aktuelle Grundrisse und Leitungspläne des Gebäudes vorliegen. Gerade iteratives Vorgehen profitiert von jederzeit aktuellen Entwürfen.

Abb. 1: User Story für die Altersdarstellung von Chatbeiträgen

Abb. 2: Systemspezifikation für Altersdarstellung als „Aktogramm“

Zur Verdeutlichung zeigt Abbildung 1 ein einfaches Beispiel für eine Handvoll Geschäftsregeln, wie sie in einer User Story stehen könnten, und Abbildung 2 eine daraus entworfene Ablaufbeschreibung in einer Notation, die später noch genauer betrachtet wird. Die Entwurfsform und das Software-Engineering dahinter sind bewährte Konzepte, die auch in hochkomplizierten Szenarien Anwendung finden. In dem Trivialbeispiel aus Abbildung 1 geht es um die leserfreundliche Darstellung des Alters von Beiträgen in einem Chat. Statt 3 621 Sekunden seit der Veröffentlichung soll dort z. B. „1 Stunde“ stehen. Völlig unkritische Funktionalität, aber es könnten natürlich auch die Ratingregeln einer Bilanzanalyse sein, die in einer Bank über die Vergabe von Millionenkrediten entscheiden.

Was passiert nun, wenn ein unbedarfter Entwickler direkt aus den Geschäftsregeln heraus Code produziert? Die Anforderungen klingen einfach, also wird er direkt drauflos programmieren, ist aber natürlich trotzdem gezwungen, sich Gedanken über die Struktur des Codes zu machen. Die Geschäftsregeln sagen ja nur, was zu tun ist, aber nicht wie. Was sich jetzt abspielt, nennt sich „design a little, code a little“ – ein Antipattern! Unter [3] kann man nachlesen, wieso es anti ist. Es entstehen schlechtes Ad-hoc-Design und schlechter Code. Und was ändert sich, wenn man stattdessen mit einem KI-Agenten arbeitet? Man bekommt den schlechten Code schneller geliefert, das ist alles.

Enterprise AI & Agentic Systems

Enterprise AI-Track entdecken

 

Bei einem KI-Coding-Dojo im vergangenen Herbst durften sich Github Copilot und Junie mit verschiedenen LLMs an dieser Aufgabe versuchen, ohne dass dabei Code entstand, der durch weiteres Prompting irgendwann eine vernünftige Gestalt angenommen hätte. Man wird also im Zeitraffer direkt Zeuge des Problems, und die KI-Agenten zeigen uns im Grunde nur: So naiv geht es nicht. Dabei ist noch gar nicht berücksichtigt, dass die Fachbereiche ihre Anforderungen eher „in loser Schüttung“ liefern – ohne Anspruch auf Vollständigkeit und Schlüssigkeit, dafür mit unausgesprochenen Details zwischen den Zeilen.

Ganz anders sieht das Ergebnis basierend auf der Ablaufspezifikation in Abbildung 2 aus. Hier ist der Entwurf in einer separaten Phase vorweg entstanden und wurde so beschrieben, dass alle Beteiligten das Prozedere der Software nachvollziehen können. Die Beschreibung geschieht in einer Weise, dass man quasi bei Ausfall der Software einen Sachbearbeiter hinsetzen könnte, damit er dem Ablauf folgend die Arbeit manuell erledigt, wenn auch eine Milliarde Mal langsamer. Sowohl Entwickler als auch KI-Agenten können auf dieser Basis brauchbaren Code erstellen, der sich an der Struktur des spezifizierten Ablaufs orientiert und somit auch leicht darauf zu prüfen ist, ob er überhaupt das tut, was gefordert ist. Bei späteren Änderungen im Rahmen iterativer Entwicklung sind die in der Spezifikation vorgenommenen Modifikationen entsprechend leicht im Code zu verorten.

Hier spielen gleiche mehrere Faktoren eine Rolle, die eine vorgelagerte Designphase so wertvoll machen, die sich in späteren Phasen auszahlt:

  • Klarheit des Entwurfs: Wenn man sich zwingt, ein für „Normalsterbliche“ nachvollziehbares Verfahren zu beschreiben, das das fachlich gewünschte Ergebnis erzielt, dann stellt man häufig fest: Die vermeintlich bereits glasklaren Gedanken dazu sind unklarer, als man glaubte. Fast jeder kennt das: Sobald man versucht, jemand anderem zu erklären, was man vorhat, zerbröselt eine undurchdachte Idee zu Staub. Eine gute Idee hingegen wird dadurch gestärkt und verfeinert.
  • Analytisches Denken: Es dauert u. U. ziemlich lange, bis man ein leicht zu verstehendes Vorgehen formuliert hat, und das ist gut so. Es zwingt den Entwickler, aus dem schnellen impulsiven Denken in das langsame analytische Denken zu wechseln. Es werden dabei ganz andere Hirnregionen aktiv [4] und das Ergebnis ist ein völlig anderes als bei „design a little, code a little“.
  • Kurze Feedbackschleifen: Sind die Entwürfe für alle lesbar, können auch die Fachanforderer sie beurteilen und dazu beitragen. Hier entstehen schnelle Feedbackschleifen, von denen agile Entwicklung ohne Spezifikationen nur träumen kann. Schwere Missverständnisse und Konzeptfehler fallen nicht erst nach zwei Wochen in der Sprintreview auf, sondern schon nach zwei Stunden in der frühestmöglichen Phase der Softwareentwicklung. Die Rule of Ten [5] erlaubt da schon viel Spezifikationsarbeit, bevor eine Überfrachtung entsteht.

Um es klar zu betonen: Es geht nicht um eine Rückkehr zum Wasserfallmodell! Die Entwürfe müssen iterativ weiterentwickelt werden können. Sie sind keine Wegwerfprodukte aus einer initialen Anstrengung, sondern lebende Artefakte des Software-Engineerings. Das funktioniert nur, wenn sie in geeigneten Werkzeugen erstellt werden und tagtäglich Mehrwert schaffen. Das ist eine der Herausforderungen, die sich bewusst zu adressieren lohnt.

Entwürfe MIT statt VON der KI

Entwurfsarbeit ist soooo anstrengend – kann das nicht die KI machen? Kann sie nicht, denn sonst wäre ja in dem oben erwähnten Dojo vom Fleck weg guter Code entstanden. Sie ist Konsument der Entwürfe. Die Diskussionen, ob und wie weit KI auch Entwürfe herstellen kann, füllen derzeit die Kaffeeküchen aller IT-Unternehmen der Welt.

Die in diesem Artikel vertretene Meinung fußt auf folgender Sichtweise: Der Abschnitt zuvor erwähnt die Feedbackschleifen, die einen guten Entwurf ausmachen. Menschen, die solche Entwurfsarbeit lieben, streben dabei nach Erkenntnisgewinn und nach einem gemeinsamen Verständnis der Welt zwischen den Beteiligten. In ihren Entwürfen sind sie darauf aus, fachliche Notwendigkeiten und technische Möglichkeiten optimal auszubalancieren. Sie treiben diesen Vorgang bis zu einem Punkt, an dem sie sicher sind, dass die folgende Umsetzung kein Abenteuer mehr ist, sondern zielgerichtetes Handwerk mit einem vorhersagbaren Ergebnis. Software Craftsmanship eben.

Künstliche Intelligenz heutiger Bauart hingegen will auf gar nichts hinaus. Es ist eine hochentwickelte Mustererkennungsmaschine, der wir Menschen ihrer guten Ausdrucksweise wegen gerne Intelligenz, Empathie und intrinsische Motivation zuschreiben. Das ist eine fundamentale Denkfalle, so alt wie der Begriff „künstliche Intelligenz“ selbst [6]. Um KI in der Entwicklung komplizierter Softwaresysteme zu nutzen, muss sie mit klaren Mustern und Strukturen gefüttert werden. Das Ziel ist schließlich keine grob in die richtige Richtung gehende „interessante Inspiration“, sondern exakter, bis auf die letzte 0 und 1 stimmiger Code. Mit „exakt“ haben es LLMs aber naturbedingt nicht so, weshalb die Klarheit der Strukturen und Muster umso wichtiger wird. Und wo kommt diese Klarheit her? Entwurfsarbeit!

Entwürfe sind von der KI also nicht zu erwarten, was eigentlich eine gute Nachricht ist: Wer sich auf diese Tätigkeit versteht – tendenziell also jeder Clean Coder – wird so schnell nicht arbeitslos. Es geht wohlgemerkt um komplizierte Softwaresysteme mit Hunderttausenden Zeilen. Energiewesen, Bilanzanalyse, Touristik und Kundenbindungsprogramme sind einige der Umfelder, in denen die hier dargestellten Konzepte Anwendung finden. Abbildung 3 zeigt bespielhaft aus der Vogelperspektive eine vollständige Use-Case-Spezifikation mittlerer Größe, um einen Eindruck zu vermitteln, wie so etwas in echten Projekten aussieht. Die Ablaufbeschreibung ist hier eingebettet in eine Präambel, Vor- und Nachbedingungen, Geschäftsklassendiagramme, Wireframes usw. Auch wenn man die Texte in dieser Auflösung nicht lesen kann, sollte deutlich werden, dass hier keine triviale Logik beschrieben ist. Ohne Spezifikation ist eine permanente Fortentwicklung solcher Use Cases viel zu ineffizient. Natürlich lässt sich die Arbeit an großen Entwürfen prima mit KI unterstützen, wie Matthias Bartels in seinem Artikel „Mit automatisierter Review zum gelungenen Entwurf“ in dieser Ausgabe des Java Magazins zeigt [7]. Um die zentralen Aspekte zu erläutern, soll aber im Weiteren das oben vorgestellte kleine Beispiel genügen.

Abb. 3: Echte Use-Case-Spezifikation mittlerer Größe, Vogelperspektive

Der eigene Kopfcomputer bleibt also der Ort, wo die Entwürfe letztlich entstehen, wo sie aber nicht bleiben dürfen. Machen wir sie Mensch und Maschine zugänglich, dann maximieren wir die Möglichkeit der Zuarbeit von beiden Seiten. An dieser Stelle kommt passendes Tooling ins Spiel. Künstliche Intelligenz hat zwar erstaunliche Fähigkeiten, fast beliebigen Input zu verarbeiten. Die Qualität der Ergebnisse hängt aber doch davon ab, wie präzise die Eingaben strukturiert und formuliert sind.

Abb. 4: Systemspezifikation für Altersdarstellung in Microsoft Word

Abbildung 4 zeigt eine alternative Darstellung der Spezifikation aus Abbildung 2, basierend auf einem viel genutzten Microsoft-Word-Template, wie eingangs schon erwähnt wurde. Es soll beispielhaft die Problematik zeigen, die entsteht, wenn man bequem greifbare Text- und Maltools zum Modellieren missbraucht und damit der KI und auch jeder anderen maschinellen Unterstützung das Interpretieren schwer macht. Was direkt auffällt: Die in Abbildung 2 auf den ersten Blick erkennbaren Verzweigungen (dargestellt durch Diamanten) sind nicht mehr unmittelbar zu sehen. Sie liegen textuell beschrieben vor, was dem tabellarischen Layout geschuldet ist, um automatische Nummerierung und Änderungsstabilität zu gewährleisten. Heftige Kompromisse also, die für menschliche Leser vielleicht ein verkraftbares Problem sind. Aber ob eine KI daraus die gemeinte Ablaufstruktur noch gut herauslesen kann? Noch ungünstiger wird es, wenn für Remotearbeit auf kostengünstige, webbasierte Kollaborationswerkzeuge zurückgegriffen wird (z. B. Confluence). Für die Modellierung sind diese Tools nicht gedacht.

Stay tuned

Regelmäßig News zur Konferenz und der JAX-Community erhalten

[mc4wp-simple-turnstile]

 

Wie sieht’s mit UML-Aktivitätsdiagrammen oder BPMN aus? Hier lassen sich Abläufe modellieren und damit auch gut maschinell auswerten. Leider tauscht man damit die Vor- und Nachteile gegenüber MS Word quasi eins zu eins aus. Änderungsverfolgung, automatische Nummerierung und änderungsstabiles Layout sind futsch. Davon abgesehen brauchen diese Notationen viel Platz und vertragen kaum Text für notwendige Details, die man stattdessen in eine verborgene Doku oder angeheftete Textblasen auslagern muss. Also wieder reichlich Kompromisse in anderer Form, wie in Abbildung 5 exemplarisch dargestellt ist. Das sind alles keine unüberwindbaren Hürden und keine Ausrede dafür, ohne Entwürfe zu arbeiten. Aber vielleicht ist es für eine neue Mensch-Maschine-Zusammenarbeit beim Entwerfen Zeit für „New Kids on the Block“ …

Abb. 5: Systemspezifikation für Altersdarstellung als UML-Aktivitätsdiagramm

Aktogramme

Abbildung 2 zeigt, wie wir gesehen haben, eine Spezifikation für das Beispiel der Altersdarstellung von Chatbeiträgen in Form eines „Aktogramms“. Es handelt sich um eine für Spezifikationen weiterentwickelte Form von Struktogrammen – auch als Nassi-Shneiderman-Diagramme bekannt [8]. Sie sind noch älter als die erste Erwähnung von „Design before Implementation“, dienten aber genau diesem Prinzip. Dabei lag ihr Fokus seinerzeit auf der strukturierten Darstellung von Programmcode, als Letzterer noch 70er-Jahre-Assembler war (so alt sind Struktogramme).

DIE KUNST DER SOTWARE-ARCHITEKTUR

Architecture & Design-Track entdecken

 

Dieses Problem ist mit Hochsprachen und IDEs mit Syntax-Highlighting inzwischen besser gelöst. Für Spezifikationen werden sie aber wieder interessant, wenn man sie ein bisschen aufmöbelt. Der Begriff „Aktogramm“ ist aus „Struktogramm“ und „Aktivitätsdiagramm“ zusammengesetzt, weil die Notation im Kern dem Darstellungskonzept von Struktogrammen folgt, aber einige Anlehnungen an UML-Aktivitätsdiagramme aufweist. So zum Beispiel die in Abbildung 2 gut erkennbaren Diamanten als Ausdrucksform für Verzweigungen, weil die originale Dreiecksform von Struktogrammen unordentlich wirkt, wenn sie in einem Ablauf wiederholt auftaucht. Es steckt also einiges an Feinschliff in der Notation, um sie für Spezifikationen größeren Umfangs tauglich zu machen. Schaut man sich das Aktogramm aus Abbildung 2 und das entsprechende UML-Aktivitätsdiagramm aus Abbildung 5 aus größerer Entfernung an, bemerkt man die beiden Diamanten recht gut als Gemeinsamkeit.

Das Aktogramm wurde mit dem Java-WYSIWYG-Editor Specman [9] erstellt, der verschiedene praktische Eigenschaften von Word und UML zusammenbringt, z. B.

  • Änderungsstabiles Layout: Wenn man hier Schritte in den Ablauf einbringt oder entfernt, gerät das Layout nie durcheinander wie bei Aktivitätsdiagrammen
  • Änderungsmodus: Wie in Word, aber mit einer deutlicheren farblichen Hervorhebung wie mit einem Textmarker (3); man scrollt einfach durch und schaut sich eine gelbe Markierung nach der nächsten an, um die am Use Case vorgenommenen Änderungen zu erkennen
  • Automatische Schrittnummerierung: Sehr wichtig für Abstimmungsprozesse; „Schau dir mal Schritt 3.4.2 an, ob ich dich gestern richtig verstanden habe“, ohne Nummern wird das Absprechen komplizierter Abläufe schwierig
  • Schrittreferenzen: Automatisch aktualisierte Referenzen auf die Nummern anderer Schritte in den Beschreibungen; in Word Usus, in UML ein Manko
  • Viel Inhalt: Wir können uns nicht aussuchen, ob die Geschäftslogik einfach oder kompliziert ist, und die Notation darf uns im Fall der Fälle nicht beschränken; in Specman kann man aber jeden untergliederten Schritt (Schleifen, Verzweigungen, Untersequenzen) in der Ansicht zusammenklappen, wenn man an deren Inhalt gerade nicht interessiert ist
  • Kontrollstrukturen: Sind gut erkennbar – nicht so gut wie in UML, aber viel besser als in Word; hinzu kommt eine wichtige Erweiterung gegenüber Struktogrammen: Letztere kennen zwar sogenannte Breaks als Entsprechung zum Werfen einer Exception, aber es fehlt die Entsprechung für das Fangen; in Spezifikationen ist es aber wichtig, wie das System mit fachlich relevanten Ausnahmen umgeht, also nicht mit NullPointerException und OutOfMemory– das gehört als querschnittliches Verfahren in die Architekturdoku –, sondern mit Fällen wie „Kunde ist gesperrt“ oder „Zahlungsart ungültig“

Specman speichert die Diagramme in einem JSON-Format, aus dem eine KI die Struktur des Ablaufs gut ablesen kann. Muss sie so etwas aus Bildern ablesen, führt das zu einer höheren Anfälligkeit für Fehlinterpretationen und kostet wesentlich mehr Zeit (und Tokens).

Beim Coding auf Grundlage von Aktogrammen können KI und menschliche Entwickler einige Aspekte direkt übernehmen. Ein Beispiel ist die Unterstrukturierung, die man beim Entwurf komplizierter Abläufe in Aktogrammen automatisch vornimmt. Sie lässt sich bei der Implementierung für das Designprinzip „Single Level of Abstraction“ nutzen. Ein erneuter Blick auf Abbildung 2 zeigt beispielsweise, welche Schritte die oberste Spezifikationsebene bilden – die Schritte mit einstelliger Schrittnummer. Man darf also erwarten, dass auf der obersten Codeebene für die Funktionalität eine Abfolge von Funktionsaufrufen zu finden ist, die genau das widerspiegelt:

  • Absicherung gegen negative Angaben
  • Bildung des Stundenanteils
  • Bildung des Minutenanteils
  • Bildung des Sekundenanteils
  • Zusammenstellen des Ergebnisses aus den Anteilen

Es stellt sich für den Entwickler nicht die Frage, ob das eine funktionierende und sinnvolle Strategie zur Erfüllung der Anforderungen ist. Das wurde im Vorfeld in der Entwurfsphase ausgetüftelt. Idealerweise unter seiner direkten Mitarbeit oder sogar Federführung, denn die Entwürfe sind nur brauchbar, wenn sie mit technischem Sachverstand entstehen. Dass Fachbereiche oder Businessanalysten das zukünftig selbst machen und dann nur noch eine KI anwerfen, ist also unwahrscheinlich.

Speckit und Co.

Ausgerechnet die junge Szene der Vibe Coder [10] bringt das Thema Spezifikation im Zusammenhang mit KI wieder ins breite Bewusstsein. Trotz zweifellos höherer Schmerzresistenz bzgl. der Codequalität sind die Erkenntnisse dort offenbar die gleichen wie in dem oben beschriebenen KI-Dojo: Von Hello-World-Programmen abgesehen führt Coding ohne Design innerhalb kürzester Zeit in die Unwartbarkeit. Also entstehen auch in diesem Umfeld Spezifikationswerkzeuge, die allerdings nicht den Anspruch haben, die Entwürfe für alle Stakeholder lesbar zu machen. Speckit [11] ist so ein Werkzeug, das eine Plaintext-basierte Notation verwendet. Die Zugänglichkeit aus einer IDE oder Kommandozeile heraus genügt, weil es nur um verbesserte Kommunikation zwischen Entwickler und KI geht. Andererseits reicht der Anspruch in Richtung Coding so weit, dass die KI auf reproduzierbare Weise vollständige Applikationen implementieren soll.

Der Verbrauch kostbarer KI-Tokens gilt bei Speckit allerdings als enorm, was die leichtgewichtigere Alternative OpenSpec [12] zu verbessern verspricht. Dafür hat Letztere aber noch weniger mit Spezifikationen gemeinsam, wie sie in diesem Artikel gemeint sind. Es geht hier eher um die Beschreibung von Arbeitsaufträgen an die KI. Immerhin entsteht auf diese Weise Wiederholbarkeit und Nachvollziehbarkeit, statt dass der Code über geheimnisvolles Ad-hoc-Prompting immer wieder überraschend anders entsteht [13].

Nassi

Plaintext-basierte Spezifikation und hoher Detailgrad sind übrigens kein Widerspruch. Neben Specman gibt es den Aktogramm-Editor Nassi [14], der einen solchen Weg verfolgt und die für Fachanforderer verständlichen Diagramme in HTML-Form generiert. Dieser andere Ansatz für die Erstellung der Aktogramme führt zu einigen Unterschieden darin, wie sich der Spezifikationsvorgang in das Software-Engineering integriert. Im Kern verfolgt Nassi aber dieselbe Intention wie Specman.

In seinem Artikel „Aktogramme als Kommunikationsmedium“ in dieser Ausgabe des Java Magazins stellt Jan Hermanns den Editor ausführlich vor und geht außerdem tiefer auf iterative Entwurfsarbeit ein [15]. Beide Editoren sind Open Source.

KI auf Aktogrammen

Mit maschinenlesbaren, feingranularen und klar strukturierten Entwürfen kann die KI nun auf vielfältige Weise unterstützen, ohne dabei ausgeprägt in die Irre zu laufen oder zu halluzinieren.

Das naheliegende erste Einsatzgebiet ist die Validierung der Spezifikation selbst. Nicht so sehr im Sinne von: Beschreibt die Spezifikation das, was gebraucht wird? Das zu entscheiden ist Aufgabe der Fachbereiche im Rahmen ihrer Reviews. Deswegen ist gute Lesbarkeit für uns Menschen so wichtig. Eine KI kann aber prüfen, ob die Abläufe in sich logisch konsistent und vollständig sind oder ob sie „lose Enden“ haben. Matthias Bartels geht darauf in seinem Artikel ein [7].

Auch die Validierung von Code gegen die Spezifikation – ein zentraler Bestandteil jeder Code-Review – lässt sich teilautomatisieren. Wenn sich der Code sauber an der Struktur der Spezifikation orientiert, dann ist eine KI wie beispielsweise Claude Code ziemlich gut in der Lage, die Übereinstimmung zu prüfen.

Matthias Bartels vertieft auch diesen Aspekt in seinem Artikel, da er ein wichtiges Sicherheitsnetz für die Frage aller Fragen ist: Kann die KI aus genügend präzisen Beschreibungen zielgerichtet Code generieren, der exakt die spezifizierte Funktionalität realisiert? Vorzugweise, ohne dass die Spezifikation dafür den Umfang des Codes selbst annimmt.

Ein klares „Jein“

Es müssen Stand heute noch viele zusätzliche Bedingungen geschaffen werden, um in komplizierten Systemen nennenswert Code aus Entwürfen generieren zu können. Neben der Spezifikation der Geschäftsobjekte (siehe unten) braucht KI noch eine ganze Menge mehr saubere Strukturen – und zwar in der Architektur. Wenn die KI auf eine Weise coden soll, dass es in eine größere Codebasis passt, dann muss sie darin Muster für alles vorfinden, was es zu coden gibt. Sie verwendet sonst munter alles durcheinander, wovon sie in ihrem angelernten projektfremden Wissen meint, dass es passen könnte. Wenn also im Code keine Ordnung herrscht, dann beschleunigt die KI das Broken-Window-Problem [16], und die Unordnung wächst noch schneller als mit menschlichen Entwicklern.

In einer unordentlichen Codebasis empfiehlt es sich, zunächst eine „Insel der Ordnung“ zu schaffen, an der sich die KI orientieren kann. Ein erfolgreich praktizierter Ansatz besteht darin, in einem verlebten Bestandsprojekt einen getrennten Source-Folder einzurichten, in dem sich ausschließlich blitzsaubere Referenzimplementierungen mit minimalem Umfang für alle Bausteine der Architektur befinden, die als verbindliche Muster fungieren. Wie sieht eine Entity aus? Wie sieht ein Testdaten-Builder für eine Entity aus? Wie ist ein Repository mit Cache für datenbankbasierte Konfigurationsdaten aufgebaut? Die Codebausteine in diesem Folder dienen KI und menschlichen Entwicklern als garantiert unverschmutzte Vorbilder und landen nicht in der Auslieferung. Auch hier gilt das gleiche Prinzip: Design before Implementation.

Ein anderer Punkt ist die Minimierung des Kontexts, den die KI für eine Aufgabe berücksichtigen muss. Je kleiner, desto besser. Anforderungsseitig sind präzise Spezifikationen eine gute Voraussetzung. Codeseitig empfiehlt Simon Martinelli in einem kürzlich veröffentlichten Podcast zu Spec-driven Development [17] den Architekturansatz der Self-contained Systems [18]. Grundidee ist die Zerlegung eines größeren Softwaresystems in Säulen unabhängiger Subsysteme, ohne in das Extrem von Microservices abzugleiten. Jedes der Subsysteme erfüllt eine klar umrissene fachliche Aufgabe und verfügt über eigene Datenhaltung und eigenes UI.

„Self-contained“ heißt: Die Subsysteme sind in sich abgeschlossen und brauchen nur minimalen Kontakt zueinander. Ein System-Use-Case bezieht sich auf Funktionalität innerhalb eines dieser Subsysteme, was dann auch für dessen Spezifikationen gilt. Eine KI, die auf Grundlage solcher Spezifikationen Code generiert, muss sich also nur auf das jeweilige Subsystem konzentrieren. Das kann den codeseitig zu berücksichtigenden Kontext erheblich reduzieren und umgekehrt die Wahrscheinlichkeit erhöhen, dass die KI brauchbaren Code in akzeptabler Zeit produziert.

Subsysteme in einer gemeinsamen Codebasis erfordern dann strenge Modularisierung und u. U. mehr Arbeit beim Prompting, damit der KI-Agent nicht aus Versehen links und rechts schaut. Mit separaten Codebasen für die Subsysteme geht man auf Nummer sicher, was aber wiederum viele andere Komplikationen mit sich bringen kann. Das ist Abwägungssache.

Eine sauber gegliederte Spezifikation hat den Vorteil, dass man einer KI auch klar abgegrenzte Teilaufgaben geben kann. Ist das Coden eines komplette Use Case ein zu dickes Brett, kann der Auftrag auch lauten: Implementiere Schritt 3.4.2 der Spezifikation. Es entsteht dann auch nicht so viel Code auf einmal und man tut sich leichter mit der Review. Man führt die KI sozusagen an ganz kurzer Leine. „Vise Coding“ statt „Vibe Coding“ nennt David Farago dieses Konzept, das sich mit Entwürfen der hier beschriebenen Detaillierung gut verbinden lässt [19].

Der statische Teil einer Spezifikation

Das oben verwendete Beispiel wurde bewusst so gewählt, dass es auch ohne Modellierung von Geschäftsobjekten einigermaßen verständlich ist. In der echten Praxis machen die Formulierungen der Abläufe aber intensiven Gebrauch von einem projektspezifischen Begriffsvokabular. Wenn der Leser einer Ablaufbeschreibung nicht zufällig alle darin auftauchenden Geschäftsobjekte und ihr Beziehungsgeflecht im Kopf hat, muss er sich das irgendwo anschauen können – und zwar nicht im Code, der nur Entwicklern zugänglich ist.

Ist die Formulierung „Wenn der Kunde einen Rabattcoupon eingelöst hat …“ überhaupt valide? Kann man irgendwo sehen, was ein Kunde und was ein Rabattcoupon ist und wie die Beziehung zwischen beiden aussieht? Vielleicht besteht die nur indirekt über den Umweg der Artikel einer Bestellung, und man sollte den Ablauf präziser formulieren – für Mensch und KI. Wenn diese Dinge nicht einsehbar sind, ist die Ablaufbeschreibung womöglich nur Kauderwelsch.

Abb. 6: Geschäftsobjekte als „umweltverträgliches“ UML-Klassendiagramm

Der Anspruch, dass die Entwürfe mit allen Beteiligten abgestimmt sind und sich permanent weiterentwickeln, gilt auch für die statischen Bestandteile. In den Projekten, in denen Specman und Word für die Ablaufbeschreibungen verwendet werden, kommen UML und im Besonderen Klassendiagramme zum Einsatz. Das ist trotz fehlender Änderungsverfolgung usw. ein akzeptabler Kompromiss, denn die Geschäftsobjekte ändern sich weniger häufig und enthalten auch nicht so viele Details wie die Abläufe. In Abbildung 6 ist das beispielhaft für die Geschäftsobjekte des Chatsystems dargestellt, auf die sich die Anforderung mit der Altersdarstellung bezieht. Allerdings sollte es schon „echtes“ UML sein und nicht nur gemalte Diagramme in DrawIO oder Visio. Der Hauptgrund ist die Unterscheidung zwischen dem Modell einerseits und dem Diagramm als eingeschränkte Sicht auf das Modell andererseits.

In größeren Softwaresystemen ist es wenig hilfreich, ein Riesentapetendiagramm mit allen Geschäftsobjekten darauf herzustellen. Sinnvoll und naheliegend ist eher, dass man Use-Case-bezogene Diagramme herstellt, die den Ablaufspezifikationen der Use Cases beigestellt werden (Abb. 3). Zentrale Geschäftsobjekte tauchen dann in diversen Diagrammen auf, sollen aber natürlich nur einmalig modelliert werden, sofern hier nicht nach dem Prinzip der Bounded Contexts bewusst Redundanzen erwünscht sind [20]. Diese Trennung von Modell und Diagramm ist eine wichtige Eigenschaft professioneller UML-Tools wie z. B. Enterprise Architect oder Visual Paradigm.

Hinzu kommt in diesen Tools die Möglichkeit des Aufbaus sogenannter UML-Profile, die u. a. einen Kanon projektspezifischer Stereotype und zugehöriger Tagged Values bereitstellen. Stereotype sind eine Ausdrucksform querschnittlicher Konzepte, über die sich damit verbundene Muster im Code adressieren lassen. Ein KI-Assistent kann über die Stereotype also auf die richtigen Patterns zur Codegenerierung hingewiesen werden, wie Abbildung 6 verdeutlicht. Aus einer View-Klasse sind völlig andere Dinge zu generieren als aus einer Entity-Klasse, wobei Letztere noch mit einem Tagged Value für den Tabellennamen angereichert ist. Mit einem guten Farbschema, unterstützenden Icons und möglichst fachlicher Terminologie lassen sich auch für die Anforderungsseite ansprechende Diagramme herstellen.

Use-Case-bezogene Diagramme haben übrigens den Nebeneffekt eines geschenkten Dependency-Trackings. Ändert man für einen Use Case etwas an der Struktur eines zentralen Geschäftsobjekts, lässt sich im Tool feststellen, in welchen Diagrammen und damit in welchen anderen Use Cases das Objekt eine Rolle spielt und wo man mögliche Auswirkungen prüfen muss.

Alle professionellen UML-Tools bieten Exporte in maschinenlesbare Formate an, z. B. XMI. Damit ist sichergestellt, dass auch KI die Modelle leicht versteht. Noch einfacher ist es mit Tools, die selbst auf Plaintext arbeiten und die grafischen Darstellungen generieren. Weit verbreitet ist z. B. PlantUML [21]. Leider ist hier die Trennung Modell/Diagramm nicht gegeben, was den Einsatz für größere Softwaresysteme erschwert.

Es ist auch die Frage, ob man mit der Optik generierter Diagramme klarkommt. Einer agentischen Codegenerierung ist Optik egal, solange die modellierten Strukturen präzise sind, aber wie bei den Abläufen sind die Adressaten für die Diagramme auch die Fachanforderer. Sinnvolle Platzierung nach fachlichen Gesichtspunkten, saubere Linienführung und Aufgeräumtheit sind hilfreich für das Verständnis. Auch bei den statischen Modellbestandteilen kann man sich nicht immer aussuchen, wie kompliziert die darzustellenden Zusammenhänge sind. Wo immer es geht, hält man die Dinge klein, etwa nach der C4-Methode [22] oder nach den Gestaltungskonzepten von Jacqui Read [23]. Wenn das aber einmal nicht geht, ist es ärgerlich, wenn die Einflussmöglichkeiten auf das Layout beschränkt sind. Man denke an die Analogie zur Bauzeichnung: Detailreichtum und Lesbarkeit sollten kein Widerspruch sein.

Spezifikation einführen

Wenn man Spezifikationen in einem Projekt einführen will, dann fängt man am besten klein an. Ähnlich wie bei der Einführung von Testautomatisierung muss man dabei gegen das kontraintuitive Gefühl ankämpfen, mehr Dinge herzustellen zu müssen, um am Ende effizienter zu sein. Dass ein Drittel mehr Code für Unit-Tests schlussendlich spart statt kostet, musste man auch erst mal verinnerlichen. Erfahrungsgemäß tun sich Youngster damit leichter als alte Hasen.

Für den Einstieg entwirft man z. B. für die nächste etwas kompliziertere Aufgabe einfach mal vorher ein Aktogramm, um darauf basierend zu coden. Das muss nicht gleich perfekt und mit den Anforderern abgestimmt sein. Erst mal für sich selbst üben, um z. B. das Arbeiten im Flow anzustreben [24]. Ist der Feinentwurf nämlich vorweg entstanden, kann man sich hinterher unterbrechungsarm dem Coden widmen. Wenn mir das als Mensch gelingt, sind die Voraussetzungen für KI-Unterstützung gut.

Ein anderer bewährter Einstieg ist z. B. eine fragmentarische Beschreibung aus einem Reverse Engineering von Code, dessen unklare Funktionsweise wiederholt Ärger macht – übrigens auch eine Tätigkeit, bei der KI helfen kann. Der Effekt, dass alle Beteiligten plötzlich in natürlicher Sprache nachlesen können, wie das System an der fraglichen Stelle arbeitet, ist ein Augenöffner.

Vollständig in das Software-Engineering integrierte Spezifikationen nehmen in der oben dargestellten Form etwa ein Fünftel der Zeit ein, die das Implementieren benötigt. Das ist ein grober Erfahrungswert über 15 Jahre und diverse Projekte in verschiedenen Firmen hinweg. Dabei entsteht ein Shift-Left-Effekt [25], der die Aufwände in teureren Folgephasen des Entwicklungsprozesses deutlicher reduziert als neuer Aufwand in den frühen Phasen entsteht. Es braucht nur seine Zeit, bis es einem in die DNA übergeht.

Immer erst die Spezifikation aktualisieren, bevor man an den Code geht. Das muss sich so natürlich und indiskutabel anfühlen wie: Immer erst den Gurt anlegen, bevor man losfährt. Dann funktioniert es.

Stay tuned

Regelmäßig News zur Konferenz und der JAX-Community erhalten

[mc4wp-simple-turnstile]

 

Fazit

KI-Unterstützung in der Softwareentwicklung hat, der besonderen Natur von LLMs wegen, ihre Tücken. Wo sie einerseits verspricht, das Coding zu beschleunigen, deckt sie andererseits gnadenlos auf, wo in Design und Code bisher „herumgesumpft“ wurde. Detaillierte, abgestimmte Spezifikationen und saubere Codestrukturen sind die Leitplanken, die einer KI den Weg weisen, um sie auch in anspruchsvollen Projekten für mehr als eine bessere Internetsuche zu verwenden. Insofern ist der Zeitpunkt gerade günstig, sich auf solide Entwurfsarbeit zu besinnen. Neue Spezifikationstools sind am Start und die Investitionsbereitschaft für alles rund um KI-Unterstützung ist hoch. Das Prinzip „Design before Implementation“ hat sich über mindestens 30 Jahre als zeitlos erwiesen und es ist unwahrscheinlich, dass KI das ändert, wenn selbst Vibe Coder es erkennen.

Ist dieser Artikel eigentlich mit KI-Unterstützung entstanden? In der Tat ist er das, und zwar genau nach den Prinzipien, die hier für Software beschrieben sind. Der Entwurf des Artikels ist up-front aus menschlicher Arbeit und vielen, vielen Diskussionen entstanden. Aber wenn es um die „Implementierung“ ging, konnte die KI dabei helfen z. B. einen angefangenen Satz sauber formuliert zu Ende zu bringen.

The post AI braucht Design: Die Rolle von Spezifikationen in Enterprise AI appeared first on JAX.

]]>
Golden Master Testing für Legacy-Systeme https://jax.de/blog/golden-master-testing-fuer-legacy-systeme/ Fri, 13 Mar 2026 16:00:34 +0000 https://jax.de/?p=209952 In diesem Artikel stellt der Autor mit dem Golden Master Testing eine Testmethode für die Migration von Legacy-Systemen vor. Anstatt zu versuchen, unbekannte Fachlichkeit nachträglich zu spezifizieren, wird das bestehende Verhalten des Systems systematisch beobachtet und abgesichert. So entsteht ein belastbares Sicherheitsnetz, das Veränderungen ermöglicht, ohne vollständiges Fachwissen vorauszusetzen. Gerade im Kontext von Migrationsprojekten entwickelt sich Golden Master Testing damit vom reinen Testwerkzeug zu einem zentralen Baustein für eine kontrollierte, schrittweise Modernisierung.

The post Golden Master Testing für Legacy-Systeme appeared first on JAX.

]]>
Die Migration von Legacy-Systemen scheitert selten an Technologie. Die eigentliche Herausforderung liegt fast immer woanders: in der fehlenden oder nur noch teilweise bekannten Fachlichkeit des bestehenden Systems. Über Jahre gewachsene Logik, implizite Geschäftsregeln und historische Sonderfälle sind oft ausschließlich im Code verborgen – die Dokumentation ist veraltet, Domänenexperten haben das Unternehmen verlassen oder sind in den Ruhestand gegangen und fachliche Tests decken nur geringe Teile ab, wenn sie überhaupt vorhanden sind.
Gleichzeitig besteht ein hoher Veränderungsdruck. Softwareversionen sind veraltet, Plattformen werden gekündigt und Bibliotheken nicht mehr mit Sicherheitsupdates versorgt. Das bestehende System muss also modernisiert, eventuell entkoppelt und/oder auf eine neue Plattform überführt werden. All das muss natürlich ablaufen, ohne den laufenden Geschäftsbetrieb zu gefährden.

Stay tuned

Regelmäßig News zur Konferenz und der JAX-Community erhalten

[mc4wp-simple-turnstile]

 

Die vorhandene Testabdeckung bietet in der Regel nicht genug Sicherheit, um die Migration durchzuführen. Die Testabdeckung zu erhöhen, erweist sich in der Regel als schwierig, weil klassische Testansätze in dieser Situation schnell an ihre Grenzen stoßen: Unit-Tests erfordern ein Verständnis der fachlichen Intention, Integrationstests setzen stabile Zustände der Umsysteme (inkl. der Datenbank) voraus und fachliche Abnahmetests benötigen explizite Erwartungen – all das fehlt häufig genau dort, wo Migration am dringendsten ist. In solchen Situationen ist Golden Master Testing für uns das Mittel der Wahl.

Golden Master Testing: die Grundidee

Die Grundidee dieses Ansatzes ist ebenso einfach wie wirkungsvoll: Statt das gewünschte Verhalten eines Systems zu beschreiben, wird das tatsächlich beobachtbare Verhalten als Referenz festgehalten.

Ein Golden Master ist dabei keine Spezifikation im klassischen Sinn, sondern quasi ein Snapshot des aktuellen Systemverhaltens. Für definierte Eingaben wird die erzeugte Ausgabe aufgezeichnet und als Vergleichsbasis für zukünftige Änderungen verwendet. Solange sich die Ausgaben nicht verändern, gilt das Verhalten als stabil – unabhängig davon, wie sich die interne Struktur des Systems entwickelt.

Dieser Ansatz ist besonders dort hilfreich, wo das fachliche Warum eines Systems nicht mehr vollständig bekannt ist. Golden-Master-Tests setzen bewusst außerhalb des Systems an. Sie betrachten es als Blackbox: Eingabe hinein, Ausgabe heraus. Welche Pfade im Code durchlaufen werden oder welche fachlichen Regeln dabei greifen, ist zunächst zweitrangig. Entscheidend ist allein, dass das beobachtete Verhalten reproduzierbar bleibt.

Wichtig ist dabei, zu verstehen, dass ein Golden Master kein Qualitätsurteil darstellt. Er konserviert bestehendes Verhalten – inklusive historisch gewachsener Sonderlogik, Inkonsistenzen oder fachlichen Fehlern. Golden Master Testing schafft somit zwar keine fachliche Klarheit, aber technische Sicherheit. Und diese ist häufig die notwendige Voraussetzung, um ein System überhaupt schrittweise zu modernisieren, zu refaktorisieren oder in eine neue Zielarchitektur zu überführen.

Damit verschiebt Golden Master Testing den Fokus von Spezifikation zu Stabilität: Erst wenn bekannt ist, was sich nicht verändern darf, wird es möglich, zu entscheiden, was sich tatsächlich verändern soll.

DIE KUNST DER SOTWARE-ARCHITEKTUR

Architecture & Design-Track entdecken

 

Abgrenzung zu anderen Testmethoden

Golden Master Testing wird häufig missverstanden – entweder als Ersatz für bestehende Testarten oder als spezielle Ausprägung von Integrationstests. Technisch ist das zwar richtig, organisatorisch nimmt der Ansatz jedoch eine eigene Rolle im Testportfolio ein und adressiert ein sehr spezifisches Problem: den fehlenden Zugang zur fachlichen Intention eines Systems.

Klassische Unit-Tests setzen voraus, dass die fachliche Bedeutung einzelner Codeeinheiten bekannt ist. Sie formulieren explizite Erwartungen an Methoden, Aggregates oder Services und prüfen diese isoliert. In Legacy-Systemen ist dieses Wissen jedoch oft nicht mehr vorhanden oder nur implizit im Code verankert. Golden-Master-Tests umgehen dieses Problem, indem sie nicht auf Codeeinheiten zielen, sondern auf beobachtbares Gesamtverhalten.

Integrations- und End-to-End-Tests prüfen das Zusammenspiel mehrerer Komponenten, also z. B. des Application Servers und der Datenbank, indem Requests über eine REST- oder (wahrscheinlich eher) SOAP-Schnittstelle abgesetzt werden. Solche Tests benötigen also klare Erwartungen, mit denen das tatsächliche Ergebnis verglichen werden kann. Golden-Master-Tests unterscheiden sich hier grundlegend: Sie bewerten nämlich gar nicht, ob ein Ergebnis korrekt ist, sondern lediglich, ob es sich im Vergleich zum bisherigen Verhalten verändert hat. Damit eignen sie sich besonders für instabile oder historisch gewachsene Systeme, die sich zwar technisch verändern lassen, deren Verhalten aber erhalten bleiben muss.

Häufig werden Golden-Master-Tests auch mit Approval-Tests gleichgesetzt. Auch hier gibt es tatsächlich technische Überschneidungen, da auch Letztere Ausgaben gegen gespeicherte Referenzen vergleichen. Der Unterschied liegt allerdings weniger im Werkzeug als im Ziel: Während Approval-Tests meist bewusst gestaltete, fachlich geprüfte Erwartungen absichern, dienen Golden-Master-Tests primär der Verhaltenskonservierung in Situationen fehlender Fachklarheit.

Im Unterschied zu klassischen Tests formulieren sie keine Erwartungen, sondern treffen eine Beobachtung. Sie beantworten nicht die Frage „Ist das Ergebnis fachlich korrekt?“, sondern „Liefert das neue System dasselbe Ergebnis wie das alte?“ Genau diese Verschiebung macht sie in Migrationsszenarien so wertvoll: Wo keine verlässliche Fachlichkeit verfügbar ist, kann zumindest fachliche Regression verhindert werden.

Entscheidend ist daher die richtige Einordnung: Golden Master Testing ist kein dauerhafter Ersatz für fachlich motivierte Tests. Vielmehr fungiert es als Einstiegspunkt. Es schafft die notwendige Sicherheit, um ein System schrittweise zu verändern, zu modularisieren oder zu migrieren. Erst auf dieser Grundlage können gezielt fachliche Tests ergänzt sowie veraltete Logiken hinterfragt und Domänenwissen wieder explizit gemacht werden. In einer nachhaltigen Teststrategie stehen Golden-Master-Tests somit nicht am Ende, sondern am Anfang.

Typische Einsatzszenarien

Nach dieser Beschreibung wird klar, dass Golden Master Testing seinen größten Nutzen in Situationen entfaltet, in denen Veränderung notwendig ist, das Risiko jedoch schwer abschätzbar bleibt. Besonders in Migrationsprojekten tritt diese Konstellation häufig auf.

Ein klassisches Einsatzszenario ist die technische Migration eines bestehenden Systems, etwa beim Wechsel der Plattform, der Programmiersprache oder der Laufzeitumgebung. Ob Hostsysteme, proprietäre Frameworks oder historisch gewachsene Eigenentwicklungen – häufig ist unklar, welche fachlichen Sonderfälle im Detail implementiert sind. Golden-Master-Tests ermöglichen es, das bestehende Verhalten vor der Migration zu erfassen und nach der Umsetzung gezielt zu vergleichen. Abweichungen werden sichtbar, ohne dass jede Regel explizit verstanden oder neu spezifiziert werden muss.

Auch beim schrittweisen Refactoring großer Legacy-Codebasen sind solche Tests ein bewährtes Mittel. Statt das System in einem großen Wurf umzubauen, können einzelne Module oder Komponenten isoliert verändert werden. Solange das beobachtete Verhalten stabil bleibt, lässt sich die interne Struktur kontinuierlich verbessern. Golden-Master-Tests sorgen dafür, dass strukturelle Änderungen möglich werden, ohne unbeabsichtigte fachliche Regressionen zu riskieren.

Ein weiteres häufiges Szenario ist die Extraktion von Modulen oder Services aus monolithischen Systemen. Bei der Abgrenzung fachlicher Verantwortlichkeiten ist das tatsächliche Verhalten oft aussagekräftiger als die vorhandene Dokumentation. Golden-Master-Tests helfen dabei, den bisherigen Funktionsumfang eines Moduls explizit festzuhalten und ihn beim Übergang in eine neue Architektur abzusichern.

Besonders relevant ist der Ansatz zudem bei Batchverarbeitung und datengetriebenen Prozessen. Hier sind fachliche Regeln oft über Jahrzehnte hinweg gewachsen und eng mit Datenformaten, Sonderfällen und historischen Annahmen verknüpft. Golden-Master-Tests ermöglichen es, diese Logik zunächst als Ganzes zu stabilisieren, bevor sie schrittweise analysiert, vereinfacht oder neu implementiert wird.
Gemeinsam ist all diesen Szenarien, dass das Testing nicht als langfristige Strategie verstanden wird, sondern als Enabler für kontrollierte Veränderung. Dort, wo vollständiges Verständnis fehlt, aber Verlässlichkeit erforderlich ist, bietet der Ansatz einen pragmatischen Weg, Migrationen schrittweise und mit kalkulierbarem Risiko umzusetzen.

Vorteile des Ansatzes

Der zentrale Vorteil des Golden Master Testings liegt in der frühen Herstellung von Sicherheit. In Situationen, in denen weder fachliche Spezifikationen noch belastbare Tests existieren, ermöglicht der Ansatz einen schnellen Einstieg in die Absicherung des Systemverhaltens. Damit wird Veränderung überhaupt erst verantwortbar.

Ein wesentlicher Nutzen besteht im geringen Bedarf an fachlichem Vorwissen. Golden-Master-Tests setzen nicht voraus, dass Geschäftsregeln vollständig verstanden oder neu modelliert werden. Stattdessen machen sie das bestehende Verhalten explizit und überprüfbar. Gerade in Migrationsprojekten, in denen Domänenexperten nur eingeschränkt verfügbar sind, ist das ein entscheidender Vorteil.

Darüber hinaus unterstützen die Tests eine inkrementelle Vorgehensweise. Veränderungen können schrittweise vorgenommen werden, etwa Modul für Modul oder Schnittstelle für Schnittstelle. Jede Anpassung lässt sich unmittelbar gegen das bisherige Verhalten prüfen. Abweichungen werden frühzeitig sichtbar, bevor sie sich in der Zielarchitektur verfestigen oder in spätere Projektphasen verschieben.
Ein weiterer Vorteil liegt in der Unabhängigkeit von internen Strukturen. Da Golden-Master-Tests auf beobachtbares Verhalten abzielen, bleiben sie auch dann gültig, wenn sich die interne Architektur grundlegend ändert. Daher sind sie besonders geeignet für tiefgreifende Refactorings, Plattformwechsel oder Sprachmigrationen.

Nicht zuletzt schaffen die Tests Transparenz über das tatsächliche Systemverhalten. Sie machen implizite Annahmen, Sonderfälle und historische Logik sichtbar, die zuvor im Code verborgen waren. Diese Transparenz bildet eine wichtige Grundlage, um in späteren Schritten fachliche Klarheit zu schaffen, Tests gezielt zu verfeinern und bewusste Entscheidungen über notwendige fachliche Änderungen zu treffen.

Zusammengefasst bietet Golden Master Testing keinen perfekten Testansatz, aber einen pragmatischen und wirkungsvollen Einstieg: Es reduziert Risiken, erhöht die Veränderungsgeschwindigkeit und schafft Vertrauen – genau dort, wo Migration sonst häufig ins Stocken gerät.

Grenzen und Risiken

So hilfreich Golden Master Testing in Migrationsprojekten ist, so wichtig ist ein realistischer Blick auf seine Grenzen. Der Ansatz schafft Sicherheit durch Stabilität – und genau darin liegt zugleich sein größtes Risiko.

Golden-Master-Tests sichern bestehendes Verhalten ab, unabhängig davon, ob dieses Verhalten fachlich korrekt, konsistent oder noch zeitgemäß ist. Historische Sonderfälle, implizite Workarounds oder längst überholte Geschäftsregeln werden mit konserviert. Wird dieses Verhalten ungeprüft in eine neue Zielarchitektur übernommen, so besteht die Gefahr, fachliche Altlasten zu zementieren, statt sie schrittweise abzubauen.

Ein weiteres Risiko liegt in der Fragilität der Tests. Viele Legacy-Systeme produzieren Ausgaben, die nicht vollständig deterministisch sind. Diese Aspekte müssen erkannt und aktiv angegangen werden. Ansonsten führen Golden-Master-Tests zu häufigen, wenig aussagekräftigen Abweichungen. In der Praxis untergräbt das schnell das Vertrauen in die Tests und damit ihren eigentlichen Zweck. Auf das Thema kommen wir weiter unten noch zurück.

Zudem liefern Golden-Master-Tests keine Erklärung für beobachtetes Verhalten. Sie zeigen, dass sich etwas verändert hat, nicht warum. Für tiefere fachliche Analysen, Fehlerursachen oder bewusste Weiterentwicklungen sind zusätzliche Schritte erforderlich. Ohne begleitende Refactorings, fachliche Klärung und gezielte Tests bleibt der Ansatz rein konservierend.

Schließlich besteht die Gefahr, Golden Master Testing als dauerhafte Lösung misszuverstehen. Wird der Ansatz nicht bewusst als Übergangswerkzeug eingesetzt, kann er zu einem Stillstand führen, bei dem Veränderung zwar technisch möglich, fachlich aber nie hinterfragt wird. Auf dieses Problem gehe ich im Abschnitt „Fallstricke und Antipatterns“ näher ein.

Golden-Master-Tests sind also kein Selbstzweck. Ihr Wert entsteht nur dann, wenn sie als temporäre Lösung verstanden werden – mit dem klaren Ziel, auf dieser Basis fachliches Verständnis aufzubauen, Tests zu verfeinern und das System langfristig weiterzuentwickeln.

Stay tuned

Regelmäßig News zur Konferenz und der JAX-Community erhalten

[mc4wp-simple-turnstile]

 

Einordnung in eine nachhaltige Teststrategie

Golden Master Testing entfaltet seine Stärke vor allem als Einstieg in unsicheres Terrain. In einer nachhaltigen Teststrategie nimmt der Ansatz daher eine klar begrenzte, aber zentrale Rolle ein: Er schafft Stabilität dort, wo Verständnis fehlt, und ermöglicht so die nächsten Schritte.

Zu Beginn einer Migration oder Modernisierung dient der Golden Master als Sicherheitsnetz. Er schützt vor unbeabsichtigten fachlichen Regressionen, während technische Strukturen verändert, Abhängigkeiten reduziert oder Module neu zugeschnitten werden. In dieser Phase steht nicht fachliche Korrektheit im Vordergrund, sondern die Gewissheit, dass sich das beobachtete Verhalten nicht unbemerkt verändert.

Mit zunehmendem Verständnis des Systems sollte sich der Schwerpunkt jedoch verschieben. Einzelne fachliche Bereiche werden expliziter, implizite Regeln werden sichtbar, und erste Domänenkonzepte lassen sich benennen. An diesem Punkt verlieren Golden-Master-Tests ihre Rolle als primäres Absicherungsinstrument und machen Platz für gezielte fachliche Tests: Unit-Tests auf Domänenebene, Integrationstests für klar definierte Schnittstellen und explizite Akzeptanzkriterien.

Wichtig ist dabei ein bewusster Umgang mit bestehenden Golden-Master-Tests. Sie können schrittweise ersetzt, reduziert oder auf grobe Regressionen beschränkt werden, während feinere fachliche Tests an ihre Stelle treten. Auf diese Weise wird das ursprünglich konservierte Verhalten nicht einfach eingefroren, sondern kontrolliert weiterentwickelt.

In einer langfristigen Perspektive sind Golden-Master-Tests somit kein Endzustand, sondern ein Katalysator für Lernprozesse. Sie ermöglichen es, Migrationen zu starten, bevor vollständiges Fachwissen verfügbar ist – und schaffen zugleich die Voraussetzung, dieses Wissen im Lauf des Projekts wieder aufzubauen und explizit zu machen.

Herausforderungen bei der technischen Umsetzung

Golden Master Testing bietet damit einen pragmatischen Ansatz, um Migration auch unter unsicheren fachlichen Rahmenbedingungen kontrollierbar zu machen. Indem das bestehende Verhalten systematisch beobachtet und abgesichert wird, entsteht die Sicherheit für tiefgreifende technische Veränderungen.

Die eigentliche Herausforderung beginnt jedoch dort, wo die Theorie in die Praxis überführt wird. Die Wirksamkeit von Golden-Master-Tests hängt maßgeblich von konkreten Entscheidungen ab: an welchen Stellen angesetzt wird, welche Szenarien abgesichert werden und wie mit instabilem oder historisch gewachsenem Verhalten umzugehen ist. Unbedachte Implementierungen führen schnell zu fragilen Tests, die mehr behindern als helfen.

Im nächsten Abschnitt widme ich mich daher der praktischen Umsetzung. Er zeigt, wie geeignete Einstiegspunkte identifiziert werden, wie aussagekräftige Testfälle ausgewählt werden und welche Gestaltungsprinzipien Golden-Master-Tests in Migrationsprojekten tatsächlich tragfähig machen.

Geeignete Einstiegspunkte finden

Der Erfolg der Methode hängt weniger von der Anzahl der Tests ab als von der Wahl der richtigen Einstiegspunkte. Nicht jeder Codebereich eignet sich gleichermaßen, um beobachtbares Verhalten stabil abzusichern. Gerade in Legacy-Systemen ist es entscheidend, dort anzusetzen, wo fachliche Wirkung entsteht.

Geeignete Einstiegspunkte sind in der Regel die öffentlich sichtbaren Schnittstellen. Dazu zählen externe APIs, Batchprogramme, Dateischnittstellen oder fachliche Services, die von anderen Systemen oder Prozessen genutzt werden. Aber auch ein eventuell vorhandenes UI ist ein guter Einstiegspunkt. Dort ist es erfahrungsgemäß allerdings aufwendiger, Tests zu realisieren, worauf ich im nächsten Abschnitt eingehe. Diese Schnittstellen bilden das tatsächliche Verhalten des Systems ab, das auch von Umsystemen und/oder Anwendern erwartet wird – unabhängig davon, wie komplex oder unübersichtlich die interne Implementierung ist.

Tief verschachtelte interne Methoden oder technische Hilfsfunktionen sind hingegen weniger geeignet. Sie besitzen oft keine eigenständige fachliche Bedeutung und ändern sich im Zuge von Refactorings zwangsläufig. Golden-Master-Tests auf dieser Ebene führen schnell zu instabilen Tests, die strukturelle Änderungen verhindern, statt sie abzusichern.

Bei der Auswahl von Einstiegspunkten helfen drei zentrale Kriterien: fachliche Relevanz, Stabilität und klar definierte Ein- und Ausgaben. Fachlich relevante Einstiegspunkte sind solche, deren Verhalten für nachgelagerte Systeme oder Geschäftsprozesse kritisch ist. Stabil sind Einstiegspunkte, deren Schnittstellen sich im Rahmen der Migration möglichst wenig ändern sollen. Klare Ein- und Ausgaben erleichtern schließlich die Vergleichbarkeit und reduzieren den Aufwand zur Stabilisierung der Tests.

In Migrationsprojekten empfiehlt es sich zudem, Einstiegspunkte entlang geplanter Migrationsschritte zu wählen. Wird ein Modul oder Service schrittweise ersetzt oder neu implementiert, sollte genau dieser Übergangspunkt durch Golden-Master-Tests abgesichert werden. Auf diese Weise entsteht ein direktes Feedback darüber, ob das neue System das bisherige Verhalten zuverlässig reproduziert.

Die bewusste Auswahl weniger und gut geeigneter Einstiegspunkte ist damit ein wesentlicher Erfolgsfaktor. Sie sorgt dafür, dass Golden-Master-Tests nicht zur Belastung werden, sondern gezielt dort Sicherheit schaffen, wo sie für die Migration den größten Nutzen haben.

Migration der Benutzeroberfläche

Die Migration der Benutzeroberfläche wird in Legacy-Projekten häufig unterschätzt, da in ihr oft implizite Fachlichkeit steckt. Bildschirmfolgen, Validierungen oder Navigationslogiken transportieren fachliches Verhalten, das selten dokumentiert ist. Gleichzeitig sind die UI-Technologien häufig veraltet und der Gedanke: „Wenn wir schon migrieren, dann auch das UI“ liegt nahe. Es ist allerdings davon abzuraten, Backend-Logik und UI gleichzeitig zu migrieren, weil die Absicherung durch Golden-Master-Tests dann besonders schwierig wird.

Ein Vorgehen, dass sich in unseren Projekten bewährt hat, besteht darin, die bestehende UI im ersten Schritt unverändert zu lassen und eine neue Service-Schicht, etwa in Form eines REST API, unter der Oberfläche einzuziehen. Um sicherzustellen, dass das fachlich korrekt geschieht, können UI-basierte Golden-Master-Tests von Beginn an eingesetzt werden. End-to-End-Tests mit Werkzeugen wie Playwright bedienen dann die bestehende Oberfläche automatisiert und überprüfen, ob sich das sichtbare Verhalten für die Nutzer trotz neuer Schnittstellen nicht verändert.

Sobald diese Absicherung etabliert ist, wird die Service-Schicht zum fachlichen Einstiegspunkt und kann selbst durch Golden-Master-Tests abgesichert werden. Erst danach wird das UI migriert oder neu implementiert – mit der Sicherheit, dass die Fachlichkeit durch die bestehenden Tests unverändert bleibt. So wird das UI schrittweise austauschbar, ohne implizite Fachlichkeit zu verlieren.

Testfälle auswählen – Qualität vor Quantität

Ein häufiger Fehler bei der Einführung von Golden-Master-Tests ist der Versuch, möglichst viele Testfälle zu erfassen. Gerade in Legacy-Systemen mit komplexem oder historisch gewachsenem Verhalten führt dieser Ansatz jedoch schnell zu hohem Pflegeaufwand und geringer Aussagekraft. Entscheidend ist nicht die Menge der Tests, sondern wie repräsentativ sie sind.

Ziel der Testfallauswahl ist es, typische und kritische Verhaltensmuster des Systems sichtbar zu machen. Bewährt hat sich eine bewusste Mischung aus Normalfällen, Grenzfällen und historisch relevanten Sonderfällen. Normalfälle bilden den alltäglichen Betrieb ab und sorgen dafür, dass zentrale Geschäftsprozesse stabil bleiben. Grenzfälle decken Eingaben an fachlichen oder technischen Grenzen ab und helfen, unerwartete Regressionen frühzeitig zu erkennen. Sonderfälle schließlich spiegeln häufig genau jene implizite Logik wider, die über Jahre hinweg in das System eingebaut wurde und bei Migrationen besonders risikobehaftet ist.

Bei der Auswahl von Testfällen ist zudem Zurückhaltung geboten. Jeder Golden-Master-Test konserviert Verhalten – und damit auch Komplexität. Tests sollten daher nur dort entstehen, wo ihr Schutz tatsächlich benötigt wird. Ein kleiner, gut gewählter Satz an Testfällen ist in der Regel wertvoller als eine breite, aber unstrukturierte Abdeckung.

Produktionsdaten können bei der Testfallerstellung eine wertvolle Grundlage sein, erfordern jedoch besondere Sorgfalt. Sie sollten anonymisiert, reproduzierbar und fachlich nachvollziehbar sein. Eine unreflektierte Übernahme großer Datenmengen führt häufig zu schwer wartbaren Tests, deren Aussagekraft mit der Zeit abnimmt.

Letztlich ist die Auswahl von Golden-Master-Testfällen immer eine Risikoentscheidung. Sie orientiert sich nicht an theoretischer Vollständigkeit, sondern an der Frage, welches Verhalten bei der Migration auf keinen Fall unbeabsichtigt verändert werden darf. Diese bewusste Fokussierung macht Golden Master Testing praktikabel und verhindert, dass der Ansatz selbst zum Hindernis für Veränderung wird.

Umgang mit nichtdeterministischem Verhalten

Golden-Master-Tests setzen voraus, dass identische Eingaben zu vergleichbaren Ausgaben führen. In der Praxis ist diese Annahme bei Legacy-Systemen jedoch häufig nicht erfüllt. Zeitabhängige Werte, zufällige Identifikatoren, implizite Sortierungen, externe Datenbanken oder andere externe Systeme führen dazu, dass sich Ausgaben trotz unverändertem fachlichem Verhalten unterscheiden. Ohne bewussten Umgang mit dieser Nichtdeterministik verlieren Golden-Master-Tests schnell ihre Aussagekraft.

Typische Ursachen nichtdeterministischen Verhaltens sind Zeitstempel, fortlaufende Nummern, technische IDs oder kontextabhängige Reihenfolgen in Listen und Reports. Auch externe Abhängigkeiten, etwa Datenbankzustände oder angebundene Systeme, können zu Abweichungen führen, die fachlich irrelevant, für den Testvergleich aber kritisch sind.

Der erste Schritt besteht darin, solche instabilen Anteile gezielt zu identifizieren. Abweichungen sollten nicht reflexartig akzeptiert oder ignoriert werden, sondern Anlass zur Analyse sein: Welche Teile der Ausgabe variieren und warum? Diese Transparenz ist Voraussetzung für jede weitere Stabilisierung.

Im zweiten Schritt werden die betroffenen Ausgabebereiche normalisiert. Zeitwerte können beispielsweise auf feste Referenzen gesetzt, technische IDs maskiert oder nicht relevante Felder aus dem Vergleich ausgeschlossen werden. Wichtig ist dabei, bewusst zwischen fachlich relevanten und rein technischen Unterschieden zu differenzieren. Alles, was fachliche Bedeutung hat, sollte weiterhin Bestandteil des Vergleichs bleiben.

Nicht jede Quelle von Nichtdeterminismus lässt sich sinnvoll beherrschen. In solchen Fällen ist zu prüfen, ob der gewählte Einstiegspunkt überhaupt für Golden-Master-Tests geeignet ist oder ob der Vergleich auf eine gröbere Ebene gehoben werden sollte. Manchmal ist ein weniger detaillierter, aber stabiler Vergleich wertvoller als ein vollständiger, aber fragiler Golden Master.
Der Umgang mit nichtdeterministischem Verhalten erfordert Disziplin und klare Entscheidungen. Wird diese Arbeit frühzeitig investiert, entstehen belastbare Tests, die strukturelle Veränderungen absichern, ohne bei jeder Ausführung Fehlalarme zu produzieren. Vernachlässigt man diesen Schritt, wird Golden Master Testing schnell als unzuverlässig wahrgenommen – und damit seines eigentlichen Nutzens beraubt.

DIE KUNST DER SOTWARE-ARCHITEKTUR

Architecture & Design-Track entdecken

 

Typische Fallstricke und Antipatterns

Golden Master Testing ist in Migrationsprojekten ein äußerst wirkungsvolles Instrument – zugleich aber auch anfällig für Fehlanwendungen. Viele der Probleme entstehen nicht durch das Konzept selbst, sondern durch falsche Erwartungen und mangelnde Disziplin im Umgang mit den Tests.

Ein verbreiteter Irrtum besteht darin, Golden-Master-Tests als Ersatz für fachliche Tests zu betrachten. Sie sichern ausschließlich das beobachtbare Verhalten eines Systems ab, nicht jedoch dessen fachliche Bedeutung oder Korrektheit. Wird dieser Unterschied nicht bewusst gemacht, bleibt Fachlichkeit dauerhaft implizit. Das Team vergleicht nur noch mit dem Vergangenen, ohne das Verhalten fachlich zu hinterfragen oder weiterzuentwickeln. Die Migration wird dadurch zwar technisch abgesichert, fachlich jedoch nicht beherrschbar.

Ein weiteres häufiges Problem ist die unkontrollierte Ausweitung der Golden-Master-Test-Suite. Es bringt nichts, automatisiert alle möglichen Varianten von Eingaben zu generieren und sie gegen das System zu schicken. In solchen automatisierten Set-ups entstehen schnell sehr große Mengen an Tests, deren Pflegeaufwand und Laufzeit stetig wachsen. Die eigentliche Aussagekraft der Tests leidet darunter, weil Abweichungen immer schwerer zu interpretieren sind. Statt gezielt Sicherheit zu schaffen, erzeugen solche Testlandschaften Unsicherheit und führen dazu, dass Warnungen ignoriert oder pauschal akzeptiert werden.

Besonders kritisch ist der bereits erwähnte Umgang mit nichtdeterministischem Verhalten. Dieses Verhalten darf nicht toleriert werden. Es muss sichergestellt sein, dass die Tests immer grün sind, wenn sich nichts Fachliches verändert hat. Ansonsten gehen echte fachliche Abweichungen im ständigen Rauschen unter. Ein instabiler Golden Master ist gefährlicher als gar keiner, da er vermeintliche Sicherheit vorgaukelt, wo keine existiert.

Eng damit verbunden ist das blinde Akzeptieren von Abweichungen. Werden Referenzausgaben aktualisiert, ohne die Ursache der Differenz verstanden zu haben, wird Wissen dauerhaft vernichtet. Fachliche Änderungen, Regressionen oder unbeabsichtigte Effekte werden stillschweigend in den Golden Master übernommen und damit legitimiert. Der Test dokumentiert dann nicht mehr das Verhalten des Legacy-Systems, sondern lediglich den aktuellen Zustand – unabhängig davon, ob dieser fachlich korrekt ist.

Auch die Wahl der Vergleichsebene ist ein häufiger Stolperstein. Zu feingranulare Vergleiche auf rein technischer Ebene, etwa vollständige Datenbank-Dumps oder detaillierte interne Strukturen, reagieren extrem empfindlich auf Änderungen in der Implementierung. Der fachliche Effekt geht dabei verloren, während der Wartungsaufwand steigt. Golden-Master-Tests sollten sich daher an der fachlichen Wirkung orientieren, nicht an technischen Details.

Das vielleicht subtilste Antipattern ist das dauerhafte Festhalten an diesen Tests. Was ursprünglich als temporäre Lösung für eine regressionsfreie Migration gedacht war, wird aus Bequemlichkeit, Unsicherheit oder aus Kostengründen zur Dauerlösung. In solchen Fällen bleibt Fachlichkeit dauerhaft implizit, fachliche Tests werden nie aufgebaut und die Migration bleibt unvollständig. Der eigentliche Erfolg von Golden Master Testing liegt darin, schrittweise überflüssig zu werden, sobald Fachlichkeit verstanden, modelliert und explizit getestet ist.

Richtig eingesetzt schaffen Golden-Master-Tests Vertrauen in Veränderungen bei unbekannter Fachlichkeit. Falsch eingesetzt konservieren sie genau die Intransparenz, die eine Migration eigentlich überwinden soll.

KI-Ensatz zur Generierung von Golden-Master-Tests

Der initiale Aufbau von Golden-Master-Tests ist in Migrationsprojekten oft der größte Hemmschuh. Gerade bei umfangreichen Legacy-Systemen fehlt es an Dokumentation, fachlichem Wissen und klaren Einstiegspunkten. Hier kann der gezielte Einsatz von KI einen entscheidenden Unterschied machen.

KI eignet sich besonders gut, um bestehende Legacy-Artefakte systematisch auszuwerten. Quellcode, Schnittstellenbeschreibungen, Datenformate oder bestehende Batchjobs enthalten implizites Wissen darüber, wie das System genutzt wird und welche Eingaben zu welchen Ausgaben führen. KI-gestützte Analysen können diese Informationen bündeln und daraus Vorschläge für sinnvolle Testfälle ableiten. Statt Tests manuell aus fachlichen Annahmen zu entwerfen, entstehen Golden-Master-Tests direkt aus dem tatsächlich beobachtbaren Verhalten des Systems.

Ein zentraler Mehrwert liegt dabei in der Skalierung. Während manuell erstellte Golden-Master-Tests meist auf wenige exemplarische Fälle beschränkt bleiben, kann KI eine große Bandbreite realistischer Szenarien abdecken. Insbesondere in Kombination mit Tools zur Messung der Testabdeckung, etwa JaCoCo, deren Ergebnisse als Input für KI dienen, kann sie automatisch typische Eingabekombinationen, Randfälle und Sonderlogiken auf Basis der Codeverzweigungen identifizieren und gezielt Tests generieren. So lassen sich auch selten genutzte, aber potenziell geschäftskritische Ablaufpfade absichern.
Wichtig ist jedoch, KI nicht als autonomes Werkzeug zu verstehen, das dauerhaft Tests schreibt. Die generierten Tests liefern eine gute Basis für das initiale Erstellen der Golden-Master-Tests. Sobald es in die fachliche Bewertung geht, ist das Team wieder gefordert. Gerade bei Abweichungen zwischen Legacy- und Zielsystem ist menschliche Interpretation unverzichtbar, um zu entscheiden, ob es sich um fachlich relevante Unterschiede oder um technische Artefakte handelt.

Richtig eingesetzt beschleunigt KI vor allem die frühe Phase der Migration. Sie reduziert den manuellen Aufwand, senkt die Einstiegshürde und macht Golden Master Testing auch bei sehr großen Systemen praktikabel. Gleichzeitig bleibt die Verantwortung klar beim Entwicklungsteam: KI generiert Tests, aber sie entscheidet nicht über fachliche Korrektheit.

Langfristig unterstützt KI auch den Übergang weg vom Golden Master. Erkenntnisse aus den generierten Tests lassen sich nutzen, um fachliche Regeln explizit zu formulieren und in intentionale Tests zu überführen. So wird KI nicht nur zum Beschleuniger der Absicherung, sondern auch zum Katalysator für das fachliche Verständnis des Legacy-Systems.

Der Einsatz von KI verändert damit nicht das Prinzip des Golden Master Testing, sondern dessen Wirtschaftlichkeit. Was früher Wochen oder Monate manueller Arbeit erforderte, kann heute in kurzer Zeit vorbereitet werden – ohne den Anspruch an fachliche Sorgfalt aufzugeben.

Nach den Tests ist vor der Migration – auch mit KI?

Sobald belastbare Golden-Master-Tests etabliert sind, verändert sich die Rolle von KI im Migrationsprojekt grundlegend. Während KI zuvor vor allem beim Aufbau der Tests unterstützt hat, kann sie nun direkt zur Umsetzung der Migration beitragen. Die Golden-Master-Tests bilden dabei den entscheidenden Sicherheitsanker: Sie definieren das erwartete Verhalten des Systems unabhängig von Technologie und Implementierung.

Auf dieser Grundlage kann KI gezielt eingesetzt werden, um Legacy-Code zu analysieren, zu transformieren oder neu zu strukturieren. Da das gewünschte Verhalten durch die Golden-Master-Tests bereits abgesichert ist, entsteht ein geschützter Raum für automatisierte oder teilautomatisierte Änderungen. KI-gestützte Refactorings, Übersetzungen zwischen Programmiersprachen und die Extraktion von Services können iterativ geschehen, ohne dass jede Änderung manuell fachlich verifiziert werden muss.

Ein wesentlicher Vorteil liegt in der Möglichkeit, alternative Implementierungen zu vergleichen. KI kann verschiedene Migrationsstrategien vorschlagen oder unterschiedliche Zielarchitekturen unterstützen, während die Golden-Master-Tests zuverlässig anzeigen, ob das fachliche Verhalten erhalten bleibt. Dadurch verschiebt sich der Fokus des Teams von der reinen Absicherung hin zur bewussten Gestaltung der Zielarchitektur.

Darüber hinaus lassen sich die Tests als Feedbackmechanismus für KI-gestützte Transformationen nutzen. Schlägt eine Migration fehl oder entstehen unerwartete Abweichungen, liefern sie präzise Hinweise darauf, an welchen Stellen fachliches Verhalten verändert wurde. Diese Rückkopplung ermöglicht es, KI iterativ zu steuern und schrittweise bessere Ergebnisse zu erzielen, statt große, riskante Migrationsschritte zu gehen.

Auch bei der Modularisierung bestehender Systeme entfaltet diese Kombination ihren Nutzen. KI kann dabei helfen, fachlich zusammengehörige Logik zu identifizieren, Abhängigkeiten sichtbar zu machen und Kandidaten für eigenständige Module oder Services vorzuschlagen. Die Golden-Master-Tests stellen sicher, dass diese Schnitte fachlich korrekt bleiben und nicht unbeabsichtigt Verhalten verlieren oder verändern.

Entscheidend ist dabei, dass KI nicht als autonomer Migrator verstanden wird. Sie arbeitet innerhalb eines klar definierten Rahmens, der durch Golden-Master-Tests und fachliche Entscheidungen des Teams vorgegeben ist. Die Verantwortung für Architektur, Fachlichkeit und Priorisierung bleibt weiterhin beim Menschen.

In dieser Rolle wird KI zu einem wirkungsvollen Verstärker: Sie beschleunigt Analyse und Umsetzung, während die Tests die notwendige Sicherheit liefern. Gemeinsam ermöglichen sie eine Migration, die schrittweise, kontrolliert und auch bei unbekannter Fachlichkeit verantwortbar bleibt.

Fachlichkeit zurückgewinnen statt nur bewahren

In vielen Legacy-Systemen ist Fachlichkeit nicht nur technisch verborgen, sondern auch organisatorisch entkoppelt. Wissen steckt in einzelnen Köpfen, in externen Dienstleistern oder in historisch gewachsenen Prozessen, die niemand mehr vollständig überblickt. Entscheidungen werden vermieden oder vertagt, weil niemand sicher sagen kann, welches Verhalten fachlich korrekt ist. Migration wird in solchen Situationen zwangsläufig defensiv: Ziel ist es, nichts kaputt zu machen – und nicht, das System aktiv weiterzuentwickeln.

Das Zusammenspiel aus Golden Master Testing und KI bietet hier eine Chance zur organisatorischen Neuausrichtung. Die Tests machen Systemverhalten unabhängig von Personen reproduzierbar. Sie entziehen implizitem Wissen seinen exklusiven Charakter und überführen es in ein gemeinsames, überprüfbares Artefakt. Fachliche Diskussionen stützen sich nicht länger auf Erinnerungen oder Vermutungen, sondern auf beobachtbares Verhalten.

KI verstärkt diesen Effekt, indem sie große Mengen an Verhalten systematisch erschließt und sichtbar macht. Nutzungsmuster, Sonderfälle und Abhängigkeiten werden transparent, ohne dass das Wissen einzelner Experten vorausgesetzt wird. Damit verändert sich die Gesprächsgrundlage mit der Fachabteilung: Statt „Wer weiß, wie das früher gemeint war?“, tritt die Frage „Wollen wir dieses Verhalten künftig noch?“ in den Vordergrund.

Dieser Perspektivwechsel ist organisatorisch entscheidend. Verantwortung für Fachlichkeit kann wieder im Team verankert werden, weil Entscheidungen auf einer belastbaren Basis getroffen werden. Golden-Master-Tests schaffen Sicherheit, KI reduziert Analyseaufwand – und gemeinsam ermöglichen sie es, fachliche Verantwortung bewusst zu übernehmen, statt sie weiter zu delegieren oder zu konservieren.

Mit fortschreitender Migration verschiebt sich die Rolle der Tests. Fachliche Regeln werden explizit formuliert, Golden-Master-Tests schrittweise abgelöst und Wissen in Modelle, Dokumentation und fachliche Tests überführt. Das System wird damit nicht nur technisch modernisiert, sondern organisatorisch neu verankert.

Migration wird so zu mehr als einem Umbau: Sie wird zu einem Prozess der Rückaneignung. Die Organisation gewinnt Entscheidungsfähigkeit zurück, Abhängigkeiten werden reduziert und Fachlichkeit wird wieder gestaltbar. Genau darin liegt der nachhaltige Wert dieses Vorgehens.

Stay tuned

Regelmäßig News zur Konferenz und der JAX-Community erhalten

[mc4wp-simple-turnstile]

 

Fazit: Sichere Migration beginnt mit beobachtbarem Verhalten

Migrationen scheitern selten an Technologie, sondern vielmehr an fehlender fachlicher Sicherheit. Legacy-Systeme enthalten oft jahrzehntelang gewachsene Fachlichkeit, die nur implizit im Code, in Datenstrukturen oder in Prozessen existiert. Hier setzt Golden-Master-Testing an: Es ermöglicht, bestehendes Verhalten zu konservieren, ohne es vollständig verstehen zu müssen, und schafft damit die Sicherheit für tiefgreifende Veränderungen.

Richtig eingesetzt sind diese Tests kein Ersatz für fachliche Tests, sondern ein Übergangswerkzeug. Sie machen implizites Wissen reproduzierbar und liefern die Grundlage für bewusste Entscheidungen. Abweichungen werden sichtbar, analysiert und fachlich eingeordnet. So entsteht die Möglichkeit, Fachlichkeit nicht nur zu bewahren, sondern aktiv zurückzugewinnen. Wissen, das zuvor in einzelnen Köpfen, alten Prozessen oder externen Dienstleistern verborgen war, wird ins Team zurückgeführt und kann bewusst gestaltet werden.

Der gezielte Einsatz von KI verstärkt diesen Ansatz erheblich. KI unterstützt beim Aufbau der Tests. Sie identifiziert Muster, macht Sonderfälle transparent und liefert Vorschläge für Refactoring oder Modularisierung – abgesichert durch die Golden-Master-Tests. Die Verantwortung für Fachlichkeit, Architektur und Priorisierung bleibt dabei beim Team, das nun auf einer stabilen und überprüfbaren Basis agiert.

Langfristig ermöglicht dieses Vorgehen eine Migration, die sowohl technisch als auch organisatorisch tragfähig ist. Golden-Master-Tests werden schrittweise durch explizite fachliche Tests ersetzt, Wissen wird dokumentiert und Fachlichkeit wieder gestaltbar. Migration wird so nicht nur zum Umbau von Systemen, sondern zum Prozess der Rückaneignung von Fachlichkeit und Verantwortung. Ihr größter Erfolg liegt darin, dass sie das Team befähigt, Entscheidungen bewusst zu treffen – sicher, nachvollziehbar und nachhaltig.

The post Golden Master Testing für Legacy-Systeme appeared first on JAX.

]]>
Observability für LLM-Anwendungen mit OpenTelemetry https://jax.de/blog/llm-observability-opentelemetry-langfuse/ Thu, 08 Jan 2026 13:40:11 +0000 https://jax.de/?p=108041 LLM-Projekte werden meist mit Begeisterung angegangen: Die ersten Tests mit einem Prompt sehen gut aus, das Team ist schnell gefunden und das Budget steht bereit. Doch die Herausforderungen beginnen mit der Skalierung des Prototyps. Hier gewinnt Observability zunehmend an Bedeutung. Denn LLM sind inhärent nicht deterministisch und ihr Einsatz bringt Chaos in die Entwicklung. Auch wenn sich das nicht vollkommen in den Griff kriegen lässt, gibt es doch Ansätze: Wir gehen an die Wurzeln von Observability, untersuchen, wie sie die LLM-Entwicklung unterstützt und Daten zur Qualitätsbewertung liefert.

The post Observability für LLM-Anwendungen mit OpenTelemetry appeared first on JAX.

]]>
Als Observability wird die Fähigkeit beschrieben, den internen Zustand eines (komplexen) Softwaresystems allein durch seine externen Ausgaben zu verstehen. Observability versetzt uns in die Lage, in Zukunft Antworten auf uns heute unbekannte Fragen zu geben, die wir an den internen Zustand eines Softwaresystems haben könnten (sogenannte unknown Unknowns).

Für uns Softwareentwickler bedeutet das, dass wir den internen Zustand unserer Microservices – aber auch die Kommunikation zwischen ihnen – durch Betrachtung der Ausgaben dieser Systeme erkennen können, ohne Änderungen an der Software zu deployen.

DIE MODERNISIERUNG MIT DEVOPS

DevOps & CI/CD-Track entdecken

 

In der Literatur werden diese Systemausgaben gern in folgende „drei Säulen“ der Observability geclustert:

  • Metriken bieten einen aggregierten Blick auf Zustand und Performance eines Systems über die Zeit. Es handelt sich dabei um numerische, annotierte Datenpunkte, die in festen Intervallen eingesammelt und zentral gespeichert werden (z. B. in Prometheus).
  • Traces verfolgen eine Anfrage von Anfang bis Ende durch ein verteiltes System und zeichnen Operationen innerhalb eines Systems sowie die Kommunikation zwischen Systemen auf.
  • Logs liefern feingranulare Informationen über individuelle Ereignisse. Sie sind inhaltlich unstrukturiert, können aber in strukturierter Form ausgegeben (JSON) und über Metadaten angereichert werden (Instanz, Version, Kritikalität).

Wer sich auf diese drei Säulen beschränkt, greift allerdings in dem Anspruch zu kurz, den internen Zustand einer Software ausschließlich von außen herleiten zu können. Charity Majors [1], bekannt als „Queen of Observability“ sagt: „There are no three pillars of observability.“ Jegliche nützliche Daten sollten Teil der Software-Observability sein. Dazu gehören z. B. auch JVM Tracer wie der Java Flight Recorder oder externe Webseitenprüfer wie der Prometheus Blackbox Exporter.

Wichtig ist: Nur gemeinsam ergeben die Daten ein umfassendes Bild des Verhaltens eines Systems:

  • Über Metriken erkennen wir eine Anomalie.
  • Mit Traces kann die verursachende Operation identifiziert werden.
  • Logs liefern die Details zur Operation.

    Um in der Fehleranalyse Metriken, Traces und Logs korrelieren zu können, müssen diese mit korrelierenden Metadaten annotiert werden. Und da wir von „unkown Unknowns“ sprechen, sollten diese Metadaten möglichst vielfältig sein, denn wir wissen nicht, welche Fragen wir in Zukunft an die Software haben werden.

    So wie die Definition, welche Art von Daten eigentlich Teil der eigenen Software-Observability sind, ist auch die Wahl der Observability-Tools einem ständigen Wandel unterlegen. In der Vergangenheit haben Open Source, aber auch kommerzielle Tools meist nur eine der drei Säulen bedient. Das lag sicherlich an den fehlenden Standards, was sich zuletzt aber geändert hat. Hier schafft der OpenTelemetry-Standard Abhilfe.

    Tracing mit OpenTelemetry

    OpenTelemetry (OTEL) ist ein Projekt der Cloud Native Computing Foundation (CNCF), das aus den Vorgängern OpenTracing und OpenCensus hervorgegangen ist. Beide waren angetreten, um einen einheitlichen Weg zu schaffen, Code zu instrumentieren und Telemetriedaten einheitlich an ein Backend zu schicken. Keines der Projekte konnte sich wirklich durchsetzen, was sich mit deren Zusammenlegung geändert hat. OpenTelemetry ist der Standard für Distributed Tracing, schickt sich inzwischen aber an, Log-Shipping und Metriken neu zu standardisieren.

    Das Distributed Tracing mit OpenTelemetry baut sich aus Spans auf. Ein Span wird über ein JSON-Dokument definiert und repräsentiert eine Transaktion innerhalb eines Softwaresystems. Das kann ein HTTP-Aufruf sein oder ein Datenbankzugriff. Spans können eine beliebige Granularität haben. Sie sind hierarchisch aufgebaut, haben einen Start- und Endzeitpunkt und werden (system-)übergreifend zu einem Trace zusammengefasst (Listing 1).

    {
      "name": "hello",
      "context": {
        "trace_id": "5b8aa5a2d2c872e8321cf37308d69df2",
        "span_id": "051581bf3cb55c13"
      },
      "parent_id": null,
      "start_time": "2025-04-29T18:52:58.114201Z",
      "end_time": "2025-04-29T18:52:58.114687Z",
      "attributes": {
        "http.route": "/users/{id}"
      },
      "events": [
        {
          "name": "Guten Tag!",
          "timestamp": "2025-04-29T18:52:58.114561Z",
          "attributes": {
            "event_attributes": 1
          }
        }
      ]
    }
    

    Beim verteilten Tracing werden die Trace-IDs via HTTP-Header von System zu System weitergereicht. Meist übernimmt das Tracing-Framework diese Manipulation der HTTP Requests transparent. Jedes der an einem verteilten Trace beteiligten Systeme liefert seine Spans an einen zentralen OTEL-Empfänger, der aus den Informationen ein Gesamtbild zusammenpuzzelt (Abb. 1).

    Interessant wird es, wenn Spans mit Attributen angereichert werden. OpenTelemetry definiert einen Standardkatalog an Attributschlüsseln und -werten [2] in diversen Kategorien, z. B. für HTTP- oder Datenbanktransaktionen. In diesem Standardkatalog befindet sich auch ein eigener Abschnitt für GenAI-Attribute. Dazu gehören natürlich das in einer GenAI-Operation genutzte Modell (gen_ai.request.model), aber auch die LLM-Operation (gen_ai.operation.name) und die verbrauchten Tokens (gen_ai.usage.total_tokens) (Listing 2).

    {
      "attributes": {
        "gen_ai.request.model": "google/gemini-2.5-flash",
        "gen_ai.system": "openai",
        "gen_ai.request.temperature": "0.8",
        "gen_ai.response.id": "gen-1762722252-LMWMkgi5DkSwFhFaBmu3",
        "gen_ai.response.finish_reasons": [ "STOP" ],
        "gen_ai.operation.name": "chat",
        "gen_ai.usage.input_tokens": "6518",
        "gen_ai.usage.total_tokens": "6958",
        "gen_ai.response.model": "google/gemini-2.5-flash",
        "gen_ai.usage.output_tokens": "440"
      }
    }
    

    Mittels dieser Attribute lassen sich die gängigen LLM-Operationen im OTEL-Empfänger grafisch aufbereiten. Bei LLM-Anwendungen können wir auf den ersten Blick erkennen, wo Latenz verloren geht, welche Prompts generiert werden, welche RAG-Dokumente selektiert wurden und – durch die aufgezeichnete Token-Nutzung – wie viel uns eine Transaktion gekostet hat.

    Stay tuned

    Regelmäßig News zur Konferenz und der JAX-Community erhalten

    [mc4wp-simple-turnstile]

     

    Observability in LLM-Anwendungen

    Zu Beginn eines LLM-Entwicklungsprojekts wird gern viel Zeit auf die Frage verwendet, welches LLM-Modell das am besten geeignete sei. Mit Fortschritt des Projekts wird die Irrelevanz dieser Eingangsfrage immer deutlicher, denn das Modell und der Modellanbieter wurden schon dreimal aktualisiert oder komplett gewechselt: Die wahren Probleme der Entwicklung von LLM-Anwendungen treten an anderer Stelle zutage. Hugo Bowne-Anderson und Stefan Krawczyk beschreiben in [3], wie sich der Entwicklungszyklus bei LLM-Anwendungen anpassen muss, und stellen u. a. folgende Bedingungen für einen erfolgreichen LLM-Entwicklungsprozess auf:

    • Möglichst vollständige Observability von Anfang an ist entscheidend, um Probleme zu diagnostizieren.
    • Synthetische Daten helfen, den Entwicklungsprozess zu beschleunigen, bevor echte Nutzer mit dem System interagieren.
    • Strukturierte Evaluationsmethoden helfen Entwicklungsteam eher dabei, besser zu werden als rein intuitionsgetriebene Verbesserungen.

    „In AI systems, evaluation and monitoring don’t come last – they drive the build process from day one“, heißt es bei Bowne-Anderson und Krawczyk.

    Im Unterschied zum klassischen Entwicklungsprozess kommen der Überwachung und Evaluierung einer LLM-Anwendung „in freier Wildbahn“ ein wesentlich größerer Anteil zu. In klassischen deterministischen Anwendungen reichen Unit-, Integrations- und Akzeptanztests aus, um die Funktionen einer Anwendung zu testen. Mit der Integration nichtdeterministischer LLMs in den Anwendungscode entsteht ein Portal in die chaotische Entropie der echten Welt. Das führt dazu, dass man LLM-Anwendungen unweigerlich in wesentlich mehr Experimentierschleifen entwickeln muss als klassische Software – denn wir können nur begrenzt vorhersagen, wie das LLM sich verhalten wird. Deshalb ist es für das Gelingen eines LLM-Softwareentwicklungsprojekts essenziell, von Tag eins an ein umfassendes Tracing der Anwendung zu haben.

    „You can’t fix what you can’t see“: Der Grundsatz der frühen Observability im LLM-Entwicklungszyklus gilt unabhängig von der eingesetzten LLM-Architektur. Je komplexer und eventuell verteilter diese ist, desto dringender wird er jedoch:

    • One-Shot: Ein LLM Call mit einem System- und optional einem User-Prompt. Die Antwort wird von der Anwendung verarbeitet, komplexe Antworten werden als Structured Output im JSON-Format ausgegeben. Hinsichtlich der Observability sind die eingehenden Prompts, das gewünschte JSON-Antwort-Schema (wenn vorhanden) und natürlich die LLM-Antwort von Interesse.
    • Retrieval-augmented Generation (RAG): Vor dem LLM-Call werden aus einer Vektordatenbank relevante Dokumente extrahiert. Diese werden dem LLM im Prompt als Kontext mitgegeben, um daraus eine Antwort zu generieren.
    • Function Calls: Das LLM arbeitet wie in der One-Shot-Architektur, hat jedoch zusätzlich Tools für das Function Calling registriert. Diese Tools kann das LLM nutzen, um auf Daten zuzugreifen, die nicht in seinem Weltwissen vorhanden sind und die ihm die Anwendung auf Wunsch bereitstellt (z. B. Produkte, Kundendaten, Wetter). Hier interessierten uns zu den Daten oben die registrierten Tools und die erfolgten Tool-Calls.
    • Agentic Workflow: Im Endeffekt ist das eine Mischung der beiden vorher genannten Architekturen auf Steroiden: Ein großer Prompt wird in kleinere dedizierte Aufgaben aufgebrochen. Die Übergabe zwischen den einzelnen Prompts geschieht mittels Anwendungscode. Die Anwendung hat die Möglichkeit, Daten in einem Context zwischen den Prompts zu transportieren. Die Workflows können auch verteilt ablaufen. Hier interessiert uns zusätzlich noch die Übergabe von Daten zwischen den einzelnen LLM-Calls und gegebenenfalls die verteilte Anwendungsausführung.

    Üblicherweise werden im verteilten Tracing nur Stichproben aufgezeichnet (Sampling), d. h., nur ein gewisser Prozentsatz (zwischen einem und zehn Prozent, je nach Volumen) aller Transaktionen wird an den OTEL-Empfänger geschickt. Beim Sampling in verteilten Umgebungen wird darauf geachtet, dass es auch über alle beteiligten Microservices konsistent ist, also ein einmal aufgezeichneter Trace auch von allen beteiligten Microservices aufgezeichnet und verschickt wird.

    Die Erfahrung hat jedoch gezeigt, dass es sich in LLM-Anwendungen anbietet, das Sampling komplett zu deaktivieren. So sind wir nämlich in der Lage, Nutzersessions vollständig nachzuvollziehen. Bezogen auf das Beispiel eines Chatbots möchten wir für eine Nutzersession den gesamten geführten Dialog und nicht nur Teile daraus nachvollziehen können.

    Zum Glück lässt sich in unser aller Lieblingsframework Spring Boot Observability und speziell Tracing mit OpenTelemetry sehr leicht aktivieren [6]. Um Span-Definitionen per Java-Annotation (@NewSpan) steuern zu können, muss die Property management.observations.annotations.enabled aktiviert und die spring-boot-starter-aop-Abhängigkeit in die Anwendung inkludiert werden, da die Annotationen als AspectJ Pointcuts implementiert sind. Nutzen wir jetzt Spring AI für den LLM-Zugriff in unserer Anwendung, integriert sich Spring AI nahtlos in die Spring Observability.

    Langfuse liebt Spring AI

    Doch wohin schicke ich denn nun meine OTEL-Traces und Spans? Jeder Observability-Anbieter (Elastic, Grafana, New Relic, Data Dog) hat einen OTEL-Empfänger im Angebot und kann OTEL-Traces entsprechend aufbereiten. Bei LLM-Anwendungen haben wir aber spezielle Anforderungen an einen OTEL-Empfänger:

    • Traces sollen mit einem Fokus auf LLM-Aufrufe aufbereitet werden, denn uns interessieren vor allem die Interaktionen mit dem LLM (Prompts und Tools).
    • Traces sollen in einer Session-Übersicht aufbereitet sein, sodass ich Konversationen zwischen Nutzer und z. B. einem Chatbot nachvollziehen kann.
    • Die Darstellung soll die verschiedenen LLM-Architekturen (One-Shot, RAG, Agentic Workflows) unterstützen und die Ansicht an diese anpassen.

    All das können die verbreiteten OTEL-Empfänger nicht (oder nur zum Teil) leisten, da sie sich bisher (meist) auf die Darstellung verteilter Traces konzentriert haben. Wie so oft in der Informatik gilt deshalb für LLM-Traces: Use the right tool for the job! Hier kommen neue Anbieter wie Arize Phoenix [4] oder Langfuse [5] ins Spiel.

    Langfuse (Abb. 2, Kasten: „Observability am Beispiel MidMind“) ist ein Open-Source-Produkt des gleichnamigen Berliner Start-ups und erfreut sich in der LLM-Python-Welt schon länger großer Beliebtheit. Es erfüllt die oben genannten Punkte voll und hat inzwischen auch einen OTEL-Empfänger spendiert bekommen. Damit lässt es sich ideal mit Java-Anwendungen und Java-LLM-Frameworks wie Spring AI kombinieren.

    Langfuse ist Open Source und wird in einem Freemium-Modell vertrieben. Der Kern der Anwendung und die Hauptfeatures sind Open Source, bestimmte Zusatzfunktionen benötigen eine kostenpflichtige Lizenz. Die Anwendung lässt sich on Premise betreiben, ein Cloud-Dienst wird ebenfalls angeboten. Zum Testen und Kennenlernen oder für kleinere Projekte kann ich das Freikontingent im Cloud-Offering sehr empfehlen.

    Observability am Beispiel MidMind

    Was sind denn das für merkwürdige Baby-Beispiele in den Screenshots und den Tests? Die Beispiele stammen aus der digitalen Hebammen-App MidMind, die ich für meine Frau (Hebamme) entwickelt habe. Als einfache RAG-Anwendung auf Basis von Spring AI hilft sie ihr, die Vielfalt an Wissen, Leitlinien und Vorschriften bei Fragen rund um Geburt und Wochenbett instantan zu durchsuchen und zusammenzufassen.

    Das Tool hat seine Stärken im Darstellen von Spans innerhalb eines Traces auf einem System und glänzt dort mit Detailreichtum hinsichtlich LLM-Attributen. Über die Session-Übersicht lassen sich sehr schöne User-Sessions (im Beispiel ein E-Commerce-Chatbot, Abb. 3) verfolgen. Über eine User-Übersicht kann ich weitere Sessions eines Nutzers aufrufen. Langfuse möchte aber noch viel mehr sein als ein OTEL-Empfänger und bietet Funktionen zum Einsammeln von Nutzerfeedback, zur Promptoptimierung und zum Kategorisieren von Sessions in Datasets an.

    Die Integration in Spring Boot bzw. Spring AI ist denkbar einfach [6]. Nachdem die OpenTelemetry-Dependencies eingebunden sind, muss nur noch der OTEL-Endpunkt von Langfuse konfiguriert werden. Für detailreichere OTEL-Span-Attribute kann der im Langfuse Onboarding empfohlene ObservationFilter implementiert werden. Und schon kann fleißig getract, beobachtet und analysiert werden.

    Der klare Nachteil der Lösung ist, dass Langfuse eher eine LLM-Insellösung ist. In größeren Softwarelandschaften ist meist ein gemeinsamer Tracer konfiguriert, um Tracing in verteilten Systemlandschaften abzubilden. Ein solches Set-up ist sinnvoll und wichtig. Dort könnte man Langfuse als zweiten OTEL-Empfänger in einer Anwendung konfigurieren, was ein wenig Custom-Code bedeuten würde.

    LLM as a Judge

    Ich hatte eingangs dargestellt, dass neben möglichst vollständiger Observability synthetische Daten und strukturierte Evaluationsmethoden helfen, den Entwicklungsprozess zu beschleunigen. Zu Beginn eines LLM-Entwicklungsprojekts beweist man seine Hypothese mittels exemplarischer Daten anhand eines ersten Prompts. Das muss gar nicht notwendigerweise im Anwendungscode geschehen, das API des LLM-Modellanbieters reicht meistens aus.

    Ist man in die Anwendungsentwicklung eingestiegen, lassen sich für einen ersten Stresstest der aufgestellten Hypothese – ausgehend von den exemplarischen Daten – mittels einfacher Prompts synthetische Testdaten für die eigene Anwendung generieren. Beim Generieren dieser Daten sollten folgende Informationen miteinfließen:

    • Personas: Wer ist die Zielgruppe meiner Anwendung und wie wird diese benutzt? Personas lassen sich ideal mit LLMs simulieren. Ist der Nutzer zögerlich, gibt er immer den vollen Kontext seiner Anfrage mit oder referenziert er frühere Angaben?
    • Seed Queries: Um schon in dieser frühen Phase möglichst viel Entropie in den synthetischen Testdaten zu produzieren, sollte der Prompt keiner festen Struktur folgen, sondern die Anfrage sich aus Seed Queries erzeugen („Abo kündigen“, „Versandkosten“). Das LLM erzeugt dann mittels der definierten Persona einen individuellen Testdatensatz.

    Schon wenige synthetische Testdaten können große (und dann offensichtliche Bugs) in der LLM-Interaktion der Anwendung frühzeitig aufdecken. Observability und vor allem Tracing hilft in dieser Phase, Schwächen im System frühzeitig zu erkennen und zu beheben.

    Stay tuned

    Regelmäßig News zur Konferenz und der JAX-Community erhalten

    [mc4wp-simple-turnstile]

     

    Aber wie kann ich die synthetischen Daten strukturiert evaluieren? Da wir ja neben einer nichtdeterministischen Anwendungskomponente (dem LLM) nun auch vom LLM generierte Testdaten haben, können wir Ein- und Ausgabe unserer Tests nur schwer mit einer klassischen deterministischen Prüfung versehen. Deshalb sollten wir für die Prüfung auch ein LLM zurate ziehen und ihm die Entscheidung überlassen, ob ein Test erfolgreich war oder nicht.

    Spring AI verpackt dieses Evaluation Testing in Unit-Test-Cases. Das Framework stellt einen eigenen FactCheckingEvaluator [7] zur Verfügung, der mittels eines LLM die Ausgabe eines anderen LLM auf einen gegebenen Fakt überprüft. Als Beispiel:

    • LLM-Ausgabe: „Ein 9 Tage alter Säugling sollte etwa 1/6 seines aktuellen Körpergewichts pro Tag als Trinkmenge erhalten und benötigt mindestens 8 bis 12 Mahlzeiten in 24 Stunden.“
    • Zu prüfender Fakt: „8-12 of meals per day“

    Wir können sofort den Vorteil dieses „LLM as a Judge“ erkennen: Wir benötigen keine syntaktische Prüfung der LLM-Ausgabe, sondern können deren Semantik prüfen lassen (in diesem Beispiel sogar sprachübergreifend).

    LLM Knowledge Cutoff

    Lässt man LLM die Ausgaben der eigenen Anwendung bewerten, muss man je nach Anwendungsfall den Knowledge Cutoff des Weltwissens des genutzten LLM bedenken. Dieser liegt meist 12 bis 18 Monate vor Erscheinungsdatum des Modells. Am Beispiel eines Chatbots für die Produktsuche führt dieser Knowledge Cutoff dazu, dass dieser aktuell weder das iPhone 17 noch das iPhone 16 kennt. Weder die aktuellen ChatGPT- noch Google-Gemini-Modelle sind entsprechend aktuell.

    Das kann dazu führen, dass der Chatbot sich standhaft weigert, nach diesen „Fantasieprodukten“ zu suchen. Wenn man dem Chatbot aber das Wissen über die neuen Apple-Modelle in seinem Kontext mitgibt, sind es keine Fantasieprodukte mehr. Allerdings muss man dieses Wissen auch auf das LLM übertragen, das als Judge agiert. Denn sonst erhalten Unterhaltungen, die sich um das iPhone 17 drehen, eine schlechte Bewertung, da der Judge meint, der Chatbot würde Fantasieprodukte von Apple halluzinieren …

    Spinnt man den Gedanken des LLM as a Judge konsequent weiter, lässt sich auch die Qualität von Interaktionen in unterschiedlichen Dimensionen bewerten (in Schulnoten oder in Prozent). Neben der Korrektheit der Antworten (Kasten: „LLM Knowledge Cutoff“) könnte dazu auch die Tonalität oder Struktur der Antwort gehören. Auf diese Bewertungen können wir dann Akzeptanzkriterien legen und diese vor einem Anwendungs-Deployment testen.

    Auf solche Gedankenspiele ist das Spring-AI-Testing-Framework allerdings nicht ausgelegt bzw. man müsste sich eine entsprechende Erweiterung dazu bauen. Als Java-Entwickler möchten wir uns aber auf unseren Anwendungscode konzentrieren („Fertigungstiefe verringern“) und nicht in Zukunft eine JUnit-Erweiterung pflegen müssen. Wenn wir nun unseren Blick in die Python-LLM-Welt schweifen lassen, stoßen wir relativ schnell auf Promptfoo (Abb. 4). Mit Promptfoo erhalten wir ein umfassendes Testwerkzeug für LLM-Anwendungen, das zum einen die Qualität prüft, aber auch OWASP-Angriffsvektoren [8] testen kann.

    Promptfoo kann von Haus aus unterschiedlichste HTTP und WebSocket APIs testen. Standard-APIs wie OpenAI funktionieren direkt ohne eigenen Adapter. Eigene APIs, Protokolle oder komplexe Interaktionen lassen sich über Plug-ins definieren. Diese Provider können jetzt mit Testnachrichten angefragt werden und deren Antworten testet Promptfoo. Am Ende erstellt das Tool einen übersichtlichen Testbericht (Listing 3).

    description: "midmind.de"
    prompts:
      - "{{ prompt }}"
    providers:
      - id: openai:chat:midmind/app
        config:
          apiBaseUrl: http://host.docker.internal:8080/v1
          apiKey: "-"
    defaultTest:
      options:
        provider: openai:gpt-5-nano
    tests:
      - vars:
          prompt: >
            Wieviel Milch sollte ein 9 Tage alter Säugling pro Mahlzeit trinken
            und wie viele Mahlzeiten benötigt er in 24h?
        assert:
          # Literatur
          - type: icontains
            value: Kapitel 33.4 Tagestrinkmenge
          # Content
          - type: llm-rubric
            value: >
              ensure that the output provides a specific 
              amount of milk in milliliters and the number 
              of meals per day
          - type: factuality
            value: 8-12 of meals per day
    

    Interaktionen können sowohl deterministischen Tests (icontains – reguläre Ausdrücke) als auch LLM-gestützten Tests unterzogen werden. Dank standardisierter Prompts schreibt sich ein „factuality“-Faktenchecker sehr schnell, und komplexere Tests können mittels der llm-rubric-Prüfung formuliert werden. Bewertungen einer Antwort bzw. einer Interaktion können mittels des g-eval realisiert werden. Dieser (und weitere *-relevance)Tests bewerten eine Antwort mit einem Wert zwischen 0 und 1 und schlagen unterhalb eines konfigurierten Schwellenwerts fehl.

    Promptfoo kann On Premise aufgesetzt oder – wie Langfuse – als Cloud-Lösung eingekauft werden. Mittels einer zentralen Promptfoo-Instanz können die Ergebnisse unterschiedlicher Läufe vorgehalten und untereinander verglichen werden. Promptfoo kann aber auch ohne zentrale Instanz rein lokal genutzt werden. Die Ergebnisse können z. B. als HTML-Report exportiert werden, der dann aber zugegebenmaßen eher hässlich rudimentär daherkommt. Die Sahnehaube auf dem Kuchen ist aber die bereitgestellte GitHub Action, mit der sich Promptfoo maximal einfach in den eigenen CI/CD-Prozess integrieren lässt. Wichtig ist, die Testsuite vollständig, aber knapp zu halten. Denn nur in der CI-Pipeline lassen sich Probleme frühzeitig entdecken und harte Qualitätsschranken erzwingen.

    Tests mit synthetischen Daten erzeugen eine Baseline der Qualität unserer Anwendung. Aber sie bilden nicht die reale Welt ab und deshalb ist es wichtig, echte Nutzer auf/in die Anwendung zu bekommen. Nur so können wir:

    • echtes Nutzerverhalten mit dem synthetisch Erzeugten abgleichen,
    • gewählte Personas der Realität anpassen und
    • die Bewertungen von Interaktionen mit echten Businesskennzahlen rückschließen.

    Um möglichst schnell echte Nutzer auf die Anwendung zu bekommen, sind je nach Anwendungsfall mehrere Szenarien denkbar:

    • Shadow-Traffic: Live-Traffic wird von der bestehenden Anwendung auf die neue LLM-Anwendung gespiegelt. Die ideale Möglichkeit, um Unterschiede zwischen alter und neuer, LLM-basierter Anwendungslogik zu analysieren.
    • Interne Testgruppe: Ein Friendly-User-Test mit einer besonders leidensfähigen, aber wohlgesonnenen Testgruppe. Neben dem eigenen Team vielleicht noch weitere Kolleg:innen.
    • Real User Testing: Eingeladene, eventuell betriebsfremde Nutzer erledigen definierte Aufgaben in der LLM-Anwendung unter Laborbedingungen. Die Tests werden aufgezeichnet und können gemeinsam mit dem Entwicklungsteam ausgewertet werden.
    • A/B-Tests: Tests mit echten Nutzern bergen das größte Risiko, können allerdings auch die spannendsten Nutzerinteraktionen erzeugen.

    Für synthetische und echte Nutzertests kann es von Vorteil sein, sie z. B. über einen HTTP-Header zu markieren und etwa als Langfuse-Tag zu speichern. So können die Tests später gezielt ausgewertet oder ausgeblendet werden. Denn: Mit Langfuse als OTEL-Empfänger sind wir in der komfortablen Situation, über das Langfuse REST API auf die Interaktionen zugreifen zu können.

    Und damit können wir unseren „Full Circle Moment“ erleben und die aufgezeichneten Interaktionen als Testprompts zurück in unser Test- und Evaluationsframework geben. Wo wir vorher noch synthetische Interaktionen bewertet haben, können wir nun reale Nutzerinteraktionen bewerten lassen und diese Daten für die iterative Weiterentwicklung der Anwendung nutzen.

    Red Teaming

    Je näher ein LLM-Entwicklungsprojekt dem Livegang rückt, desto mehr stellt sich die Frage nach der Sicherheit der Anwendung. Wenn das nichtdeterministische LLM auf die Entropie (a.k.a Wahnsinn) der Welt trifft, können durchaus amüsante Ergebnisse entstehen (Abb. 5).

    Allerdings geben solche Beispiele auch Grund zur Besorgnis, denn der Einsatz von LLMs in Anwendungen öffnet einen latent ungeschützten Angriffsvektor. Die OWASP hat die Angriffsszenarien auf GenAI- bzw. LLM-Anwendungen inzwischen in einen eigenen Katalog ausgelagert, der fortlaufend erweitert wird.

    Das Thema ist sehr komplex, und ich bin gespannt, ob und wie wir dem Fortschritt in den Angriffsvektoren in den nächsten Jahren Herr werden. In der Anwendungsentwicklung sollte sich niemand anmaßen, mit diesem Fortschritt mithalten zu können. Deshalb ist es durchaus sinnvoll, sich auf externe Tools zu verlassen, die für uns am Puls der Zeit bleiben.

    Promptfoo bietet mit dem Red Teaming [9] einen spannenden Ansatz in diese Richtung. Wir definieren in dem Tool die Angriffsvektoren, auf die wir unsere Anwendung testen wollen. Promptfoo generiert daraufhin mittels eines LLM mögliche Angriffsszenarien. Auf Basis dieser Angriffe evaluiert Promptfoo unsere Anwendung auf mögliche Schwächen (Listing 4). Bei der Generierung der Angriffsszenarien wird auf aktuelles Wissen zurückgegriffen, weshalb sie von Zeit zu Zeit neu generiert werden sollten (Kasten: „Promptfoo-Red-Teaming-Konfiguration“).

    description: "midmind.de"
    targets:
      - id: openai:chat:midmind/app
        config:
          apiBaseUrl: http://host.docker.internal:8080/v1
          apiKey: "-"
    defaultTest:
      options:
        provider: openai:gpt-5-nano
    redteam:
      numTests: 10
      plugins:
        - harmful:hate
      strategies:
        - jailbreak
      language: de
    

    Promptfoo-Red-Teaming-Konfiguration

    In der beispielhaften Red-Teaming-Konfiguration ist das Promptfoo Plug-in harmful:hate konfiguriert, das prüft, ob die Anwendung dazu verleitet werden kann, Hassrede zu erzeugen. Jedes Promptfoo-Plug-in steuert ein LLM, das darauf trainiert ist, Angriffe auf eine spezifische Schwachstelle zu generieren.

    Die Strategie „Jailbreak“ definiert, wie die Angriffe ausgeführt werden sollen. Sie ändert einen Prompt iterativ so lange, bis die Guardrails der Anwendung umgangen werden können.

    Taming PII

    Was wir im Tracing (aber auch im Logging) unbedingt vermeiden wollen, ist das Speichern personenbezogener Daten (Personally Identifiable Information, PII). Nur so umgehen wir die Büchse der Pandora der Datenschutz-Grundverordnung. Für eine Chatbotanwendung ist es offensichtlich, dass die personenbezogenen Daten anonymisiert werden müssen, bevor sie im Tracing-Storage oder in den Logdateien landen. Aber auch bei One-Shot-LLM-Anwendungen weiß man nie, welche Daten sich in den Anfragen verbergen („Entropie der Welt“).

    Für die Anonymiserung von PII gibt es selbstverständlich Cloud-Dienste, was auf den ersten Blick widersprüchlich wirken mag. Wir können aber auch Open-Source-Anwendungen wie DataFog [10] oder Microsoft Presidio [11] selbst hosten. Beide Anwendungen können an die eigenen (Anonymisierungs-)Bedürfnisse angepasst werden und benötigen ausgiebiges Testen, damit nicht zu viele oder zu wenige Entitäten anonymisiert werden. Leider wird bei beiden Anwendungen von Haus aus Englisch als einzige Sprache unterstützt. Das bedeutet, dass man hier ein wenig Hand anlegen muss, um auch eine (sinnvolle) deutschsprachige Anonymisierung zu bekommen.

    Beide Dienste haben REST APIs und lassen sich leicht von Spring aus via RestClient ansprechen und aus einem ObservationFilter heraus aufrufen. Wir vermeiden zusätzliche Latenz in unserer Anwendung, da das Versenden der Observation Spans (und damit auch das Filtern) asynchron zu Nutzertransaktion geschieht.

    Wrap-up

    Observability ist ein integraler Bestandteil des Entwicklungszyklus von LLM-Anwendungen und sollte so früh wie möglich eingebunden werden. Sie ist unerlässlich, um die eigene LLM-Anwendung zu verstehen und zu optimieren. Langfuse hat sich als ideales Werkzeug dafür herausgestellt, wird sich aber in den nächsten Jahren neben den großen OTEL-Anbietern behaupten müssen. Das LLM-Observability-Feld bleibt spannend und wird sich in den nächsten Jahren weiter professionalisieren. Mit OpenTelemetry sind wir aber für die Zukunft gut gerüstet!

    Stay tuned

    Regelmäßig News zur Konferenz und der JAX-Community erhalten

    [mc4wp-simple-turnstile]

     


    Links & Literatur

    [1] Majors, Charity; Fong-Jones, Liz; Miranda, George: „Observability Engineering“; O’Reilly Media, 2022

    [2] https://opentelemetry.io/docs/specs/semconv/registry/attributes/

    [3] https://www.oreilly.com/radar/escaping-poc-purgatory-evaluation-driven-development-for-ai-systems/

    [4] https://phoenix.arize.com/

    [5] https://langfuse.com/

    [6] https://langfuse.com/integrations/frameworks/spring-ai

    [7] https://docs.spring.io/spring-ai/reference/api/testing.html

    [8] https://www.promptfoo.dev/

    [9] https://www.promptfoo.dev/docs/red-team/

    [10] https://github.com/DataFog/datafog-python

    [11] https://github.com/microsoft/presidio

    The post Observability für LLM-Anwendungen mit OpenTelemetry appeared first on JAX.

    ]]>
    Mixed Teams in IT-Projekten: Vorteile, Herausforderungen & Best Practices https://jax.de/blog/mixed-teams-agile-it-projekte/ Mon, 08 Dec 2025 08:28:16 +0000 https://jax.de/?p=107983 Gemischte Teams aus Kunden und Dienstleistern gelten als Erfolgsmodell für moderne IT-Projekte. Sie versprechen schnellere Entscheidungen, direkteren Nutzerkontakt und gemeinsames Verantwortungsgefühl. Doch das Miteinander zweier Organisationen bringt auch Reibung, Rollenkonflikte und rechtliche Risiken mit sich. Dieser Artikel zeigt, worauf es beim Aufbau gemischter Teams ankommt und was sie erfolgreich macht.

    The post Mixed Teams in IT-Projekten: Vorteile, Herausforderungen & Best Practices appeared first on JAX.

    ]]>
    Pling! Rückblende in das Jahr 2013: Immer mehr agile Projektansätze tauchen auf. Scrum ist zwar etabliert, aber noch längst kein Standard. In vielen Projekten ist man stattdessen noch „klassisch“ nach Wasserfall unterwegs – genauso wie in meinem damaligen. Ich sitze also seit zwei Monaten am Pflichtenheft für das neue Release eines IT-Systems und werde für die geschätzten 80-100 Seiten wohl auch noch einige Zeit sitzen. Jeder Use Case, jede Maske, jeder Button, jeder Edge-Case wird detailliert beschrieben. Ohne eine einzige Zeile Code zu schreiben. In solchen klassischen Softwareprojekten war die Aufgabenverteilung zwischen Kunde und Dienstleister meist recht eindeutig geklärt: Der Dienstleister schreibt das Pflichtenheft, der Kunde nimmt es ab. Der Dienstleister entwickelt daraufhin die Software, der Kunde nimmt sie ab. Das war wenig flexibel, denn nur im Pflichtenheft detailliert niedergeschriebene Aspekte konnten auch in der späteren Software umgesetzt werden. Dafür gab es mit den Abnahmen jedoch sehr eindeutige Checkpoints: Der Kunde musste sich die Zeit für die Abnahme des Pflichtenheftes nehmen, da man sonst mit der Entwicklung der Software nicht beginnen konnte.

    DIE DIGITALE TRANSFORMATION STARTEN

    Mehr Talks zu Agile, People & Culture

     

    Heutzutage sind Wasserfallprojekte im IT-Bereich nur noch vereinzelt zu finden, stattdessen setzen viele Vorhaben auf agile Frameworks wie zum Beispiel Scrum. Das macht den Ablauf deutlich flexibler, da man Vereinbarungen und passende Realisierung in deutlich kleineren Zyklen umsetzen kann. Das führt nicht zwingend zu kürzeren Projektlaufzeiten, aber zu kürzeren Feedbackschleifen und damit zu einem Projektergebnis, das oft näher am Kundenwunsch liegt. Das flexiblere Vorgehen bringt allerdings auch Herausforderungen mit sich – und zwar nicht nur inhaltlich und zeitlich, sondern auch in Bezug auf die Teamaufstellung zwischen Kunde und Dienstleister. Während früher die Teams meist komplett getrennt waren, gibt es in heutigen agilen Projekten verschiedene Möglichkeiten für die Teamzusammensetzung. Diese oft enge Verzahnung von Teams über Unternehmensgrenzen hinweg beleuchtet auch die Studie „Digitalisierung und Softwareinvestition“ [1]. In der Auswahl von externen IT-Dienstleistern ist demzufolge den beauftragenden Unternehmen die „kollegiale Zusammenarbeit ohne große hierarchische Grenzen“ und „gemeinsames Arbeiten“ besonders wichtig (Abb. 1).

    Abb. 1: Kommunikation und Zuverlässigkeit sind zentrale Erwartungen in der Zusammenarbeit mit externen IT-Dienstleistern [1]

    Diese enge Zusammenarbeit zwischen Kunde und Dienstleister bietet sowohl Vorteile als auch Herausforderungen, auf die im weiteren Verlauf des Artikels näher eingegangen wird. Für Projektleiter:innen ist vor allem die Frage relevant, wie Verantwortung und Steuerung im Mixed Team sinnvoll verteilt werden können, sodass alle Teammitglieder konsequent auf das gleiche Projektziel hinarbeiten. Als Mixed Teams sind hier Projektteams zu verstehen, in denen Mitarbeitende von Kunden- und Dienstleisterseite gemeinsam, interdisziplinär und möglichst ohne künstliche Trennung auf ein Ziel hinarbeiten.

    Stay tuned

    Regelmäßig News zur Konferenz und der JAX-Community erhalten

    [mc4wp-simple-turnstile]

     

    Zwei Welten, ein Ziel: wenn Business und IT aufeinandertreffen

    Die Mitarbeitenden beider Parteien haben unterschiedliche Backgrounds, Ausbildungen und Erfahrungen. Das führt dazu, dass im gemeinsamen Projekt ggf. deutlich verschiedene Wissensgebiete aufeinandertreffen. Der Dienstleister beispielsweise hat oft Expertenwissen zu Technologien wie etwa KI, Cloud, bestimmten IT-Produkten usw. Der Kunde hingegen hat ausgiebiges Wissen in seiner Fachdomäne, also etwa Automotive, Telekommunikation, Bankenwesen usw. Für ein erfolgreiches Projekt braucht es beides: Fach- und IT-Know-how. Gerade diese Kombination führt zu besonders innovativen Lösungen [2], weil sich fachliches und technisches Know-how sinnvoll ergänzen. Je weniger „weiße Flecken“ es dabei gibt, desto wahrscheinlicher ist ein Projektabschluss im Rahmen des magischen Dreiecks (Time, Budget, Scope) oder Fünfecks (zzgl. Qualität und Team, Abb. 2). Zusätzlich führt Austausch und gegenseitiges Lernen über die Unternehmensgrenzen hinweg zu einem erweiterten Horizont. Außerdem steigert die hierdurch erlangte thematische Abwechslung die Motivation der Teammitglieder auf beiden Seiten.

    Abb. 2: Das magische Fünfeck definiert fünf Zieldimensionen für ein Projekt: Team, Leistung, Qualität, Termin und Budget

    Kurze Wege, bessere Ergebnisse: Der Draht zum Anwender zählt

    In klassischen Softwareprojekten, bei denen die Umsetzung vollständig durch einen externen IT-Dienstleister erfolgt, gestaltet sich der direkte Austausch mit zentralen Stakeholdern, insbesondere mit den späteren Anwendern, oft schwierig. Oft kommuniziert der Projektleiter des Dienstleisters mit dem Projektleiter auf Kundenseite, der wiederum als Mittelsmann zwischen Dienstleister und Nutzer fungiert. Diese mehrstufige Kommunikationskette erschwert nicht nur die Verständigung, sondern verlangsamt auch die Feedback-Zyklen erheblich. Schnelles, iteratives Arbeiten wird dadurch zur Herausforderung.

    Ganz anders sieht es in Mixed Teams aus, in denen Mitarbeitende des Dienstleisters und des Kunden gemeinsam an der Umsetzung arbeiten. Hier lassen sich wesentlich direktere und belastbarere Kommunikationswege etablieren – nicht nur auf Managementebene, sondern auch zwischen Fachexperten. So können beispielsweise Software-Engineers direkt mit Anwendern und Anwenderinnen in den Dialog treten, um Anforderungen besser zu verstehen, Nutzungskontexte nachvollziehbar zu machen und frühzeitig Rückmeldung zu erhalten. Dieser unmittelbare Austausch fördert nicht nur die Qualität des Endprodukts, sondern erhöht auch das gegenseitige Verständnis und die Identifikation mit dem gemeinsamen Projektziel. Der direkte Kontakt zum Nutzer sorgt dafür, dass Teammitglieder die Auswirkungen ihrer Arbeit unmittelbar erleben. Das erhöht die intrinsische Motivation durch das Erleben von Wirksamkeit [3].

    Gemeinsam starten: damit keiner später mit dem Finger zeigt

    In einer idealen Welt verlaufen Projekte problemlos und im Rahmen des Drei- bzw. Fünfecks. Es gibt keine neuen Anforderungen, keine Notwendigkeit zu priorisieren und keine technischen Probleme. Wenn es aber, wie in den meisten größeren Projekten, zu Änderungen und Verschiebungen während der Laufzeit kommt, kann eine trennscharfe Front zwischen Kunde und IT-Dienstleister Schuldzuweisungen begünstigen: „WIR wären ja fertig geworden, wenn DIE denn alles fristgerecht geliefert hätten“. In gemischten Teams hingegen sitzen alle Projektmitglieder sprichwörtlich im gleichen Boot. Das gilt umso mehr, je stärker die Teams gemischt sind. Ein Beispiel für eine starke Mischung ist, wenn Software-Engineers sowohl vom Kunden als auch vom Dienstleister im gleichen Team agieren. In solchen Konstellationen haben beide Parteien die Möglichkeit, gestalterisch in den Projektverlauf einzugreifen. Abgesehen von diesem planerischen Aspekt entsteht auf der menschlichen Ebene ein stärkeres Wirgefühl, je verzahnter und gemischter das Team zusammengesetzt ist. Für die transparente Darstellung von Verantwortung dient die RACI-Matrix (Abb. 3). RACI steht hierbei für die Anfangsbuchstaben von Responsible (verantwortlich), Accountable (verantwortlich für das Ergebnis), Consulted (konsultiert) und Informed (informiert) [4].

    Abb. 3: Die RACI-Matrix zeigt übersichtlich, wer für welche Themen verantwortlich ist und wo es Lücken gibt

    Entscheiden statt eskalieren: warum gute Teams nicht immer fragen müssen

    In klassischen Projekten zwischen Kunde und Dienstleister verläuft vieles sequenziell. Anforderungen werden gesammelt, weitergereicht, umgesetzt – oft mit erheblicher Verzögerung zwischen Bedarf und Reaktion. Doch gerade in dynamischen IT-Projekten funktioniert dieses Modell nicht mehr. Anforderungen ändern sich – und das oft kurzfristig. Rahmenbedingungen verschieben sich. Neue Erkenntnisse tauchen auf, sobald man beginnt, das erste Feature zu implementieren. Gemeinsame Teams schaffen hier echte Flexibilität, da das gemeinsame Arbeiten auf Augenhöhe spontane Entscheidungen erlaubt. Nicht jede Frage braucht ein neues Ticket oder eine Rücksprache auf Managementebene. Oft reicht ein kurzes Gespräch im Daily, ein schneller Slack-Call oder ein gemeinsames Whiteboard. Wenn beide Seiten im Projekt tatsächlich Verantwortung übernehmen, trifft das Team Entscheidungen direkt vor Ort – schnell und fundiert. Was gestern noch Prio 1 war, kann heute bewusst zurückgestellt werden, weil etwas anderes dringender ist. Statt Blockaden entstehen damit Beweglichkeit und ein echtes Reaktionsvermögen auf neue Herausforderungen – fachlich, technisch und organisatorisch. Wichtig ist dabei: Mit „Flexibilität“ ist hier nicht „Chaos“ gemeint, sondern moderierte Beweglichkeit. Ein Mixed Team kann schneller navigieren, weil es sich nicht ständig gegenseitig erklären muss, was warum wie entschieden werden soll.

    Blinder Fleck ade: Mixed Teams sehen mehr Risiken

    Das Risikomanagement kann nach PM3 [5] in IT-Projekten nach folgendem Ablauf erfolgen:

    1. Projekt-/Risikostrategie

    2. Identifikation

    3. Analyse und Bewertung

    4. Steuerung

    5. Controlling

    Vor allem in den Punkten 2-4 ist es von Vorteil, Aspekte aus zwei verschiedenen Perspektiven zu betrachten. Das führt einerseits dazu, dass in der Identifikation, z. B. durch Methoden wie Mind Mapping oder Brainstorming, deutlich mehr Risiken aufgedeckt werden. Während der Kunde oft fachliche Risiken aufdeckt („Use Case X mit Eingangsdaten Y nicht ausführbar“), findet der IT-Dienstleister vermehrt die technischen und architektonischen Risiken eines IT-Systems (Performance, Skalierbarkeit, Stabilität, Ausfallsicherheit). Bei der darauffolgenden Bewertung von insbesondere technischen Risiken kann der IT-Dienstleister dann hilfreiche Hintergründe liefern, warum etwa der Aufbau von technischen Schulden später Probleme bereiten kann und somit mehr ist als nur ein Schönheitsfehler. Auch bei der Steuerung der Risiken bringt der IT-Dienstleister seine Erfahrungen aus anderen Projekten ein und trägt damit zu passgenauen Maßnahmen bei.

    Die Medaille der gemischten Teams hat jedoch zwei Seiten, denn den genannten Vorteilen stehen durchaus einige Herausforderungen gegenüber. Die Kunst des richtigen Teamaufbaus besteht darin, diese gut gegeneinander abzuwägen. Idealerweise kann man dabei die Vorteile ausbauen und stärken und die Herausforderungen minimieren.

    Kommunikation ohne Chaos: Wenn alle reden, aber keiner weiß mit wem

    Besteht ein Team lediglich auf einer der beiden Seiten, sind die Kommunikationsstrukturen meist sehr klar und für alle Beteiligten transparent. Die Best Practices für die Aufbauorganisation von Projekten (Projektleiter, Architektin, Software-Engineers, Scrum Master, Product Owner, …) sind bekannt und es gibt gewisse Regeln und Gepflogenheiten, an die sich alle halten. Jedes Teammitglied weiß, zu welcher Frage wer im Team angesprochen werden kann und Änderungen an der Teamstruktur sind für alle Teammitglieder gut und schnell nachvollziehbar. In gemischten Teams ist es deutlich anspruchsvoller, eine einheitliche Kommunikationsstruktur zu etablieren. Einige Beispiele dafür sind:

    • Kann etwa jeder Software-Engineer direkt mit dem Product Owner auf Kundenseite sprechen oder gibt es dafür passende Meetings?

    • Wie gelangen Anforderungen an die Software-Engineers? Ist der Product Owner SPOC (Single Point of Contact) oder sprechen Stakeholder direkt mit Software-Engineers?

    • In welchem Kreis erfolgen Sprint Planning und Sprint Reviews? Sind alle relevanten Teilnehmer eingebunden – aber auch nicht mehr?

    Die Frage, wer wann mit wem redet, ist von zentraler Bedeutung. Ein Kommunikationsplan schafft hier Klarheit. Dieser sollte direkt in der Projektinitialisierung aufgesetzt werden. Darin ist abzulesen, welche Meetings existieren, welche Ziele diese haben und wer daran teilnehmen sollte. Auch regelmäßige Reports (die monatliche Auswertung der umgesetzten Story Points an den Product Owner oder ähnliches) zählen dazu. Das Team trägt alle Informationen dort ein und sorgt damit für maximale Transparenz. Die Darstellung kann sowohl tabellarisch als auch grafisch erfolgen, in Abhängigkeit der Teamgröße und -strukturen. Bei einer grafischen Darstellung (Abb. 4) kann die Dicke der Linien die Häufigkeit, die Farbe die Art der Kommunikation und die Hintergrundfarbe die Unternehmenszugehörigkeit abbilden. Ohne solche abgestimmte Kommunikationsregeln sind Mixed Teams ineffizienter als klassische Vorgehensmodelle.

    Abb. 4: Ein grafischer Kommunikationsplan zeigt durch Verbindungslinien regelmäßige Kommunikation im Team auf

    Wenn „Sie“ auf Slack trifft: Kulturkonflikte im Projektalltag

    Firmenkulturen können viele Facetten haben: Vom klassischen „Du oder Sie?“ über den üblichen Kleidungsstil im Büro bis hin zum Umgangston. Auch in den internen Kommunikationswegen, der Art der möglichen Mitwirkung im Unternehmen, dem gegenseitigen Vertrauen und der Gehaltstransparenz kann es deutliche Unterschiede geben. Jedes Unternehmen hat seine eigene Kultur, seinen eigenen „Style“ – und das ist auch gut so. Das gleiche gilt für die Arbeitsmethoden: Was sind die im Alltag genutzten Tools? Was sind übliche Meetingabläufe von z. B. einem Scrum Daily? Was sind die Aufgaben des Scrum Masters oder Product Owners in einem agilen Team? Wie flexibel sind die Arbeitszeiten? Wie läuft die Zusammenarbeit im Team? Vielen Unternehmen ist nicht bewusst, wie breit das Spektrum an dieser Stelle ist. Oft wird angenommen, dass die eigene Unternehmenskultur der Normalfall ist. Oft ignorieren Teams unbedacht mögliche Alternativen. Wichtig wird dieses Bewusstsein allerdings, wenn man in einem gemischten Team zusammenarbeitet. Dabei geht es im ersten Schritt darum, die Bedürfnisse der Teammitglieder zu sehen und dafür Verständnis aufzubringen. Im Umkehrschluss ist es wichtig, die eigenen Bedürfnisse möglichst deutlich und präzise mitzuteilen, um Missverständnisse und falsche Annahmen zu vermeiden. Das Verständnis auf der einen und die Offenheit auf der anderen Seite ermöglichen dann idealerweise für alle Kategorien von möglichen Unterschieden Mittelwege, die von allen Beteiligten akzeptiert werden können.

    Prioritäten setzen: Aber bitte nicht aus dem Bauch heraus

    Vor allem in agilen Projekten steht die Priorisierung von Anforderungen weit oben auf der Tagesordnung. Und: In den meisten Fällen gibt es deutlich mehr User Stories im Backlog als Platz in den nächsten Sprints. Es führt somit kein Weg daran vorbei, sich immer wieder für die einen und gegen die anderen Themen zu entscheiden. Das Team sollte diese Entscheidungen aktiv und begründet treffen, da nur so das Projekt sinnvoll gesteuert werden kann. Bleiben die bewussten Entscheidungen aus, arbeitet das Team vielleicht trotzdem weiter und setzt User Stories um – der daraus generierte Mehrwert für die Stakeholder bleibt aber durch fehlende Fokussierung auf der Strecke. Auf den ersten Blick scheint Thema A klar wichtiger als Thema B. Verschiedene Erfahrungen und Hintergrundwissen in einem gemischten Team führen jedoch zu deutlich unterschiedlichen Priorisierungen. Während der IT-Dienstleister technische Details oft fundierter beurteilen kann, obliegt das dem Kunden für die Aspekte seiner Businesslogik. Bei vielen Priorisierungsentscheidungen haben beide Aspekte einen wichtigen Anteil. Für den Kunden kann es deshalb herausfordernd sein, eine ganzheitliche Entscheidung zu treffen, in der alle Perspektiven berücksichtigt werden. Aus Sicht der Projektverantwortlichen ist es deshalb besonders wichtig, die verschiedenen Perspektiven als Mehrwert und nicht als Widerspruch zu betrachten, durch die die Priorisierung ganzheitlicher und damit idealerweise besser wird.

    Erreichbarkeit ist kein Zufall: wie Teams Planbarkeit schaffen

    Flexibilität, Work-Life-Balance, Parallelprojekte – die Notwendigkeit von Koordination zwischen den Projektmitgliedern ist im Alltag ständig präsent. Insbesondere in Zeiten von Teilzeit und Remote Work ist es nicht selbstverständlich, dass Teammitglieder zwischen 9 und 17 Uhr tatsächlich zuverlässig erreichbar sind. Mittwochs eine längere Mittagspause? Donnerstags den Knirps zum Kindergarten bringen? Oder einfach aufgrund von 80 Prozent Arbeitszeit montags frei? Die transparente Pflege von Verfügbarkeiten über Firmengrenzen hinweg ist allerdings oft schwierig, da nur in seltenen Fällen beide Seiten Zugriffe auf die Kalender der jeweils anderen Seite haben. Wenn also der Scrum Master Urlaub in seinen Kalender eingetragen hat, sehen das meist nur die Teammitglieder aus dem eigenen Unternehmen.

    • Wie kann ein gemeinsamer Urlaubs- und Terminkalender gepflegt werden?

    • Wie werden reduzierte Verfügbarkeiten wie Teilzeitarbeit, parallellaufende Projekte oder Linientätigkeiten transparent gemacht?

    • Wie können spontane Abwesenheiten wie Arztbesuche, Notbetreuung im Kindergarten oder Handwerker im Haus zuverlässig im Team geteilt werden?

    Als hilfreich erwiesen hat sich die gemeinsame Nutzung des gleichen Messengers für alle Teammitglieder (MS Teams, Slack, …) und die dortige Pflege von Abwesenheiten, beispielsweise über den Status. Das kann eine doppelte Datenpflege sein (Kalendereintrag und Teams-Status), der Mehrwert allerdings ist deutlich spürbar und reduziert merklich das Risiko von Missverständnissen.

    Damit es wieder „mein Projekt“ wird: warum Identifikation kein Zufall ist

    Manche IT-Dienstleister arbeiten aufgrund vieler paralleler Projekt diese oft nur noch nach Schema F ab. Sie erscheinen zunehmend namenlos und austauschbar. Sie vermeiden Ownership zum Beispiel aus Selbstschutz. Fehlende Identifikation mit dem Projekt kann auch auf Kundenseite passieren, wenn etwa ein Product Owner für mehrere Projekte parallel zuständig ist. Die Identifikation mit dem Projekt schwindet, und damit auch die intrinsische Motivation, etwas besonders gut umzusetzen oder auch mal über den Tellerrand hinauszuschauen. Die Beteiligten verlieren den Bezug zu ihrem Projekt. Es ist nicht mehr „mein Projekt“, sondern nur noch eines von vielen. Zusammen mit anderen Teammitgliedern entsteht durch gemeinsame Ownership ein Gemeinschaftsgeist, bei dem man auch mal für andere mitdenkt und sich engagiert. Um die Identifikation mit dem Projekt zu fördern, können beispielsweise Teamrituale eingeführt werden, ein gemeinsames Projektlogo entworfen und im Allgemeinen eine Teamkultur gepflegt werden.

    Agil, aber legal: wie Mixed Teams rechtssicher funktionieren

    Die rechtlichen Rahmenbedingungen für den Einsatz von IT-Dienstleistern in agilen Teams sind vielfältig und kompliziert. Die Risiken sind vor allem: Illegale Arbeitnehmerüberlassung und Scheinselbstständigkeit von Freelancern. Der Kunde muss strenge Regeln definieren und konsequent umsetzen, um das Eintreten dieser Risiken zu verhindern. Das beginnt bei Rollenbeschreibungen im Vertrag und endet bei klarer Trennung von Steuerungsverantwortung und operativer Zuweisung im Teamalltag.

    Vergleichsweise einfach und unverfänglich ist es, Leiharbeiter in gemischten Teams einzusetzen. Sie unterliegen dem Weisungsrecht des Kunden und können damit nahtlos in eine Projektstruktur integriert werden. Problematisch könnte hier die maximale Überlassungsdauer von 18 Monaten (AÜG) sein, die für viele Projekte nicht ausreichend ist. Der Einsatz von Mitarbeitenden von IT-Dienstleistern und Freelancern hingegen birgt die Gefahr, dass abhängige Beschäftigungsverhältnisse entstehen. Um das zu vermeiden, sind explizite Weisungen an das externe Personal sowie dessen unmittelbare Einbindung in die Organisationsstruktur des Kunden zu unterlassen. Diese Einschränkung stellt die Zusammenarbeit vor spürbare Herausforderungen, da insbesondere die enge Interaktion im Team ein Erfolgsfaktor von agilen Projekten ist [6].

    Die nachfolgend beschriebenen Ansätze zur Bewältigung dieser Einschränkungen sind von der jeweiligen rechtlichen Detailsituation abhängig. Sie stellen ggf. nur eine Auswahl der notwendigen Maßnahmen dar und sollten deshalb vor dem Einsatz spezifisch für das betreffende Projekt rechtlich bewertet werden:

    • Explizite Benennung als „externe Unterstützung“, zum Beispiel durch den Zusatz „Extern“ in der E-Mail-Adresse oder im Benutzernamen im Messenger.

    • Pflege der extern umzusetzenden Arbeiten in einer separaten Liste, zum Beispiel in einem eigenen Jira-Projekt/-Board

    • Idealerweise werden nicht einzelne User Stories, sondern ganze Epics an den IT-Dienstleister zur Umsetzung übergeben. Das ermöglicht das selbstständige Arbeiten ohne viele einzelne Anweisungen.

    Stay tuned

    Regelmäßig News zur Konferenz und der JAX-Community erhalten

    [mc4wp-simple-turnstile]

     

    Fazit

    Was ist also die bessere Wahl für ein IT-Projekt: ein klassisches Projekt-Set-up oder ein gemischtes Team? Wie zuvor dargestellt, birgt ein „Mixed Team“ durchaus Herausforderungen, die ein Projekt ins Wanken bringen können, wenn sie nicht aktiv adressiert werden. Demgegenüber stehen jedoch klare Vorteile: Direktere Kommunikation, kürzere Feedbackzyklen und eine höhere Identifikation aller Beteiligten mit dem Projektziel. Diese Effekte lassen sich in rein klassischen Set-ups oft nur schwer erzielen. Vorausgesetzt, das Projekt eignet sich grundsätzlich für ein gemischtes Team – etwa, weil keine strengen Vorgaben zur Geheimhaltung oder Compliance entgegenstehen – und das Management ist bereit, sich den damit verbundenen Herausforderungen konstruktiv zu stellen, kann das „Mixed“-Modell einen erheblichen Mehrwert für alle Beteiligten bieten.

    Mixed Teams sind wahrlich keine Selbstläufer. Wer sie aber bewusst gestaltet, kann damit nicht nur Projekte erfolgreicher machen, sondern auch die Zusammenarbeit auf ein neues Level heben. Wer Mixed Teams einsetzen will, sollte frühzeitig klären: Wie kommunizieren wir? Wer priorisiert? Wer gibt Orientierung im Alltag?


    Links & Literatur

    [1] techconsult GmbH: „Digitalisierung und Softwareinvestition: Worauf es ab 2025 für DACH-Unternehmen ankommt“

    [2] Van Knippenberg, D.; De Dreu, C. K. W.; Homan, A. C. : „Work group diversity and group performance: An integrative model and research agenda“; in: Journal of Applied Psychology 89(6), 2004

    [3] Ryan, R. M.; Deci, E. L.: „Self-determination theory and the facilitation of intrinsic motivation, social development, and well-being“; in: American Psychologist 55(1), 2000

    [4] Project Management Institute (PMI): „A Guide to the Project Management Body of Knowledge“; 2021

    [5] GPM Deutsche Gesellschaft für Projektmanagement: „Kompetenzbasiertes Projektmanagement (PM3): Handbuch für die Projektarbeit, Qualifizierung und Zertifizierung auf Basis der IPMA Competence Baseline Version 3.0“; 2014

    [6] Haufe Online Redaktion. (o. D.). Einsatz von Fremdpersonal im Rahmen der agilen Zusammenarbeit. Abgerufen am 10. Juni 2025, von https://www.haufe.de/id/beitrag/einsatz-von-fremdpersonal-im-rahmen-der-agilen-zusammenarbeit-HI13675551.html

    The post Mixed Teams in IT-Projekten: Vorteile, Herausforderungen & Best Practices appeared first on JAX.

    ]]>
    Warum KI-Agenten nativ auf der JVM entstehen müssen – Rod Johnsons Vision für die Zukunft von Java https://jax.de/blog/ki-agenten-jvm-rod-johnson-wjax-2025/ Tue, 18 Nov 2025 16:03:36 +0000 https://jax.de/?p=107973 KI-Agenten markieren den nächsten großen Schritt in der Softwarearchitektur – und laut Rod Johnson wird ihre Zukunft auf der JVM entschieden. In seiner W-JAX-2025-Keynote zeigt der Spring-Erfinder, warum Java die ideale Grundlage für stabile, testbare und integrierbare Agentensysteme bietet und wieso Unternehmen jetzt auf native JVM-Ansätze setzen sollten, um im KI-Zeitalter konkurrenzfähig zu bleiben.

    The post Warum KI-Agenten nativ auf der JVM entstehen müssen – Rod Johnsons Vision für die Zukunft von Java appeared first on JAX.

    ]]>
    KI-Agenten markieren den nächsten großen Paradigmenwechsel in der Softwarearchitektur – und Java-Entwickler spielen dabei eine entscheidende Rolle. Auf der W-JAX 2025 in München zeigte Rod Johnson, Erfinder des Spring Frameworks, warum KI-Agenten weit über APIs und Orchestrierung hinausgehen und Systeme ermöglichen, die selbstständig planen, handeln und sich anpassen. Doch während die Zukunft rasant auf KI-native Plattformen zusteuert, arbeiten viele Java-Teams noch mit Tools und Konzepten außerhalb der JVM – und damit am Kern vorbei.

    Stay tuned

    Regelmäßig News zur Konferenz und der JAX-Community erhalten

    [mc4wp-simple-turnstile]

     

    In seiner Keynote „AI Agents and the Future of Java: Why It’s Time to Build Natively“ erläutert Johnson, warum der Einstieg in diese neue Ära nicht über fremde Ökosysteme führen sollte, sondern direkt auf der JVM stattfinden muss. Java bietet dafür eine außergewöhnlich starke Basis: bestehende Unternehmensmodelle, statische Typisierung und tief integrierte Domain-Logik – alles Elemente, die KI-Agenten stabiler, testbarer und produktionsreifer machen können als der heute verbreitete Ansatz über Python-Frameworks oder lose orchestrierte Workflows.

    Mit seinem selbst entwickelten Framework Embabel zeigt Johnson zudem, wie Java-Teams komplexe, deterministische Agenten-Workflows direkt auf der JVM implementieren können, die Enterprise-Anforderungen an Integration, Skalierbarkeit und Zuverlässigkeit erfüllen.

    Warum also jetzt handeln? Johnson macht deutlich: Wer früh native Agenten in Java entwickelt, prägt die Softwarearchitektur der Zukunft – wer zu lange wartet, riskiert den Anschluss an KI-native Plattformen zu verlieren.

    DIE KUNST DER SOTWARE-ARCHITEKTUR

    Architecture & Design-Track entdecken

     

    Im folgenden Video können Sie die komplette Keynote erleben – inklusive Live-Demos, konkreter Architekturbeispiele und einer klaren Roadmap, wie Java-Teams den Weg in die KI-getriebene Softwarewelt strategisch meistern können.

    Drei zentrale Take-aways der Keynote von Rod Johnson

    1. Unternehmensreife KI-Agenten entstehen auf der JVM – nicht als schneller Python-Prototyp.
      Während viele KI-Experimente in der Forschung oder im Startup-Umfeld auf Python basieren, brauchen Enterprise-Lösungen andere Eigenschaften: Stabilität, Typensicherheit, Wartbarkeit und Integration in bestehende Systeme. Genau hier liefert Java — und erlaubt, KI-Agenten nicht nur zu prototypisieren, sondern produktionsreif in Unternehmensarchitekturen zu verankern.
    2. Domänenwissen ist der Schlüssel zur erfolgreichen Gen-AI-Integration – und hier hat Java einen Vorteil.
      Viele Gen-AI-Initiativen scheitern an Halluzinationen, fehlender Vorhersagbarkeit und mangelnder Systemintegration. Durch starke Typisierung und fest verankerte Domain-Modelle bietet Java eine ideale Grundlage, LLMs mit realem und strukturiertem Unternehmenskontext zu verbinden.
    1. Deterministische, testbare Agentensysteme machen Java im KI-Zeitalter konkurrenzfähig.
      Frameworks wie Embabel ermöglichen agentische Systeme auf Basis deterministischer Planung (Goal-Oriented Action Planning) statt unvorhersehbarer Workflows. Damit können Unternehmen KI-Agenten entwickeln, die stabil, integrierbar und produktionsreif sind – und innovative Lösungen bieten, die nicht bloß bestehende Python-Ansätze kopieren.

    The post Warum KI-Agenten nativ auf der JVM entstehen müssen – Rod Johnsons Vision für die Zukunft von Java appeared first on JAX.

    ]]>
    LLMs in der Softwarearchitektur: Werkzeug statt Ersatz https://jax.de/blog/llm-softwarearchitektur-grenzen-einsatz/ Tue, 07 Oct 2025 08:11:44 +0000 https://jax.de/?p=107786 LLMs (Large Language Models) scheinen die Softwareentwicklung zu revolutionieren – welchen Einfluss werden sie auf Softwarearchitektur haben? Zunächst: Über das Generieren von Code mit LLMs ist schon viel geschrieben worden. Das ist nicht Thema dieses Artikels, hier soll es um den Einsatz für Softwarearchitektur gehen. Das ist ein ganz anderes Einsatzfeld.

    The post LLMs in der Softwarearchitektur: Werkzeug statt Ersatz appeared first on JAX.

    ]]>
    LLM erstellt Architektur?

    LLMs sind insbesondere dafür geeignet, Texte zu generieren. Ein wesentlicher Teil der Architekturarbeit scheint das Erstellen von Texten zu sein: Architekturdokumentationen oder -konzepte gehören dazu. Und auch Diagramme können aus textuellen Repräsentationen generiert werden. Das scheint also ein Bereich zu sein, den LLMs autonom abdecken können. Und tatsächlich kann man ein LLM dazu veranlassen, eine Architektur in diesem Sinne zu erzeugen.

    Kann das, was dabei entsteht, aber eine sinnvolle Architektur sein? Um dieser Frage nachzugehen, ist ein Fall interessant [2], bei dem ein LLM ein vorhandenes System analysieren sollte. Ergebnis: Das LLM hat einige Aspekte korrekt wiedergegeben, aber die Information, dass das System Apache ActiveMQ und Apache Artemis nutzt, war schlicht falsch. ActiveMQ und Artemis sind zwei Messagingsysteme. Solche Technologien sind eigentlich nur sinnvoll, wenn das System asynchron kommunizieren soll. Damit liegt die Analyse auf technologischer Ebene daneben, und sie erzeugt auch einen falschen Eindruck von der Architektur, denn so würde man wahrscheinlich von einer asynchronen Architektur ausgehen. Zudem ist Artemis eine Art Nachfolger von ActiveMQ. Das macht die Analyse nahezu grotesk, weil sie behauptet, dass zwei sehr ähnliche Technologien genutzt werden, eine davon eine modernere Variante der anderen. So etwas wäre nur dann zu erklären, wenn von der einen zur anderen Technologie migriert wird.

    Würde ein Mensch bei einem Architekturreview eine so fehlerhafte Analyse erstellen, würde man vermutlich dem Rest der Analyse ebenfalls keinen Glauben schenken und die Person nie wieder mit einer solchen Aufgabe beauftragen. Beim LLM waren die Reaktion deutlich anders, es überwog eher ein Erstaunen. Warum sind die Konsequenzen hier anders? Es liegt nahe, grundsätzlich infrage zu stellen, ob LLMs für solche Architekturaufgaben überhaupt geeignet sind.

    Halluzinationen

    In dem konkreten Beispiel hat das LLM also im Text eine Information – nämlich die Nutzung von Artemis und ActiveMQ – ausgegeben, obwohl diese Information in der Wissensbasis nicht vorhanden war. Der Fachbegriff für solche Fehler sind „Halluzinationen“. Es gibt sie auch in anderen Variationen. So gibt es ein Beispiel, bei dem ein LLM Code erzeugen sollte und im Rahmen eines Tests die Produktionsdatenbank gelöscht hat, obwohl das explizit verboten war. Hier hat das LLM also eine explizite und wichtige Anweisung missachtet.

    Solche Halluzinationen bedeuten, dass LLMs Fake-Informationen produzieren und bei Aktionen, die sie auslösen, unzuverlässig sind. Wenn man Architekturarbeit vollständig an LLMs delegieren will, ohne sie anschließend manuell zu überprüfen, müssten solche Halluzinationen praktisch ausgeschlossen sein.

    Zuverlässige LLMs?

    Damit kommt den Halluzinationen eine entscheidende Bedeutung zu, wenn es um die Bewertung der Nutzbarkeit von LLMs für die Softwarearchitektur geht. Halluzinationen sind ein Phänomen, das beim Entwurf von LLMs bewusst in Kauf genommen wird. LLMs sollen in erster Linie gute Dialoge mit Menschen führen können. Dafür sind ansprechende und überzeugende Texte wichtig, die Korrektheit ist sekundär. Das ist bei der Kommunikation zwischen Menschen nicht sehr viel anders: Wenn man in einem Gespräch jede Aussage validieren würde, käme der Dialog zum Erliegen. Bei einem wissenschaftlichen Paper ist die Korrektheit hingegen sehr wichtig; dafür dauert das Erstellen auch viel länger als eine Antwort in einem Dialog, weil jede Aussage geprüft wird und durch einen Prozess wie eine Peer Review auch noch andere Menschen die Aussagen validieren.

    Letztendlich sind LLMs reine Textgeneratoren. Sie haben kein Konzept einer Wahrheit oder von Fakten, sondern generieren nur Text. Aufgrund der Trainingsdaten haben die Texte eine gewisse Wahrscheinlichkeit, korrekt zu sein, wenn in den Trainingsdaten nur korrekte Texte enthalten sind. Aber logische Denkprozesse gibt es bei LLMs nicht.

    DIE KUNST DER SOTWARE-ARCHITEKTUR

    Architecture & Design-Track entdecken

     

    Man kann die Ergebnisse von LLMs nicht als Lügen klassifizieren. Lügen würden bedeuten, dass das LLM ein Konzept von Wahrheit hat und bewusst Texte erstellt, die dieser Wahrheit widersprechen. LLMs haben aber ein solches Konzept von Wahrheit nicht. Noch dazu sollte eine Lüge einer Absicht folgen. Wenn man beispielsweise behauptet, man hätte viel mehr Geld als man tatsächlich hat, stellt man eine Behauptung auf, die der Wahrheit widerspricht. Typischerweise verfolgt man damit ein Ziel, beispielsweise einen Menschen zu beeindrucken. Wegen dieser beiden Umstände ist es eine Lüge.

    Anders ist es bei Bullshit: Da trifft man Aussagen ohne Rücksicht auf den Wahrheitsgehalt. Wer bei einer Prüfung beispielsweise den Stoff nicht beherrscht, kann irgendwelche Aussagen treffen. Ob sie wahr sind oder nicht, muss einem egal sein, weil man den Stoff nicht beherrscht. Entscheidend ist, die Prüfung zu bestehen, und wenn man ausreichend kompetent wirkt und keinen zu offensichtlichen Unsinn erzählt, kann das vielleicht gut gehen.
    LLMs produzieren in diesem Sinne Bullshit, weil sie manchmal Fake-Informationen (Halluzinationen) produzieren, aber weder ein Konzept von Wahrheit haben noch eine Intention und daher nicht lügen [5]. Die Wahrheit ist ihnen noch nicht einmal egal, weil sie kein Bewusstsein haben, dass solche Entscheidungen treffen könnte, und keine Wissensbasis mit einer Wahrheit.

    Damit ist sehr gut zu erklären, wie die ActiveMQ/Artemis-Fehlinformation zustande gekommen ist: Es ist schlicht ein generierter Text, der plausibel klingt, aber ohne Rücksicht auf Wahrheit erzeugt wurde.

    Kritisch hinterfragen

    Also muss man die Ergebnisse eines LLMs kritisch hinterfragen. Dann trifft man aber selbst die Architekturentscheidungen und nicht das LLM. Das ist auch sinnvoll, denn am Ende wird man wohl kaum die Verantwortung für ein Architekturproblem auf ein LLM abschieben können. Und selbst wenn das geht: Es ist auf jeden Fall besser, die Probleme frühzeitig zu erkennen und zu lösen.

    Leider hat unsere Branche aber beim kritischen Hinterfragen und dem Treffen eigener Entscheidungen oft Schwächen. Deshalb ist es nicht ungewöhnlich, dass eine Architektur so entworfen wird, „weil man das eben heute so macht“ oder „weil das eine bekannte Person auf einer Konferenz so erläutert hat“. Natürlich ist es denkbar, dass sich in Zukunft die Begründung „weil das LLM es so entschieden hat“ dazu gesellt. Es ist sogar praktisch, weil man so Verantwortung abschieben kann. Dennoch sollten Softwarearchitekt:innen anders arbeiten und selbst Verantwortung übernehmen.

    Text ist nicht Architektur

    Wichtig dabei: Das LLM generiert genau genommen nur einen Text und keine Architektur. Das wirkt zunächst wie Haarspalterei. Aber tatsächlich kann man eine umfangreiche Architekturdokumentation schreiben, die alle möglichen Dinge erläutert, aber eben nicht die Hauptrisiken und die wichtigsten Entscheidungen definiert. Wenn man eine große Menge Text erzeugt, kann das sogar eher dazu führen, dass wichtige Entscheidungen irgendwo in dieser Textwüste begraben sind. Aber attraktiv ist es dennoch: Schließlich werden ja irgendwo in dieser Textmenge sicher alle Probleme diskutiert und gelöst sein.

    Die Realität ist jedoch, dass solche umfangreichen Architekturdokumentationen existieren, die dennoch nichts über die wesentlichen Probleme aussagen, und erst bei einer Review – idealerweise mit einem unverstellten Blick von außen – fällt das auf. Das passt zum Phänomen des „Architekturtheaters“ [9], bei dem ein komplexer, bürokratischer Architekturprozess aufgesetzt wird, der am Ende aber die eigentlichen Herausforderungen und Themen nicht identifiziert. Insofern ist es nachvollziehbar, dass das Generieren von Text, wie es LLMs ermöglichen, wie Architekturarbeit wirkt. Sicher kann mit einem LLM das Generieren einer Architekturdokumentation als Scheinlösung umgesetzt werden – und sie kann sehr umfangreich sein und sehr überzeugend wirken. Eine sinnvolle Architektur stellt der Text dennoch nicht dar.

    Worauf kommt es an?

    Hilft ein solcher Text dann überhaupt? Wenn man Menschen danach fragt, was der wichtigste Skill in der IT ist, kommt mit einer überwältigenden Mehrheit als Antwort „Kommunikation“ oder etwas anderes im Bereich Social Skills [9]. Ähnliches gilt bei der Frage nach der Hauptherausforderung im Bereich Softwarearchitektur [1]. Auch dort geben Menschen typischerweise Themen wie Kommunikation, Organisation oder das Vermitteln zwischen Business und Technik an.

    Aus diesen Gründen ist es so sinnvoll, Softwareentwicklung als soziotechnischen Prozess zu begreifen – also als einen Prozess, der sowohl eine technische Seite als auch eine soziale Seite hat. Dieser Ansatz hat sich gerade in letzter Zeit als vorteilhaft für Architekturarbeit gezeigt und stand auch wiederholt im Zentrum dieser Kolumne. Ansätze wie kollaborative Modellierung beispielsweise mit Event Storming unterstützen die notwendige Kollaboration beim Erstellen der Artefakte, haben also eine soziale Komponente. Nicht nur die Artefakte selbst, sondern auch der soziale Prozess, mit dem diese erstellt werden, wird zunehmend als wichtig erkannt, weil dabei Rollen und Zusammenspiel der Menschen deutlich wird. Und in diesem Bereich existieren oft die größten Herausforderungen.

    Es wirkt wenig wahrscheinlich, dass man soziotechnische Probleme wie Softwarearchitektur dadurch lösen kann, dass man mit einem LLM allein vor dem Rechner einen Text erzeugt. In der Praxis besteht eine wichtige Aufgabe von Architekt:innen darin, mit Stakeholdern über Ziele, Ausrichtung und Randbedingungen zu diskutieren und technische Entwürfe mit Techniker:innen zu besprechen. Ein Projekt kann nur erfolgreich sein, wenn alle Stakeholder abgeholt sind und sich abgeholt fühlen. Und schließlich sind die Anforderungen bzw. Qualitäten zentral – egal ob funktional oder nichtfunktional. Wenn man diese Rahmenbedingungen nicht geklärt hat, baut man schlichtweg das Falsche. Die Techniker:innen müssen ebenfalls überzeugt sein, weil sie sonst die Entwürfe unterlaufen, und durch die Diskussion mit ihnen ergeben sich oft noch bessere technische Alternativen.

    Wie also LLMs nutzen?

    Aber man kann natürlich ein LLM Architekturentwürfe generieren lassen – wenn man es ethisch verantworten kann. LLMs sind schließlich sehr energieintensiv und Menschen müssen sie unter fragwürdigen Arbeitsbedingungen trainieren. Und die Entwürfe müssen kritisch hinterfragt und abgestimmt werden. Ob dadurch die Architekturarbeit einfacher wird, ist fraglich: Denn am Ende muss man einen Architekturentwurf kritisch prüfen und abstimmen, der ohne ein Konzept von Wahrheit entstanden ist und dazu noch überzeugend präsentiert wird.

    Natürlich ist es denkbar, mit einem LLM statt einer ganzen Architekturdokumentation Texte zu bestimmten technischen Fragestellungen oder zu einzelnen architekturellen Entscheidungen zu generieren – so wie man sonst auch im Web nach solchen Informationen sucht. Man bekommt dann auf der einen Seite maßgeschneiderte Aussagen, die aber auf der anderen Seite Fake-Informationen bzw. Halluzinationen sein können. Oft ist dieser Ansatz aber tatsächlich der schnellere und einfachere Weg zum Ergebnis.

    Am Ende liegen aber Verantwortung und Entscheidungsgewalt bei beiden Vorgehensweisen bei der Architekt:in. Das kritische Hinterfragen und Einarbeiten der Ergebnisse des LLMs ist ihre Aufgabe. Sie ist dann auch verantwortlich und nicht das LLM – so wie eben der Autor einer Webseite oder eines Buchs auch nicht dafür verantwortlich sein kann, was mit seinen generellen Aussagen in einem spezifischen Projekt geschieht.

    Damit sind LLMs ein weiteres Werkzeug im Architekturwerkzeugkoffer. Der ist mit einer Vielzahl anderer Werkzeuge gefüllt – dazu zählen kollaborative Modellierung, Domain-driven Design und Architekturdokumentationsansätze wie arc42. Es erscheint unwahrscheinlich, dass LLMs die Produktivität von Architekt:innen massiv erhöhen oder sie überflüssig machen, wie das andere Werkzeuge auch nicht geschafft haben. Aber es gibt wahrscheinlich Einsatzmöglichkeiten wie für alle anderen Werkzeuge auch.

    Fazit

    Es ist zunächst wichtig zu verstehen, dass die Ergebnisse von LLMs prinzipienbedingt unzuverlässig sind, weil sie Halluzinationen bzw. Fake-Informationen erzeugen können. Daher muss man die Ergebnisse von LLMs immer kritisch hinterfragen – auch und gerade, weil sie so überzeugend sind. Und LLMs lösen nicht das zentrale Problem der Softwareentwicklung, das vor allem im Bereich Kommunikation und sozialen Faktoren liegt. Dass das die wesentlichen Probleme sind, ist der breiten Mehrheit klar [1],[10]. Warum dann LLMs massive Vorteile bringen sollen, bleibt offen – aber sie sind sicher ein interessantes Werkzeug, das man prinzipiell auch in der Softwarearchitektur einsetzen kann, wenn man es ethisch vertreten kann.

    Stay tuned

    Regelmäßig News zur Konferenz und der JAX-Community erhalten

    [mc4wp-simple-turnstile]

     



    Links & Literatur

    [1] Softwarearchitektur im Stream: „Was ist die Hauptherausforderung der Software-Architektur?“: https://software-architektur.tv/2025/06/27/folge269.html

    [2] Mahler, Dirk: https://www.linkedin.com/posts/dirk-mahler-837a4b5_ai-neo4j-claude-activity-7321596753223340033-xz4r

    [3] https://x.com/jasonlk/status/1946065483653910889

    [4] https://arxiv.org/html/2202.03629v7

    [5] Softwarearchitektur im Stream: „KI = Bullshit“: https://software-architektur.tv/2025/04/11/episode260.html

    [6] https://winfuture.de/news,150778.html

    [7] https://www.nature.com/articles/s41562-025-02194-6

    [8] Softwarearchitektur im Stream: „Besteht ChatGPT die iSAQB-Advanced-Level-Prüfung? 2/2 mit Ralf D. Müller“: https://software-architektur.tv/2024/01/19/folge197.html

    [9] https://mastodon.social/@kevlin/112003129757159797

    [10] Softwarearchitektur im Stream: „Was ist der wichtigste Skill in der IT?“: https://software-architektur.tv/2024/08/16/episode228.html

    The post LLMs in der Softwarearchitektur: Werkzeug statt Ersatz appeared first on JAX.

    ]]>
    Java 25: Neue Features im Überblick https://jax.de/blog/java-25-neuerungen-features-ueberblick/ Tue, 09 Sep 2025 13:00:25 +0000 https://jax.de/?p=107749 Wie jedes halbe Jahr kam im September mit dem OpenJDK 25 die nächste Java-Version heraus. Für sie wird es von vielen Anbietern wieder einen verlängerten Support (LTS) geben.

    The post Java 25: Neue Features im Überblick appeared first on JAX.

    ]]>
    Die kontinuierliche Weiterentwicklung von Java zeigt sich in der Vielzahl von Neuerungen, die sowohl die Sprache als auch die Plattform und das Laufzeitsystem betreffen. Mit diesem Release setzt sich der Trend fort, Java durch gezielte Verbesserungen moderner, effizienter und leistungsfähiger zu machen. Neben Änderungen an der Sprache gibt es auch Optimierungen in der JVM, neue APIs sowie diverse produktivitätssteigernde Fortschritte für Entwicklerinnen und Entwickler.

    Stay tuned

    Regelmäßig News zur Konferenz und der JAX-Community erhalten

    [mc4wp-simple-turnstile]

     

    Bei genauerer Betrachtung fallen zunächst viele Themen ins Auge, die bereits in früheren Versionen enthalten waren. Einige der Wiedervorlagen stehen mit Java 25 nun in einer finalen Fassung bereit. Dazu zählen die JDK Enhancement Proposals (JEPs) 506 (Scoped Values), 511 (Module Import Declarations), 512 (Compact Source Files and Instance Main Methods) und 513 (Flexible Constructor Bodies). Insgesamt wurden die folgenden 18 JEPs umgesetzt [1]:

    • 470: PEM Encodings of Cryptographic Objects (Preview)
    • 502: Stable Values (Preview)
    • 503: Remove the 32-bit x86 Port
    • 505: Structured Concurrency (Fifth Preview)
    • 506: Scoped Values
    • 507: Primitive Types in Patterns, instanceof, and switch (Third Preview)
    • 508: Vector API (Tenth Incubator)
    • 509: JFR CPU-Time Profiling (Experimental)
    • 510: Key Derivation Function API
    • 511: Module Import Declarations
    • 512: Compact Source Files and Instance Main Methods
    • 513: Flexible Constructor Bodies
    • 514: Ahead-of-Time Command-Line Ergonomics
    • 515: Ahead-of-Time Method Profiling
    • 518: JFR Cooperative Sampling
    • 519: Compact Object Headers
    • 520: JFR Method Timing & Tracing
    • 521: Generational Shenandoah

    Starten wir zunächst mit dem Langläufer der letzten Jahre, dem Vector API. Es ist nun schon das zehnte Mal als Inkubator enthalten und taucht seit Java 16 regelmäßig in den Releases auf. Es geht dabei um die Unterstützung der modernen Möglichkeiten von SIMD-Rechnerarchitekturen mit Vektorprozessoren. Single Instruction, Multiple Data (SIMD) lässt viele Prozessoren gleichzeitig unterschiedliche Daten verarbeiten. Durch die Parallelisierung auf Hardwareebene verringert sich beim SIMD-Prinzip der Aufwand für rechenintensive Schleifen.

    Der Grund für die lange Inkubationsphase des Vector API wird in den Zielen des JEP 508 [2] erklärt: „Alignment with Project Valhalla – The long-term goal of the Vector API is to leverage Project Valhalla’s enhancements to the Java object model. Primarily this will mean changing the Vector API’s current value-based classes to be value classes so that programs can work with value objects, i.e., class instances that lack object identity.“

    Man wartet also auf die Reformen am Typsystem. Bei Java ist es aktuell zweigeteilt in primitive und Referenztypen (Klassen). Die primitiven Datentypen wurden ursprünglich aus Gründen der Performanceoptimierung eingeführt, haben aber im Handling entscheidende Nachteile. Referenztypen sind auch nicht immer die beste Wahl, insbesondere was die Effizienz und den Speicherverbrauch angeht. Es braucht etwas dazwischen, was sich so schlank und performant wie primitive Datentypen verhält, aber auch die Vorzüge von selbst zu erstellenden Referenztypen in Form von Klassen kennt. Schon bald könnten daher aus dem Inkubatorprojekt Valhalla die Value Types (haben keine Identität) und Universal Generics (List<int>) ins JDK übernommen werden. Das JEP 401 (Value Classes and Objects) hat es leider wieder nicht geschafft, aber vielleicht sehen wir es ja dann im OpenJDK 26. Dementsprechend werden wir das Vector API wohl auch noch einige Releases lang als Inkubator- bzw. dann hoffentlich bald als Preview-Feature wiedersehen.

    Deferred Immutability

    Das JEP 502 führt mit „Stable Values“ Objekte ein, die genau einmal gesetzt werden und danach unveränderlich sind. Die JVM behandelt ihren Inhalt wie eine echte Konstante und kann dieselben Optimierungen (z. B. Constant Folding) anwenden wie bei final‑Feldern, sie bieten aber eine höhere Flexibilität in Bezug auf den Initialisierungszeitpunkt. Damit bekommt Java eine „Deferred Immutability“, die Start‑ und Aufwärmzeiten verkürzt, ohne Thread‑Sicherheits-Risiken einzugehen.

    Die Ziele sind ein schnellerer Start (keine monolithische Initialisierung aller Komponenten mehr), Entkopplung von Erzeugung und Initialisierung eines unveränderlichen Werts ohne nennenswerte Performanceeinbußen, garantierte At‑most‑once‑Initialisierung auch in hoch parallelem Code sowie das Zugänglichmachen der Konstantenoptimierungen für Anwendungscode (analog zu JDK‑internem Code).

    Stable Values sind ein reines Library API, es wird kein neues Schlüsselwort geben. Außerdem gibt es keine Änderung an der final‑Semantik, bestehender Code bleibt unberührt. Bisher müssen final‑Felder eager (sofort) initialisiert werden – die Initialisierung eines Loggers oder eine Datenbank‑Connection bremst so den Programmstart aus. Workarounds wie Lazy Holder, Double‑Checked Locking oder ConcurrentHashMap.computeIfAbsent sind entweder eingeschränkt, fehleranfällig oder verhindern JIT‑Optimierungen (Just in Time). Stable Values schließen die Lücke zwischen strenger Immutability und flexibler Lazy Initialization.

    In Listing 1 wird der Lambdaausdruck in orElseSet garantiert genau einmal ausgeführt, selbst wenn mehrere Virtual Threads gleichzeitig logger() aufrufen. Danach kann der JIT alle Zugriffe optimieren (z. B. mit Constant Folding).

    class OrderController {
      private final StableValue<Logger> logger = StableValue.of();
    
      private Logger logger() {
        return logger.orElseSet(() -> Logger.create(OrderController.class));
      }
    }
    

    Das API von Stable Values zeigt Tabelle 1. Stable Values kombinieren Lazy und Fast Initialization. Risiken bestehen praktisch nur dort, wo Reflection weiterhin final-Felder ändern darf. Aber das ist bereits heute eine generelle JVM-Beschränkung, und Stable Values stellen somit eine sichere Variante dar, diese Beschränkung zu umgehen. Sie passen hervorragend zu Virtual Threads (Loom) und Structured Concurrency (JEP 505). Theoretisch können nun Millionen Threads ohne teure Synchronisationstricks auf „Deferred Constants“ zugreifen.

    Methode Zweck Thread-sicher? JIT-optimierbar?
    StableValue.of() leere Hülle erzeugen
    orElseSet(Supplier) Inhalt holen oder einmalig setzen
    StableValue.supplier(Supplier) kombiniert StableValue + Supplier
    StableValue.list(int, IntFunction) Pool/Liste lazily erzeugen

    Tabelle 1: API von Stable Values

    Primitive Type Patterns

    Pattern Matching ist nun auch schon einige Jahre in der Entwicklung. Hier wurden immer wieder Teile abgeschlossen, zuletzt in Java 22 die „Unnamed Variables & Patterns“ (JEP 456). Bei den nun in der dritten Preview befindlichen „Primitive Types in Patterns, instanceof, and switch“ (JEP 507) geht es um eine Erweiterung, sodass primitive Datentypen wie int, byte und double in allen Pattern-Kontexten (beim instanceof und im switch) verwendet werden dürfen. Entwickler haben dadurch weniger Limitierungen sowie Sonderfälle und können primitive und Referenzdatentypen auch im Kontext von Type Patterns oder als Komponenten in Record Patterns austauschbar verwenden. Seit der letzten Preview gibt es keine Änderungen, die JDK-Entwickler wollen aber weiteres Feedback sammeln, wie es heißt.

    SIE LIEBEN JAVA?

    Den Core-Java-Track entdecken

     

    Beim Pattern Matching geht es darum, bestehende Strukturen mit Mustern abzugleichen, um komplizierte Fallunterscheidungen effizient und wartbar implementieren zu können. Ein Pattern ist dabei eine Kombination aus einem Prädikat (das auf die Zielstruktur passt) und einer Menge von Variablen innerhalb dieses Musters. Diesen Variablen werden bei passenden Treffern die entsprechenden Inhalte zugewiesen und damit extrahiert. Die Intention des Pattern Matching ist die Destrukturierung von Datenobjekten, also das Aufspalten in die Bestandteile und das Zuweisen in einzelne Variablen zur weiteren Bearbeitung. Mit instanceof und switch können wir also überprüfen, ob ein Objekt von einem bestimmten Typ ist, und wenn ja, dieses Objekt einer Variable dieses Typs zuweisen und diese Variable in dem folgenden Programmpfad benutzen. Das funktionierte bisher nur mit Objekten und ließ sich nicht mit primitiven Datentypen kombinieren. Einzig im switch ließen sich bereits Variablen der primitiven Typen byte, short, char sowie int gegen Konstanten matchen und konnten sogar mit den neueren Type Patterns kombiniert werden (Listing 2).

    int grade = 7;
    String result = switch (grade) {
      case 1, 2 -> "very good or good";
      case 3, 4 -> "satisfactory or sufficient";
      case 5, 6 -> "poor or deficient";
      case Integer i -> "Undefined grade: " + i;
    };
    System.out.println(result);
    

    JEP 507 verbessert nun Typprüfung, Performance und Lesbarkeit in Java und macht Pattern Matching konsistenter, indem es primitive Typen direkt unterstützt. Das reduziert überflüssiges Autoboxing und vereinfacht den Umgang mit primitiven Datentypen in switch-Anweisungen. Wie am Beispiel in Listing 3 zu sehen ist, können Entwickler in Zukunft ganz einfach prüfen, ob ein ganzzahliger Wert in den Wertebereich eines bytes passt.

    private static String checkByte(int value) {
      if (value instanceof byte b) {
        return "byte b = " + b;
      } else {
        return "kein byte: " + value;
      }
    }
    
    System.out.println(checkByte(127)); // b = 127
    System.out.println(checkByte(128)); // kein byte: 128
    

    Leichtere Entwicklung mit Java – für Einsteiger und Profis

    Trotz seiner 30 Jahre zieht Java auch weiterhin viele Programmieranfänger an. Die JEPs 511 (Module Import Declarations) und 512 (Compact Source Files and Instance Main Methods) helfen aber nicht nur Neulingen, sie erleichtern auch erfahrenen Entwicklern das Leben. Beide JEPs werden nun nach mehreren vorangegangenen Previews finalisiert.

    Durch die „Module Import Declarations“ lassen sich alle exportierten Packages eines Moduls auf einmal importieren. Das reduziert Import-Boilerplate und vereinfacht die Wiederverwendung modularer Bibliotheken (ohne dass der eigene Code selbst modularisiert sein muss). Das ist praktisch fürs Prototyping und für klare, kurze Quelltexte. In der Finalisierung hat sich nichts mehr geändert, aber bei der letzten Preview gab es zwei Erweiterungen. Einerseits wurden Beschränkungen bei den transitiven Abhängigkeiten des Moduls java.se (eine Art Aggregator-Modul ohne eigene Packages/Klassen) zu java.base aufgehoben. Dadurch kann man nun mit dem Import dieses einen Moduls das gesamte API von Java SE importieren. Außerdem ist es jetzt möglich, dass sogenannte Type-Import-on-demand-Deklarationen (z. B. import java.util.*) vorherige Modul-Import-Deklarationen überdecken. Wenn beispielsweise die Module java.base und java.sql importiert werden, gibt es eine Unklarheit beim Verwenden der Klasse Date – es gibt sie als java.util.Date und als java.sql.Date. Durch die On-demand-Deklaration import java.util.* wird in dem Fall java.util.Date verwendet.

    Zum JEP 512 (Compact Source Files and Instance Main Methods) gab es bereits vier Previews und der Name hat sich immer mal wieder geändert. Es werden kompakte Ein-Datei-Programme mit einer Instanz-main-Methode ermöglicht, sodass kleine Tools, Skripte oder Lernbeispiele ohne unnötige „Zeremonie“ startklar sind. Das führt zu einem schnelleren Einstieg für Anfänger und weniger Hürden für erfahrene Entwickler beim Skizzieren, Ausprobieren und Automatisieren. In der Finalisierung gab es noch einmal drei kleine Änderungen:

    • Die Klasse IO liegt nun im Package java.lang und wird damit immer automatisch importiert.
    • Die statischen Methoden von IO werden nicht mehr implizit in Compact Source Files importiert, Aufrufe müssen qualifiziert (z. B. IO.println(“Hello, world!”)) oder explizit importiert werden.
    • Die Implementierung von IO stützt sich jetzt auf System.out/System.in statt auf java.io.Console.

    Unter Beibehaltung der bestehenden Java Toolchain und nicht mit der Absicht, einen separaten Dialekt für Java einzuführen, lassen sich in Compact Source Files mit der vereinfachten main-Methode (Klassendeklaration entfällt …) sehr simple, skriptartige Programme erstellen. Dazu kommt die leichtere Verwendung von Standardein- und -ausgabe durch die neuen statischen Methoden print(), println() und readln() der Klasse java.io.IO. Ein Beispiel zeigt der folgende Code:

    // > java Main.java
    void main() {
      IO.println("Hello, World!");
    }

    Neues aus dem Umfeld von Virtual Threads

    Für die in Java 21 finalisierten virtuellen Threads wurden zwei weitere APIs eingeführt: „Structured Concurrency“ und „Scoped Values“. Sie ermöglichen eine effizientere, sichere und besser strukturierte Nebenläufigkeit.

    Scoped Values werden nun mit den JEP 506 finalisiert. Ein ScopedValue<T> ist eine Alternative zu ThreadLocal<T>, die speziell für Virtual Threads optimiert wurde. Er ermöglicht es, unveränderbare Werte sicher über einen Codebereich hinweg zu propagieren, ohne dabei die Nachteile von ThreadLocal zu übernehmen. Diese sind bei Virtual Threads problematisch, da Speicherplatz potenziell nicht automatisch freigegeben wird. Da Virtual Threads sehr leichtgewichtig sind und potenziell tausende parallele Aufgaben ausgeführt werden können, würden ThreadLocal-Variablen schlecht skalieren. Denn jeder Thread würde seinen eigenen Wert speichern. Bei Scoped Values sind die Inhalte nur innerhalb eines bestimmten Bereichs gültig und verursachen dadurch keine Speicherprobleme. Scoped Values sind sicherer, performanter und expliziter in ihrer Lebensdauer. Durch ihre Unveränderbarkeit können keine ungewollten Nebenwirkungen auftreten. Listing 4 zeigt ein Beispiel, bei dem USER_ID nur innerhalb des run()-Blocks gültig ist und dann automatisch aufgeräumt wird.

    public class ScopedValueExample {
    private static final ScopedValue<String> USER_ID = ScopedValue.newInstance();
    
      public static void main(String[] args) {
        ScopedValue.where(USER_ID, "User-123").run(() -> {
          processRequest();
        });
      }
    
      static void processRequest() {
        System.out.println("Processing for user: " + USER_ID.get()); // Gibt "User-123" aus
      }
    }
    

    Im Vergleich zur letzten Preview gibt es in JEP 506 nur noch eine kleine Änderung: Die Methode ScopedValue.orElse() akzeptiert keine null-Argumente mehr.

    Stay tuned

    Regelmäßig News zur Konferenz und der JAX-Community erhalten

    [mc4wp-simple-turnstile]

     

    Die Structured Concurrency (JEP 505) sorgt dafür, dass nebenläufige Tasks innerhalb eines klar definierten Scopes gestartet und beendet werden. Das hilft bei der Fehlerbehandlung (wenn eine Aufgabe fehlschlägt, werden alle anderen koordiniert abgebrochen) und bei der Lesbarkeit bzw. Wartbarkeit (explizite Nebenläufigkeitsstrukturen werden sichtbar). Alternativ konnten Entwickler für diesen Zweck bisher die Parallel Streams, den ExecutorService oder reaktive Programmierung einsetzen. Alles sehr mächtige Ansätze, die aber einfache Umsetzungen unnötig kompliziert und fehleranfällig machen. Structured Concurrency behandelt Gruppen zusammengehöriger Aufgaben als eine Arbeitseinheit, wodurch die Fehlerbehandlung sowie das Abbrechen der Aufgaben vereinfacht und die Zuverlässigkeit sowie die Beobachtbarkeit erhöht werden.

    Listing 5 zeigt ein Beispiel. Beide Subtasks laufen parallel (typisch als Virtual Threads) los. Wenn einer fehlschlägt, bricht der Scope ab und unterbricht den anderen Task automatisch, join() wirft dann eine FailedException. Waren beide erfolgreich, sind nach join() beide Ergebnisse verfügbar.

    // --enable-preview beim Kompilieren und Starten 
    record Response(String user, int order) {}
    
    Response handle() throws InterruptedException {
      try (var scope = StructuredTaskScope.<Object, Void>open()) {
        var user  = scope.fork(this::findUser);   // liefert String
        var order = scope.fork(this::fetchOrder); // liefert Integer
    
        scope.join(); // wartet auf alle; wirft eine Exception bei einem Fehler und cancelt die anderen
    
        return new Response((String) user.get(), (Integer) order.get());
      }
    }
    
    // Platzhalter:
    String findUser()   { return "alice"; }
    int    fetchOrder() { return 42; }
    

    Gegenüber der letzten Preview hat sich noch einmal einiges an dem API verändert:

    • Statische Fabrikmethoden zum Öffnen eines Scopes (StructuredTaskScope.open(…)) statt öffentlicher Konstruktoren
    • Die Standardvariante ist open() ohne Parameter: Der Scope gilt als erfolgreich, wenn alle Subtasks erfolgreich sind; schlägt einer fehl, wird der Scope abgebrochen (Short Circuit)
    • Joiner-Konzept: Über open(Joiner…) definierst du alternative Abschluss-Policies (z. B. „erstes erfolgreiches Ergebnis“ oder „sammle alle Ergebnisse“), und join() liefert dann direkt das passende Ergebnis
    • join() wirft jetzt selbst Exceptions (z. B. FailedException, TimeoutException) und liefert ggf. einen Rückgabewert; das frühere Pattern join().throwIfFailed() entfällt
    • Es gibt eine optionale Konfiguration beim Öffnen (open(joiner, cfg -> cfg.withTimeout(…).withThreadFactory(…))) für Timeouts, ThreadFactory und Scope-Name – hilfreich für Monitoring und Tuning
    • Observability wurde erweitert: Thread-Dumps (JSON) zeigen die Task-/Subtask-Bäume inkl. Scope-Beziehungen

    Listing 6 zeigt einige Beispiele für alternative TaskScope-Strategien.

    // Erstes erfolgreiches Ergebnis gewinnt
    StructuredTaskScope.open(
      Joiner.<T>anySuccessfulResultOrThrow(),
      cfg -> cfg.withTimeout(java.time.Duration.ofSeconds(2)))
    
    // Alle oder keiner
    StructuredTaskScope.open(Joiner.<T>allSuccessfulOrThrow())
    
    // Erfolgreiche und fehlgeschlagene Tasks unterscheiden
    StructuredTaskScope.open(Joiner.<T>awaitAll())
    
    // Früher Abbruch per Prädikat
    StructuredTaskScope.open(
      Joiner.<String>allUntil(st ->
        st.state() == Subtask.State.SUCCESS && st.get().contains("OK")))
    

    Flexible Konstruktorinhalte werden finalisiert

    Dank des JEP 513 (Flexible Constructor Bodies) dürfen in Konstruktoren Anweisungen nun bereits vor einem expliziten weiteren Konstruktoraufruf (super() oder this()) im sogenannten Prolog erscheinen. Die Anweisungen im Prolog unterliegen einigen Einschränkungen, es ist insbesondere keine Verwendung von this erlaubt. Dazu zählen weder das Lesen von Feldern noch das Aufrufen von Methoden. Nicht erlaubt sind z. B. this.hashCode(), System.out.println(this), var x = this.feld (lesender Zugriff). Es gibt genau eine Ausnahme, einfache Zuweisungen auf eigene Felder ohne Initializer sind erlaubt (z. B. this.feld = …). Diese Zuweisungen dürfen aber nicht innerhalb von Lambdas oder inneren Klassen im Prolog stehen.

    Im Prolog können jedoch Parameter validiert bzw. transformiert werden. Außerdem kann im Konstruktor der Oberklasse auf bereits initialisierte Felder der Subklasse zugegriffen werden. Listing 7 zeigt ein solches Beispiel, in dem nicht nur Felder validiert oder transformiert werden (Name aufsplitten), sondern auch in der Superklasse bereits auf die Werte von Feldern der Subklasse zugegriffen wird, obwohl der Subklassenkonstruktor noch nicht beendet ist (Aufruf von show() im Konstruktor Person).

    void main() {
      new Employee("Duke Java", 42, "C-7");
    }
    
    
    class Person {
      int age;
      String firstname, lastname;
    
      void show() {
        IO.println(this);
      }
    
      Person(String name, int age) {
        // Prolog
        String[] names = name.split("\\s" );
        this.firstname = names[0];
        this.lastname = names[1];
        if (age < 0) { // fail-fast im Prolog
    
          throw new IllegalArgumentException("age must be greater than or equal to zero");
        }
        this.age = age;
        super();
        // Epilog
        show(); // ruft ggf. überschriebenes show() in Subklasse auf!
      }
    
      @Override
      public String toString() {
        return String.format("Name=%s %s; Age=%d", firstname, lastname, age);
      }
    }
    
    
    class Employee extends Person {
      String officeId; // kein Initializer → im Prolog setzbar
    
      Employee(String name, int age, String officeId) {
        // Prolog
        this.officeId = Objects.requireNonNull(officeId); // Prolog: Feld der eigenen Klasse setzen
        super(name, age);  // erst jetzt Superkonstruktor
        // Epilog: weiterer Code erlaubt, Objekt ist konsistent
      }
    
      @Override
      public String toString() {
        return super.toString() + "; OfficeId=" + officeId;
      }
    }
    

    Das macht viele Konstruktoren natürlicher (z. B. Fail-fast-Validierung) und erhöht die Sicherheit, weil Felder eines Subtyps fertig initialisiert sein können, bevor Superklassenkonstruktoren (ggf. via überschreibbare Methoden) darauf zugreifen. Insgesamt erhöht sich sowohl die Lesbarkeit als auch die Performance, da unnötige Aufrufe bei der Validierung oder Weiterverarbeitung von Konstruktorparametern vermieden werden. Dieses Feature wird nun finalisiert, im Vergleich zur letzten Preview gab es keine Änderungen.

     

    Was sonst noch geschah

    Neben diesen prominenten und auch für viele Entwickler relevanten Neuerungen gibt es auch wieder einige kleine Änderungen. So wurden mit dem JEP 503 der Quellcode und Build-Support für den 32-Bit-x86-Port (nach Deprecation in JDK 24 via JEP 501) dauerhaft entfernt. Ziel ist es, neue Features nicht länger mit 32-Bit-Fallbacks bedienen zu müssen, alle 32-Bit-x86-Sonderpfade zu streichen und Build/Test-Infrastruktur zu vereinfachen. Nicht betroffen sind der 32-Bit-Support anderer Architekturen und frühere JDK-Releases. Der Pflegeaufwand stand in keinem Verhältnis zum Nutzen. Die Entfernung beschleunigt die Weiterentwicklung z. B. bei Loom, FFM API, Vector API, GC-Barrier-Expansion usw.

    Schneller Start und Warm-up

    Das JEP 514 (Ahead-of-Time Command-Line Ergonomics) wird das Erzeugen von Ahead-of-Time-Caches (AOT) vereinfachen. Diese können den Start von Java-Anwendungen deutlich beschleunigen. Für gängige Fälle soll jetzt ein einziger Schritt genügen, der Trainingslauf und Cacheerstellung kombiniert. Ziel ist es, die bislang nötige Zwei-Phasen-Prozedur abzulösen – ohne an Ausdrucksstärke zu verlieren und ohne neue AOT-Optimierungen einzuführen. Heute braucht man zwei java-Aufrufe und bleibt mit einer temporären *.aotconf-Datei zurück; künftig entfällt dieser Ballast, was u. a. Frameworks wie JRuby bei eigenen Trainingsläufen entgegenkommt. Für Spezialfälle bleiben die expliziten AOT-Modi und Konfigurationsoptionen aber weiterhin verfügbar.

    Mit dem JEP 515 (Ahead-of-Time Method Profiling) wird es eine schnellere Warm-up-Phase durch Profile aus dem AOT-Cache geben. Die JVM kann beim Start Methodenausführungsprofile aus einem früheren Lauf sofort laden, sodass der JIT direkt die voraussichtlich relevanten Methoden kompiliert, statt zunächst Profile sammeln zu müssen. Die Profile werden in einem Trainings-Run erzeugt und über den bestehenden AOT-Cache bereitgestellt. Dafür sind keine Codeänderungen und keine neuen Workflows nötig.

    Der Hintergrund ist, dass das Warm-up heute viel Zeit kostet, weil die HotSpot VM erst während der Produktion herausfindet, welche Methoden relevant sind. Durch das Verlagern des Profilings in einen Trainings-Run, erreicht die Anwendung in Produktion schneller ihre Spitzenleistung. Ein Web-Service, der sonst erst nach einigen Minuten voll performant ist, kann mit vorab aufgezeichneten Profilen schon beim Start die kritischen Request-Pfade kompilieren und so unter Last sofort schneller reagieren.

    Verbesserungen bei Beobachtbarkeit und Profiling

    Der JDK Flight Recorder wird mit dem experimentellen JEP 509 (JFR CPU-Time Profiling) so erweitert, dass er auf Linux den CPU-Zeit-Timer des Kernels nutzt und damit präzisere CPU-Profile erstellt – auch dann, wenn Java-Code gerade native Bibliotheken ausführt. Bisher stützte sich JFR (JDK Flight Recorder) vor allem auf einen „Execution Sampler“, der in festen Echtzeitintervallen (z. B. alle 20 ms) Java-Stacks zieht, dabei aber Threads in nativem Code übersieht, Proben verpassen kann und nur einen Teil der Threads erfasst. Mit CPU-Zeit-Profiling bildet JFR die tatsächlich verbrauchten CPU-Zyklen ab und vermeidet unsichere interne Schnittstellen, wie sie manche externe Tools verwenden. Ein Sortieralgorithmus verbringt zum Beispiel seine gesamte Laufzeit auf der CPU und taucht entsprechend stark im CPU-Profil auf, eine Methode, die meist auf Daten aus einem Socket wartet, beansprucht hingegen kaum CPU-Zeit und erscheint dadurch nur selten im Profil.
    Durch das JEP 518 (JFR Cooperative Sampling) soll der JFR stabiler werden, indem er Java-Thread-Stacks nur noch an Safepoints traversiert und dabei den bekannten Safepoint-Bias so weit wie möglich reduziert. Bisher nahm der JFR in festen Intervallen (z. B. alle 20 ms) asynchron Samples auch außerhalb von Safepoints. Das erforderte Heuristiken zum Stack-Parsing, die ineffizient waren und im Fehlerfall sogar JVM-Crashes auslösen konnten (etwa bei gleichzeitigem Class Unloading). Künftig wird auch die Profilerstellung der Wanduhrzeit (Wall-Clock Time) weiter unterstützt, aber mit einem robusteren Verfahren, das Genauigkeit und Ausfallsicherheit besser ausbalanciert.

    Das JEP 520 (JFR Method Timing & Tracing) erweitert den JDK Flight Recorder um eine Bytecodeinstrumentierung, die jeden ausgewählten Methodenaufruf exakt misst und mit Stacktrace aufzeichnet – im Gegensatz zu stichprobenbasierten Profilen. Methoden lassen sich ohne Codeänderungen zielgenau per Kommandozeile, Konfigurationsdatei, jcmd oder JMX auswählen. Nicht vorgesehen sind das Aufzeichnen von Argumenten/Feldwerten, das Tracen nicht bytecodierter Methoden (z. B. native, abstract) sowie das gleichzeitige Instrumentieren sehr vieler Methoden. In solchen Fällen bleibt Sampling der richtige Ansatz. Um zum Beispiel lange Startzeiten zu analysieren, können statische Initialisierer ausgewählter Klassen gezielt getracet werden. So wird sichtbar, welche Initialisierung sich auf später verschieben lässt oder wo ein jüngst eingespielter Fix tatsächlich Laufzeit spart.

    Geringerer Speicherverbrauch und effizientere Garbage Collection

    Mit dem JEP 519 werden die Compact Object Headers finalisiert. Sie werden jedoch nicht zum Standardlayout erklärt. Seit ihrer Einführung im JDK 24 (JEP 450) haben sie sich in Stabilität und Performance bewährt, unter anderem in Hunderten Amazon Services (teils auf JDK 21/17 zurückportiert). Experimente zeigen deutliche Vorteile: bis zu 22 Prozent weniger Heap, 8 Prozent weniger CPU-Zeit, 15 Prozent weniger GCs (G1/Parallel) und ein hoch paralleler JSON-Parser läuft 10 Prozent schneller. Weniger Overhead pro Objekt verbessert die Speichernutzung und Cachelokalität, was spürbar dem Start-up und Durchsatz zugutekommt.

    Seit einiger Zeit gibt es neben dem Standard Garbage Collector (G1) auch sogenannte Low Latency GCs wie ZGC und Shenandoah. Sie sind auf moderne Hardwarearchitekturen (Multi-Core-Prozessoren und Terrabyte an RAM) ausgelegt und haben trotz großer Speichermengen kürzere GC-Pausen als Ziel. Mit dem JEP 521 (Generational Shenandoah) wird der generationale Modus des Shenandoah GC (eingeführt als Experiment in JDK 24 via JEP 404) finalisiert. Der Standard bleibt unverändert: Per Default nutzt Shenandoah weiterhin eine Generation, kann aber auf Wunsch auch zwischen Old und Young Generation unterscheiden. Zum Aktivieren des generationalen Modus ist –XX:+UnlockExperimentalVMOptions jetzt nicht mehr nötig. Alle übrigen Optionen und Defaults bleiben gleich, bestehende Startskripte funktionieren weiter. Es gab noch zahlreiche Stabilitäts- und Performanceverbesserungen sowie umfangreiche Tests (u. a. DaCapo, SPECjbb2015, SPECjvm2008, Heapothesys). Anwender berichten von erfolgreichen Einsätzen unter Last.

    Erweiterungen beim Kryptografie-Support

    Bereits in Java 24 gab es einige JEPs, die Implementierungen von Algorithmen für Schlüsselaustauschverfahren und digitale Signaturen eingeführt haben, um sicher vor zukünftigen Angriffen durch Quantencomputer zu sein. Mit dem JEP 470 (PEM Encodings of Cryptographic Objects) wird nun ein einfaches API eingeführt, um kryptografische Objekte – Schlüssel, Zertifikate und Sperrlisten – in das weit verbreitete PEM-Textformat (RFC 7468) zu kodieren und daraus wieder Objekte zu dekodieren. Es unterstützt die Standardrepräsentationen PKCS#8 (Private Keys), X.509 (Public Keys, Zertifikate, CRLs) sowie PKCS#8 v2.0 (verschlüsselte Private Keys und asymmetrische Schlüssel). Bisher fehlte in Java ein komfortables PEM API, Entwickler mussten Base64, Header/Footer-Parsing, Factory-Auswahl und Algorithmuserkennung selbst erledigen. Das neue Preview-API reduziert diesen Boilerplate deutlich.

    Mit dem JEP 510 wird das „Key Derivation Function API“ finalisiert. Es ist unverändert gegenüber der Einführung als Preview in JDK 24. Es handelt sich um ein API für Key Derivation Functions (KDFs), etwa HKDF (RFC 5869) und Argon2 (RFC 9106). Es ermöglicht den Einsatz von KDFs in KEM/HPKE-Szenarien (z. B. ML-KEM, Hybrid Key Exchange in TLS 1.3), erlaubt PKCS#11-basierte Implementierungen und räumt auf, indem JDK-Komponenten wie TLS 1.3 und DHKEM auf das neue API statt auf interne HKDF-Logik umgestellt werden. Aus einem gemeinsamen Geheimnis (z. B. ECDH Shared Secret) und einem Salt kann per HKDF deterministisch ein Satz Sitzungsschlüssel für Verschlüsselung und MAC abgeleitet werden. PBKDF1/2 wandern nicht in das neue API, sie bleiben wie bisher über SecretKeyFactory nutzbar.

    Stay tuned

    Regelmäßig News zur Konferenz und der JAX-Community erhalten

    [mc4wp-simple-turnstile]

     

    Fazit und Ausblick

    Auch Java 25 ist wieder ein spannendes Release mit einigen abgeschlossenen und vielen neuen Features. Auf den ersten Blick ist für uns Entwickler zwar wenig wirklich Neues dabei – vieles sind Wiedervorlagen aus früheren Preview-Versionen. Aber genau das zeigt, wie stabil und durchdacht sich Java weiterentwickelt. Unter der Haube passiert außerdem enorm viel: von Performanceoptimierungen über Sicherheitsverbesserungen bis hin zu Weichenstellungen für die Zukunft, etwa in der Kryptografie und der Speicherverwaltung. Java bleibt damit eine moderne, leistungsfähige Plattform und Sprache. Alle weiteren kleineren Neuerungen, für die es keine JEPs gibt, können in den Release-Notes [3] nachgelesen werden. Änderungen am JDK (Java-Klassenbibliothek) kann man sich zudem sehr schön über den Java Almanac [4] anzeigen lassen.

    Ideen für die nächsten Funktionen gehen den JDK-Entwicklern nicht aus. Wer sich vorab über mögliche zukünftige Themen informieren möchte, kann sich schon mal im JEP-Index unter „Draft and submitted JEPs“ [5] umschauen. Besonders interessant sind die „Null-Restricted and Nullable Types“ [6]. Dadurch können wir ähnlich wie bei Kotlin Markierungen an Java-Typen festlegen, ob null-Werte erlaubt sind oder vom Compiler abgewiesen werden sollen. In verschiedenen Vorträgen, insbesondere des Java-Language-Architekten Brian Goetz, verdichten sich zudem die Anzeichen, dass sie beim Projekt Valhalla kurz vor dem finalen Durchbruch stehen. Dann könnten die Value Types also bald Wirklichkeit werden und eine neue Ära in der Programmierung mit Java einläuten.

    Im März 2026 wird mit dem OpenJDK 26 schon die nächste Version erscheinen. Zum Zeitpunkt der Erstellung dieses Artikels ist bisher nur ein JEP eingeplant und zwar die endgültige Entfernung des Applet API (JEP 504 [7]). Hier sieht man die Bereitschaft, alte Zöpfe abzuschneiden. Und auch wenn man Applets in modernen Browsern schon länger nicht mehr nutzen konnte, wird der ein oder andere Java-Veteran diese Entwicklung auch mit einem weinenden Auge beobachten.

    Und noch ein Fun Fact zum Schluss: Wenn Oracle im nächsten Jahr auf einen jährlichen Zyklus wechselte, dann ließen sich die nächsten Versionsnummern immer an der Jahreszahl ihrer Erscheinung ablesen (27 in 2027 …). Aber diesen Gefallen werden sie uns vermutlich nicht tun, dafür funktioniert das halbjährliche Releasemodell einfach zu gut. Eine Alternative wäre dann höchstens noch, dass man 26.a und 26.b herausbringt. Aber das würde das bisherige Versionierungsschema kaputt machen. Genießen wir also die zwei Jahre, in denen die Versionsnummer der Jahreszahl entspricht. Und ab Herbst 2026 müssen wir dann wieder herumrechnen, welches denn eigentlich die aktuelle Java-Version ist.

    Frequently Asked Questions (FAQ)

    1. Was zeichnet Java 25 als Release aus?

    Java 25 ist ein Long-Term-Support (LTS)-Release mit 18 realisierten JDK Enhancement Proposals (JEPs).

    2. Welche JEPs von Java 25 wurden finalisiert, die zuvor als Preview existierten?

    Finalisiert wurden unter anderem Scoped Values (JEP 506), Module Import Declarations (JEP 511), Compact Source Files & Instance‑Main (JEP 512) und Flexible Constructor Bodies (JEP 513).

    3. Wofür dienen „Stable Values“ in Java 25?

    Stable Values (JEP 502) ermöglichen eine “deferred immutability” – also eine verzögerte, threadsichere Initialisierung, die mehr Flexibilität als final‑Felder bietet und gleichzeitig Start‑ und Aufwärmzeiten verkürzt.

    4. Welche Verbesserungen bringt Java 25 für die Nebenläufigkeit?

    Java 25 finalisiert Scoped Values (JEP 506) und bietet strukturierte Nebenläufigkeit über Structured Concurrency in der fünften Preview (JEP 505), was fehler­anfällige Executor‑Setups ersetzt.

    5. Wie optimiert Java 25 Speicherverbrauch und Performance durch JEPs?

    Compact Object Headers (JEP 519) reduzieren Objekt‑Header von 96 auf 64 Bit, was sowohl Heap‑Nutzung als auch CPU‑Last und Garbage‑Collection‑Zyklen deutlich verringert.

    6. Welche Profiling‑ und Observability‑Verbesserungen enthält Java 25?

    Die JDK Flight Recorder‑Erweiterungen umfassen CPU-Time Profiling (JEP 509), Cooperative Sampling (JEP 518) und Method Timing & Tracing (JEP 520) für präzisere und robustere Profilerstellung.

    7. Welche kryptografischen Features sind neu in Java 25?

    Java 25 führt Preview‑Support für PEM Encodings of Cryptographic Objects (JEP 470) sowie die finale Key Derivation Function API (JEP 510) für moderne Kryptoszenarien ein.

    8. Was sind weitere relevante Änderungen in Java 25?

    Weitere wichtige Neuerungen umfassen die Entfernung des 32‑Bit‑x86‑Ports (JEP 503), AOT‑Verbesserungen (JEP 514 & JEP 515) und die finale Generational‑Shenandoah‑GC‑Unterstützung (JEP 521) sowie weitere APIs wie Vector API (JEP 508) und primitive patterns (JEP 507).


    Links & Literatur

    [1] https://openjdk.java.net/projects/jdk/25/

    [2] https://openjdk.org/jeps/508

    [3] https://jdk.java.net/25/release-notes

    [4] https://javaalmanac.io/jdk/25/apidiff/24/

    [5] https://openjdk.org/jeps/0#Draft-and-submitted-JEPs

    [6] https://bugs.openjdk.org/browse/JDK-8303099

    [7] https://openjdk.org/jeps/504?v=Dhn-JgZaBWoa/2024/03/19/announcing-javaone-2025/

    The post Java 25: Neue Features im Überblick appeared first on JAX.

    ]]>
    Microservices vs. Monolithen: Architektur-Strategien nach dem Hype https://jax.de/blog/microservices-architektur-nach-dem-hype-trends/ Wed, 27 Aug 2025 15:12:07 +0000 https://jax.de/?p=107721 Zehn Jahre Microservices führen zu den Fragen, ob es langsam an der Zeit für einen neuen Hype ist und ob von dem um Microservices noch etwas übrig ist. Schließlich wissen wir doch mittlerweile, dass Microservices viel zu komplex sind. Wie sollen wir also jetzt Architekturen aufbauen?

    The post Microservices vs. Monolithen: Architektur-Strategien nach dem Hype appeared first on JAX.

    ]]>
    Aber wenn nicht Microservices – was dann? Die wesentliche Alternative zu einem Microservices-System ist ein Monolith. Genau genommen gibt es verschiedene Arten von Monolithen (Abb. 1):

    • Ein Deployment-Monolith kann technisch nur als Ganzes deployt werden. Gerade ältere Enterprise-Java-Systeme zählen zu dieser Kategorie.
    • Bei einem Architekturmonolithen gibt es hingegen keine vernünftige Strukturierung des Systems in einzelne Module. Oder die Abhängigkeiten zwischen den Modulen sind so stark ausgeprägt, dass man einen Teil des Systems weder isoliert verstehen noch ändern kann. Eine andere Bezeichnung für diesen Architekturansatz ist „Big Ball of Mud“ [1]. Es gibt gute Indizien, dass er einer der populärsten Architekturansätze ist, weil man mit diesem Ansatz zumindest kurzfristig schnell Software entwickeln kann.

    Stay tuned

    Regelmäßig News zur Konferenz und der JAX-Community erhalten

    [mc4wp-simple-turnstile]

     

    Spring AI [4], das kürzlich das MCP integriert hat, bietet Entwicklerinnen und Entwicklern nun auf Java-Basis eine komfortable Möglichkeit, MCP-Server direkt in Spring-Boot-Anwendungen zu konsumieren und eigene MCP-Server zu implementieren.

    Abb. 1: Microservices und die verschiedenen Spielarten von Monolithen

    Es geht es also um zwei unterschiedliche Eigenschaften des Systems: Das Deployment und die Änderbarkeit. Ein gemeinsames Deployment kann sinnvoll sein, weil dazu eine weniger komplexe Infrastruktur notwendig ist – kein Kubernetes, nur eine Deployment Pipeline, einfachere Anforderungen bei Observability. Die Wahl eines Deployment-Monolithen kann daher eine sinnvolle Architekturentscheidung sein. Ein Architekturmonolith ist hingegen das Ergebnis, wenn man die Strukturierung der Software beispielsweise zugunsten von Investitionen in Features vernachlässigt.

    Warum eigentlich Microservices?

    Microservices sind ein Gegenentwurf zu Deployment-Monolithen. Typischerweise teilt man das System beispielsweise in einzelne Docker-Container auf, die eine Schnittstellen für ihre Funktionalitäten mit Technologien wie REST im Netz anbieten. So kann man technisch gesehen jeden dieser Microservices unabhängig voneinander deployen. Um tatsächlich unabhängig deployen zu können, muss es getrennte Deployment-Pipelines geben. Das ist nur möglich, wenn es beispielsweise auch unabhängige Tests gibt. So wird der Deployment-Prozess eines Deployment-Monolithen, der oft groß und komplex ist, durch viele kleine Deployment-Prozesse ersetzt, die einfacher sein sollten.

    Das getrennte Deployment ist nicht der einzige Vorteil von Microservices. Es gibt weitere:

    • Microservices können auch getrennt skaliert werden. Man kann mehrere Instanzen jedes einzelnen Microservice starten und so jeden Microservice an die individuelle Last anpassen.
    • Der Absturz eines Prozesses führt nur zum Ausfall eines Microservice, die anderen laufen weiter. Bei einer geschickten Aufteilung des Systems bleiben die meisten Funktionalitäten erhalten. Microservices können also dabei helfen, die Resilience (Widerstandsfähigkeit) eines Systems zu verbessern.
    • Auch zugunsten der Sicherheit kann die Aufteilung in Docker-Container Vorteile bringen. Zwischen den Containern können beispielsweise Firewalls eingezogen werden, sodass ein Angreifer mehr Schwierigkeiten hat, Kontrolle über größere Teile des Systems zu erlangen.
    • Schließlich können unterschiedliche Microservices auf verschiedener Hardware laufen. o kann ein Teil des Systems, der beispielsweise GPUs benötigt, auf entsprechender Hochleistungshardware betrieben werden, während der restliche Systemteil auf weniger leistungsfähiger und damit kostengünstiger Hardware läuft.

    Ein Argument für Microservices war, dass sie das Durchhalten einer Aufteilung einfacher machen. Man kann nicht einfach irgendeinen Bestandteil wie eine einzelne Klasse eines anderen Microservice nutzen, sondern nur die Schnittstelle, die beispielsweise über REST angeboten wird. In einem Deployment-Monolithen ist es hingegen leicht, auf irgendeine Klasse irgendwo im System zuzugreifen. Wenn das zunächst nicht geht, kann man auch schnell die Sichtbarkeit auf public umstellen – und schon kann man eine neue Abhängigkeit einbauen. So gehen die ursprünglich geplanten Strukturen verloren und am Ende steht ein Architekturmonolith. Microservices sollten also dieses Problem der Architekturmonolithen lösen, so die Hoffnung.

    ALL ABOUT MICROSERVICES

    Microservices-Track entdecken

     

    Microservices = bessere Strukturierung?

    In der Realität sind aber „verteilte Monolithen“ (Distributed Monoliths) entstanden. Sie sind Architekturmonolithen, weil sie eine schlechte Strukturierung des Systems aufweisen. Die schlechte Struktur hat bei Microservices-Systemen die Auswirkung, dass Änderungen mehrere Microservices umfassen und die Microservices auch sehr viel miteinander kommunizieren. Durch die starken Abhängigkeiten ist das System schwer modifizierbar und hat eine schlechte Performance. Dafür kann es verschiedene Gründe geben:

    • Der schon erwähnte Architekturmonolith bzw. Big Ball of Mud ist populär, weil man zunächst sehr einfach und schnell zu Ergebnissen kommt. Erst später tauchen die Probleme auf. So kann man mit einem Microservices-System sehr schnell bei einem verteilten Monolithen enden. Dazu kommt, dass in einem Microservices-System zwar die Änderungen in einem Microservice vielleicht einfacher sind – wenn man aber die Struktur des Gesamtsystems ändern will, muss dazu Code zwischen Docker-Containern verschoben werden. Das ist selbst dann aufwendig, wenn der Code aller Docker-Container mit derselben Programmiersprache geschrieben worden ist.
    • Wenn man einen Microservice so definiert, das er einfach und schnell ersetzbar sein soll, endet man bei sehr vielen Microservices. So ergibt sich leicht ein unübersichtliches System mit starken Abhängigkeiten. Die Priorisierung der Ersetzbarkeit gegenüber einer guten Strukturierung des Gesamtsystems ist eigentlich kaum sinnvoll. Die Nachteile der schlechten Struktur wiegen die Vorteile der besseren Ersetzbarkeit einzelner Microservices vermutlich mehr als auf. Bei solchen Entscheidungen kann auch eine Rolle spielen, Microservices ‚richtig‘ umzusetzen – also im Sinne gängiger Definitionen. Manche davon betonen beispielsweise die Austauschbarkeit einzelner Services als zentrale Eigenschaft. Allerdings ist das bloße Einhalten einer Definition wenig zielführend, wenn dadurch eine insgesamt schlechte Architektur entsteht.

    Tatsächlich entstehen in einigen Situationen sogar Systeme mit hunderten Microservices. Das macht den Betrieb noch komplizierter und das System wird unübersichtlich, was es noch schwieriger macht, das System zu verstehen und zu ändern. Die Unübersichtlichkeit ist eigentlich erstaunlich: Ein System mit Hunderten von Packages ist nicht zwingend chaotisch. Wenn die Fachlichkeit eine solche Menge an Code erfordert, dann gibt es Wege, das System änderbar und verstehbar zu machen. Packages können in Packages zusammengefasst werden. Und natürlich kann das System in mehrere Maven-Projekte oder JAR-Dateien aufgeteilt werden. So ergeben sich grobgranulare Module, von denen es wenigere gibt, sodass das System verstehbar und änderbar wird. Durch eine solche Hierarchisierung kann man also komplizierte Systeme dennoch verstehen und entwickeln.

    Wenn also ein Microservices-System zu viele Microservices enthält und daher unübersichtlich ist, ist die Reduktion der Anzahl nur eine Option. Für das Verständnis kann eine Hierarchie hilfreich sein – beispielsweise Cluster von Microservices, zum Beispiel durch eine Namenskonvention. Eine andere höhere hierarchische Ebene sind Teams: Sie sind für mehrere Microservices zuständig und haben in gewisser Weise auch eine Schnittstelle, nämlich alle Funktionalitäten, die sie anderen Teams anbieten. Wenn man die Schnittstelle eines Clusters oder der Microservices eines Teams beispielsweise an einem API Gateway als konsolidierte Schnittstelle nach außen anbietet, wird sich eine solche grobgranulare Strukturierung auch in der umgesetzten Architektur erkennbar machen und die Nutzung der Funktionalität vereinfachen.

    Da Microservices über das Netz kommunizieren, kann eine große Zahl an Microservices auch zu Netzwerk-Overhead und schlechter Performance führen. Das Problem kann nur gelöst werden, indem die Anzahl der Microservices reduziert und die Aufteilung so angepasst wird, dass fachliche Verantwortung idealerweise in genau einem Microservice implementiert sind.

    Bessere Struktur – aber wie?

    Zu komplexe Abhängigkeiten von Microservices und zu viele Microservices zeigen dennoch deutlich: Microservices führen nicht immer zu einer besseren Strukturierung von Systemen. Das sollte eigentlich nicht überraschen. Sie sind nur ein Ansatz, um die Bestandteile eines Systems anders zu implementieren – eine Alternative zu Java Packages oder JARs. Wenn man die Aufteilung des Systems nicht gut hinbekommt, sind die Konsequenzen bei einer Microservices-Aufteilung sogar schwerwiegender. Aus dieser Beobachtung haben sich mehrere Konsequenzen entwickelt:

    • Sicher ist es eine häufige Schwäche von Deployment-Monolithen, dass ihre Strukturierung entweder zu wünschen übriglässt oder mit der Zeit schlechter wird. Ohne zusätzliche Maßnahmen ist es fast sicher, dass das System über die Zeit unwartbar wird. Werkzeuge zum Architekturmanagement [2] können aber Abhängigkeiten überwachen und vermeiden so, dass sie sich unbemerkt einschleichen. Außerdem wird die Struktur explizit und formal überprüfbar, was Diskussionen über dieses Thema vereinfacht. Wenn eine Regel durchbrochen wird, sollte man idealerweise über sie diskutieren: Entweder ist sie nicht sinnvoll oder unklar. In beiden Fällen ist Kommunikation zielführend.
    • Im Java-Universum erlaubt beispielsweise ArchUnit [3], die Struktur eines Systems mit einem Unit-Test zu überprüfen. Spring Modulith [4] kann bei der Strukturierung von Spring-Anwendungen helfen. jMolecules [5] gibt Regeln vor, mit denen ein System entsprechend taktischem Domain-Driven Design strukturiert werden kann.
    • Das Durchsetzen, Überprüfen und Weiterentwickeln der Strukturierung des Systems setzt voraus, dass man das System sinnvoll aufgeteilt hat. In diesem Zusammenhang ist ein neues Interesse an Modularisierung als grundlegendes Konzept zur Systemstrukturierung entstanden. Auch Domain-Driven Design (DDD) hat dabei an Relevanz gewonnen. Besonders die grobgranulare Modularisierung durch Bounded Contexts kann hier unterstützen – weniger das taktische DDD mit seiner feingranularen Aufteilung in einzelne Klassen.

    Diese Entwicklung ist ein Fortschritt. So rücken grundsätzliche Herausforderungen in der Softwarearchitektur in den Fokus. Weil Themen wie die Strukturierung von Systemen so wichtig sind, ist das eine sehr gute Entwicklung. Scheinbar bedeutet das aber ein Scheitern von Microservices, denn sie haben anscheinend ihr Versprechen nicht eingelöst. Tatsächlich war es aber von Anfang an klar, dass Microservices nur dann Vorteile bringen, wenn es eine gute Aufteilung gibt. Und Microservices haben, wie schon erwähnt, einige Vorteile im Bereich Skalierung, Sicherheit, Resilience und der Flexibilität bezüglich der Hardware.

    Stay tuned

    Regelmäßig News zur Konferenz und der JAX-Community erhalten

    [mc4wp-simple-turnstile]

     

    Für solche Zwecke ist der Einsatz von Microservices also nach wie vor sinnvoll und es gibt kaum gute Alternativen. Oft ergibt sich ein Entwurf, der bestimmte Teile des Systems als eigene Docker-Container umsetzt, ganz natürlich aus bestimmten Anforderungen, zum Beispiel wenn man einen Teil des Systems isolieren will, der typischerweise unter hoher Last steht oder nicht besonders stabil ist.

    Deployment

    Es gibt noch einen weiteren Vorteil, den man eigentlich nur durch die Implementierung eines Systems mit Microservices erreichen kann: das unabhängige Deployment. In diesem Bereich gibt es nämlich kaum eine echte Alternative zu Microservices. Und die Vorteile können signifikant sein: Kleinere Deployment-Einheiten sind einfacher und mit weniger Risiko zu deployen. Und häufigere Deployments führen zu mehr Produktivität, weil Teams ihren gesamten Prozess beschleunigen und zuverlässiger machen müssen [6].

    Aber auch beim Deployment wird nicht plötzlich alles gut, nur weil man Microservices nutzt: Die gesamte Deployment-Pipeline jedes Microservices muss unabhängig sein, wenn man die Microservices wirklich unabhängig deployen möchte. Das ist zum Beispiel nicht der Fall, wenn das gesamte System – also alle Microservices – gemeinsam End-to-End getestet werden sollen und diese Tests der hauptsächliche Mechanismus sind, um Fehler zu finden. Der Fokus auf Tests des Gesamtsystems kann gute Gründe haben. So kann es sein, dass es kaum sinnvoll ist, nur einen Teil des Systems zu testen, weil Fachlichkeiten nur im Zusammenspiel mehrerer Systeme funktionieren. Dann ist die schlechte Strukturierung des Systems aber die Quelle des Problems und müsste idealerweise beseitigt werden.

    Consumer-driven Contract-Tests

    Technisch lässt sich ein Integrationstest mehrerer Microservices durch Consumer-driven Contracts umsetzen [7] (Abb. 2). Statt die Microservices gemeinsam zu testen, ruft ein Microservice im Test eine Simulation der anderen Microservices auf, einen sogenannten Stub. So wird klar, welche Art von Interaktion der Microservices von anderen Microservices erwartet. Daraus wird ein Contract (Vertrag) generiert. Dieser Vertrag ist die Basis von Tests des aufgerufenen Microservices. So kann der aufrufende Microservice in seiner Continuous Delivery Pipeline einen formalen Vertrag erzeugen und der aufgerufene Microservice in seiner Continuous Delivery Pipeline mit diesem Vertrag getestet werden. So ist sichergestellt, dass beide Microservices zusammenarbeiten können, auch wenn sie nie zusammen in einer Umgebung liefen. Ein populäres Werkzeug für Consumer-driven Contract-Tests ist Pact [8], das solche Contract-Tests auch bei der Benutzung verschiedener Programmiersprachen in den Microservices unterstützt.

    Abb. 2: Consumer-driven Contract-Tests können Integrationstests beseitigen

    Organisation

    Aber gemeinsame Tests sind nicht der einzige Grund, warum Microservices zusammen deployt werden. Es kann auch sein, dass ein Deployment einzelner Microservices untersagt wird, vielleicht weil ein Betriebsteam für das Management der Produktionsumgebung zuständig sein soll und nur alle Microservices gemeinsam deployen will. Bei der Technologieauswahl ist eine Einschränkung der technischen Möglichkeiten üblich. Rein technisch kann jeder Microservice in einer anderen Programmiersprache mit anderen Frameworks und Bibliotheken umgesetzt werden – und auch die Werkzeuge, beispielsweise für die Continuous Delivery Pipeline, können völlig unterschiedlich sein. Einige Projekte schränken daher die nutzbaren Technologien durch Regeln ein. Ein einheitlicher Technologiestack hat zweifellos Vorteile: Der Wissensaustausch zwischen den Teams ist einfacher und Menschen können einfacher zwischen den Teams wechseln. Auf der anderen Seite sind Teams dann nicht dazu in der Lage, die beste Technologie für die jeweilige Herausforderung zu nutzen.

    Hier gibt es einen Widerspruch: Die bessere Austauschbarkeit von Wissen und Menschen kommt den Teams zugute – warum sollten sie sich gegen einen einheitlichen Stack entscheiden? Jedes Team kann Lösungen beispielsweise für die Continuous Delivery Pipeline einfach übernehmen und muss sich nicht selbst damit beschäftigen.

    Am Ende ist ein wesentlicher Einflussfaktor dafür, wie viel man vorschreibt, Vertrauen: Wenn man die Entwickler:innen als verantwortungslose Spielkinder wahrnimmt, wird man ihnen mehr vorschreiben. Wenn man die Entwickler:innen als fähig und zuverlässig sieht, wird man ihnen mehr Freiheiten lassen. Übrigens kann es sein, dass Menschen mehr Verantwortung übernehmen, wenn man ihnen mehr Entscheidungskompetenzen gibt. Ein Mittel gegen als Spielkinder wahrgenommene Entwickler:innen kann also sein, ihnen mehr Freiheiten zu geben, damit sie daraufhin mehr Verantwortung übernehmen müssen.

    Am Ende geht es hier also um organisatorische Themen und den menschlichen Faktor. Microservices können technische Freiheiten nur ermöglichen. Ob die Freiheiten wirklich umgesetzt werden, ist eine Frage, die organisatorisch entschieden werden muss.

    Solche Freiheiten machen Teams autonomer, weil sie dann technische Entscheidungen unabhängig von anderen Teams oder Personen fällen können. Autonome Teams müssen weniger mit anderen kommunizieren und sollten daher produktiver sein – auch die Jobzufriedenheit wird dann höher sein. Agilität setzt auch auf autonome Teams. Da Agilität mittlerweile das typische Vorgehen in Projekten darstellt, müssten also die meisten Projekte in diese Richtung arbeiten. Dennoch setzen de facto viele Organisationen aber gerade nicht auf autonome Teams, auch wenn es Lippenbekenntnisse zu diesem Ideal gibt [9].

    DIE KUNST DER SOTWARE-ARCHITEKTUR

    Architecture & Design-Track entdecken

     

    Fazit

    Microservices sind nicht die endgültige Lösung für alle Architekturprobleme – genauso wenig wie alle anderen Architekturansätze auch. Dennoch haben sie eine wichtige Diskussion in Gang gesetzt:

    • Microservices sind nur eine weitere Möglichkeit, Systeme aufzuteilen. Die Aufteilung und damit die Architektur werden nicht automatisch besser, nur weil man beispielsweise statt Java Packages nun Microservices nutzt.
    • Daher rücken Strukturierung und Modularisierung stärker in den Fokus. Mit Ansätzen wie Domain-Driven Design wird die Orientierung an der Domäne wieder wichtiger. Diese Themen sind fundamental für Softwarearchitektur, aber auch für den Erfolg von Projekten, sodass sie nun zurecht wichtiger werden.
    • Technische Vorteile wie getrennte Skalierung, getrenntes Deployment, mehr Resilience oder unabhängige Technologieentscheidungen sprechen nach wie vor für Microservices – und sind in einigen Fällen alternativlos.
    • Schließlich wirft die möglicherweise größere technische Autonomie die Frage auf, wie viel Autonomie man den Teams wirklich zugestehen möchte.

    Macht man also noch Microservices? Hoffentlich ja und hoffentlich nur da, wo es sinnvoll ist – aber das gilt für jeden Architekturansatz.


    Links & Literatur

    [1] Software Architektur im Stream zu Big Ball of Mud: https://software-architektur.tv/2023/03/31/folge159.html

    [2] Software Architektur im Stream Folgen zu Architektur-Management-Werkzeugen: https://software-architektur.tv/tags.html#Architecture%20Management

    [3] Software Architektur im Stream: „Peter Gafert zu ArchUnit“: https://software-architektur.tv/2021/04/09/folge55.html

    [4] https://spring.io/projects/spring-modulith

    [5] Software Architektur im Stream: „Taktisches Domain-Driven Design mit Java und jMolecules mit Oliver Drotbohm“: https://software-architektur.tv/2024/05/31/episode219.html

    [6] Software Architektur im Stream: „Warum Continuous Delivery – Die DevOps Studie“: https://software-architektur.tv/2020/08/14/folge012.html

    [7] https://martinfowler.com/articles/consumerDrivenContracts.html

    [8] https://pact.io

    [9] Software Architektur im Stream: „Autonome Teams – Wollen wir das wirklich?“: https://software-architektur.tv/2025/01/17/folge247.html

    The post Microservices vs. Monolithen: Architektur-Strategien nach dem Hype appeared first on JAX.

    ]]>
    Model Context Protocol mit Spring Boot: Eigene KI-Tools integrieren https://jax.de/blog/mcp-spring-boot-ai-integration/ Fri, 04 Jul 2025 09:21:47 +0000 https://jax.de/?p=107607 Moderne KI-Anwendungen benötigen Kontext – und genau hier setzt das Model Context Protocol (MCP) an. Es schafft eine standardisierte Brücke zwischen Tools, Modellen und Anwendungen, unabhängig von Plattform und Programmiersprache. Wir schauen uns an, wie MCP-Server mit Spring AI konsumiert und eigene MCP-Server mit Spring Boot entwickelt werden können.

    The post Model Context Protocol mit Spring Boot: Eigene KI-Tools integrieren appeared first on JAX.

    ]]>
    In modernen KI-Anwendungen ist die effiziente Kommunikation zwischen Modellen und Anwendungen ein entscheidender Faktor. Entwicklerinnen und Entwickler stoßen dabei oft auf Probleme bei der Integration unterschiedlicher Tools und Technologien, insbesondere wenn es darum geht, Kontextinformationen zuverlässig und flexibel auszutauschen. Das Model Context Protocol (MCP) [1] schafft hier Abhilfe, indem es eine einheitliche und sprachunabhängige Schnittstelle definiert, über die Anwendungen Kontextinformationen an KI-Modelle liefern und deren Antworten empfangen können.

    Ursprünglich von Anthropic entwickelt [2], orientiert sich MCP an Konzepten des Language Server Protocols (LSP) und bietet damit eine vergleichbar universelle und flexible Basis für KI-basierte Systeme. MCP-Server lassen sich dabei vielfältig betreiben: als OCI-Container (aus dem Katalog von Docker [3]), als WebAssembly-Modul (z. B. via mcp.run) oder als gewöhnliche Anwendung, die in einer beliebigen Programmiersprache implementiert ist.

    Spring AI [4], das kürzlich das MCP integriert hat, bietet Entwicklerinnen und Entwicklern nun auf Java-Basis eine komfortable Möglichkeit, MCP-Server direkt in Spring-Boot-Anwendungen zu konsumieren und eigene MCP-Server zu implementieren.

    Technische Grundlagen des MCP

    Das Model Context Protocol soll eine einheitliche, klar definierte Schnittstelle für die Kommunikation zwischen Anwendungen und KI-Modellen schaffen. MCP folgt dabei einer Client-Server-Architektur, ähnlich dem Language Server Protocol (LSP), das sich bereits als Standard in Entwicklungsumgebungen etabliert hat. Dabei definiert das MCP eine klare Rollenverteilung zwischen Client und Server:

    • MCP-Server: Verarbeitet eingehende Anfragen und liefert Kontextinformationen oder Ergebnisse zurück. Typische Aufgaben eines MCP-Servers sind beispielsweise das Bereitstellen von Daten aus Datenbanken, Dateisystemen, Ticketing-Systemen oder externen KI-Modellen.
    • MCP-Client: Stellt Anfragen an den MCP-Server und verarbeitet die Antworten. Beispiele für MCP-Clients sind Entwicklungsumgebungen, Webapplikationen oder andere KI-Tools, die Kontextinformationen zur Bearbeitung von Aufgaben benötigen.

    Zur Kommunikation zwischen Client und Server unterstützt MCP mehrere Transporttypen (Abb. 1). Für die Kommunikation mit lokalen Prozessen und Kommandozeilentools bietet MCP den Transporttyp Standard Input/Output (stdio) an, der Anfragen und Antworten über die standardisierten Ein- und Ausgabeströme (stdin/stdout) austauscht. Alternativ steht für HTTP-basierte Kommunikation der Transporttyp Server-sent Events (SSE) zur Verfügung, mit dem sich MCP-Clients und -Server auch über das Netzwerk miteinander verbinden können.

    MCP verwendet ein standardisiertes, in der Regel auf JSON-RPC basierendes Protokollformat. Dadurch wird die Entwicklung neuer MCP-Clients und -Server vereinfacht und gleichzeitig eine breite Kompatibilität mit unterschiedlichen Systemen sichergestellt.

    Abb.1: MCP-Client- und -Serveraufbau

    Flexibles Deployment von MCP-Servern

    Ein großer Vorteil des Model Context Protocol liegt in der Flexibilität bei den Betriebsformen von MCP-Servern. Entwicklerinnen und Entwickler können MCP optimal in bestehende Systemlandschaften integrieren und dabei genau jene Technologien verwenden, die ihren Anforderungen entsprechen. Auch lassen sich bestehende Softwaresysteme über einen MCP-Server für KI-Anwendungen verfügbar machen.

    Sie lassen sich sowohl lokal als Kommandozeilenanwendungen als auch remote über HTTP einsetzen. Durch die standardisierten Schnittstellen lassen sich MCP-Server nahtlos, wie oft in Demos gezeigt, in Entwicklungsumgebungen wie Visual Studio Code oder IntelliJ IDEA sowie in Applikationsframeworks wie Spring Boot mit Spring AI integrieren. MCP-Server können situationsabhängig als sogenannte Content Retriever eingesetzt werden, um KI-Modelle mit den jeweils benötigten Kontextdaten zu versorgen.

    Stay tuned

    Regelmäßig News zur Konferenz und der JAX-Community erhalten

    [mc4wp-simple-turnstile]

     

    Integration von MCP in Spring AI

    Das MCP Java SDK [5] bietet eine vollständige Implementierung des Model Context Protocol für Java. Es ermöglicht die standardisierte Kommunikation zwischen KI-Modellen und externen Tools und unterstützt dabei sowohl synchrone als auch asynchrone Kommunikationsmuster. Entwicklerinnen und Entwickler können mit dem SDK eigene MCP-Clients und -Server erstellen.

    Spring AI MCP erweitert das MCP Java SDK um eine enge Integration mit dem Spring-Ökosystem. Mit den bereitgestellten Startern für Spring Boot lassen sich sowohl MCP-Clients als auch -Server einfach und effizient entwickeln. Das erleichtert die Anbindung bestehender Tools an KI-Modelle und die Bereitstellung eigener Kontextquellen im Rahmen von KI-Anwendungen.

    Über den Spring Initializer können Projekte schnell mit der benötigten Spring-AI-MCP-Unterstützung konfiguriert werden. Entwicklerinnen und Entwickler profitieren dabei von den gewohnten Mechanismen von Spring Boot wie der automatischen Konfiguration und dem einfachen Management von Verbindungen und Services.

    Integration eines Node-basierten MCP-Servers in Spring AI

    Spring AI unterstützt auch die Einbindung externer MCP-Server, die in anderen Sprachen implementiert sind – etwa mit Node.js. In diesem Beispiel wird ein MCP-Server zur Wetterabfrage (@h1deya/mcp-server-weather) über stdio in eine Spring-Boot-Anwendung integriert und über einen Chatendpunkt angesprochen.

    Damit die Anwendung mit MCP-Servern kommunizieren und OpenAI-Modelle ansteuern kann, werden zwei Abhängigkeiten benötigt (Listing 1):

    • spring-ai-starter-mcp-client: Ermöglicht die Kommunikation mit MCP-Servern über das MCP Java SDK.
    • spring-ai-starter-model-openai: Bindet OpenAI-Modelle wie GPT-4o-mini in die Spring-AI-Infrastruktur ein.
    <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-starter-mcp-client</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-starter-model-openai</artifactId>
    </dependency>
    

    Die application.yaml (Listing 2) konfiguriert die Verbindung zu einem lokalen, Node-basierten MCP-Server, der automatisch über npx gestartet wird: command: npx und args: -y,@h1deya/mcp-server-weather [6] startet den MCP-Server direkt aus dem Java-Prozess mit dem Node.js-Modul.

    Diese Konfiguration sorgt dafür, dass der MCP-Server beim Start der Anwendung automatisch bereitgestellt wird und über stdio ansprechbar ist. Mit dem dazugehörigen Java API kann man den MCP-Client auch programmatisch konfigurieren und verwenden.

    spring:
      application:
        name: mcp-client-weather
    
      ai:
        openai:
          api-key: ${OPENAI_API_KEY}
          chat:
            options:
              model: gpt-4o-mini
    
        mcp:
          client:
            stdio:
              connections:
                weather:
                  command: npx
                  args: -y,@h1deya/mcp-server-weather
    

    Der WeatherController (Listing 3) implementiert einen REST-Endpunkt, der Benutzeranfragen entgegennimmt und über Spring AI verarbeitet:

    • Im Konstruktor wird ein ChatClient aufgebaut, der standardmäßig auf Wetterfragen spezialisiert ist.
    • Über defaultTools wird der externe MCP-Server als Tool (MCP-Client) angebunden.
    • Der /ask-Endpunkt nimmt eine Frage entgegen und gibt eine KI-generierte Antwort zurück, die gegebenenfalls durch den MCP-Server ergänzt wird.

    SIE LIEBEN JAVA?

    Den Core-Java-Track entdecken

     

    Dieses Set-up verbindet das generische Sprachmodell von OpenAI mit der domänenspezifischen Intelligenz eines MCP-gestützten Wetterdienstes, flexibel erweiterbar durch weitere Tools.

    @RestController
    public class WeatherController {
      private final ChatClient chatClient;
    
      public WeatherController(
        ChatClient.Builder chatClientBuilder, 
        List<McpSyncClient> mcpSyncClients) {
        this.chatClient = chatClientBuilder
          .defaultSystem("You are a weather assistant ...")
          .defaultTools(new SyncMcpToolCallbackProvider(mcpSyncClients))
          .build();
      }
    
      @PostMapping("/ask")
      public Answer ask(@RequestBody Question question) {
        return chatClient.prompt()
          .user(question.question())
          .call()
          .entity(Answer.class);
      }
    }
    

    Aufbau eines eigenen MCP-Servers mit Spring Boot

    Neben dem Konsumieren externer MCP-Server ermöglicht Spring AI auch die Entwicklung eigener MCP-Server auf Basis von Spring Boot. In diesem Beispiel wird ein einfacher Wetterdienst aufgebaut, der Wetterdaten über das API des Deutschen Wetterdienstes (DWD) bereitstellt. Für die Entwicklung eines MCP-Servers wird die folgende Maven-Abhängigkeit benötigt:

    <dependency>
      <groupId>org.springframework.ai</groupId>
      <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
    </dependency>

    Sie stellt die notwendigen Komponenten bereit, um Anfragen gemäß dem Model Context Protocol über HTTP (SSE) zu empfangen und zu verarbeiten.

    Die Konfiguration des MCP-Servers erfolgt über die application.yaml (Listing 4), wobei name und version die Metadaten des Servers definieren, die bei der Kommunikation über MCP übertragen werden.

    spring:
      ai:
        mcp:
          server:
            name: dwd-weather-server
            version: 0.0.1
    

    Die folgende Konfigurationsklasse (Listing 5) registriert die angebotenen Tools:

    • Die Methode weatherTools registriert den WeatherService als Sammlung von Tools, die über MCP aufrufbar sind.
    • Zusätzlich wird mit toUpperCase ein einfaches Tool definiert, das Texte in Großbuchstaben umwandelt – ein Beispiel für generische Funktionalität.
    @Bean
    ToolCallbackProvider weatherTools(WeatherService weatherService) {
      return MethodToolCallbackProvider.builder()
        .toolObjects(weatherService)
        .build();
    }
    
    @Bean
    ToolCallback toUpperCase() {
      return FunctionToolCallback.builder("toUpperCase", (TextInput input) -> input.input().toUpperCase())
        .inputType(TextInput.class)
        .description("Put the text to upper case.")
        .build();
    }
    

    Die eigentliche Logik wird im WeatherService bereitgestellt (Listing 6). Beide Methoden sind mit der Annotation @Tool versehen, was sie automatisch für die Kommunikation über MCP verfügbar macht. Eingabeparameter werden mittels @ToolParam beschrieben, um eine präzisere semantische Beschreibung gegenüber KI-Modellen zu ermöglichen.

    @Service
    public class WeatherService {
    
      @Tool(description = "Find and retrieve the station_id for weather stations")
      public String getStationIds(@ToolParam(
        description = "Station is typically just city") String station){
        // business logic
        return stationId;
      }
    
      @Tool(description = "Retrieve current and forecast weather information.")
      public String getStationOverview(@ToolParam(
        description = "List of station ids to resolve weather forecast.") 
        List<String> stationIds){
          // business logic
        return stationOverview;
      }
    }
    

    Der erstellte MCP-Server stellt die registrierten Tools standardmäßig über SSE auf dem HTTP-Port 8080 (Standardport von Spring Boot) bereit. Eingehende Anfragen erfolgen gemäß dem MCP, wobei sowohl Anfragen als auch Antworten im definierten JSON-Format ausgetauscht werden.

    Stay tuned

    Regelmäßig News zur Konferenz und der JAX-Community erhalten

    [mc4wp-simple-turnstile]

     

    Mit Hilfe eines beliebigen MCP-Clients, der SSE unterstützt, können nun die angebotenen Tools konsumiert werden. Durch die Verwendung von SSE ermöglicht der MCP-Server eine effiziente, bidirektionale Kommunikation mit KI-Modellen und anderen Anwendungen, unabhängig von deren Implementierungssprache oder Umgebung.

    Anreicherung des Prompts

    Mit der Bereitstellung spezialisierter Tools über einen eigenen MCP-Server lässt sich das Model Context Protocol gezielt zur Anreicherung von Prompts einsetzen. In vielen KI-Anwendungen reicht es nicht aus, dem Modell nur die eigentliche Benutzeranfrage zu übergeben. Die Qualität und Relevanz der Antworten verbessern sich deutlich, wenn zusätzliche Kontextinformationen bereitgestellt werden. Diese Technik wird als „Prompt Stuffing“ oder „Anreicherung des Prompts“ bezeichnet. Besonders wichtig ist das bei Echtzeitdaten oder privaten Daten. Tools, die als MCP-Server eingebunden sind, können Informationen aus externen Systemen liefern, um das Modell bei der Beantwortung zu unterstützen.

    Wie bereits im vorherigen Abschnitt gezeigt, helfen Spring AI und Spring Boot dabei, die Konfiguration der Clients bzw. der Server zu vereinfachen. Ein typisches Beispiel ist der zuvor vorgestellte Wetterdienst. Hier wird das Sprachmodell vor der Antwortgenerierung mit aktuellen Wetterdaten ergänzt. Spring AI nutzt dafür das Konzept der Tools im ChatClient. Tools können lokal definierte Services oder externe MCP-Server sein.

    Beim Erstellen eines Prompts ruft der ChatClient zuerst die definierten Tools auf und reichert den Benutzerinput mit den Ergebnissen an. Erst danach wird der vollständige, angereicherte Prompt an das KI-Modell übergeben.

    Beispielhafter Ablauf

    Ein typischer Ablauf stellt sich wie folgt dar:

    • Benutzerfrage: „Wie wird das Wetter morgen in Kempten?“
    • LLM: Das Modell selektiert die MCP-Schnittstellen, um die Daten zu beschaffen.
    • Tool-Aufruf: Der MCP-Server liefert aktuelle Wetterdaten für Kempten.
    • Anreicherung: Die Wetterdaten werden zusammen mit der ursprünglichen Frage in den Prompt integriert.
    • Antwortgenerierung: Das KI-Modell erzeugt eine fundierte Antwort auf Basis des angereicherten Prompts. Das könnte auch wieder ein Sprung zu Schritt 2 sein.

    Spring AI bietet über seine Logging-Funktionalität Einblicke in diesen Prozess. In den Logs lässt sich nachvollziehen, wie der ursprüngliche Benutzerinput durch die Ergebnisse der Tools ergänzt wurde (Listing 7).

    2025-05-12T10:47:05.303+02:00 DEBUG 3175 --- [mcp-client-weather-sse] [nio-8081-exec-2] o.s.web.client.DefaultRestClient         : Writing [ChatCompletionRequest[messages=[ChatCompletionMessage[rawContent=Wie ist die Wettervorhersage für Kempten für morgen?, role=USER, name=null, toolCallId=null, toolCalls=null, refusal=null, audioOutput=null], ChatCompletionMessage[rawContent=Du bist ein Assistent für die Analyse von Wetterdaten. Du erhältst strukturierte Vorhersagedaten von verschiedenen Wetterstationen.
    
    Dateneinheiten:
    - Temperatur in 0,1 °C
    - Startzeit Unixzeit (ms)
    - Zeitintervall (ms)
    - Niederschlag gesamt in 0,1 mm/h
    - Niederschlag pro Tag in 0,1 mm/d
    - Sonnenschein in 0,1 min
    - Luftfeuchtigkeit in 0,1 %
    - Taupunkt in 0,1 °C (2 m Höhe)
    - Luftdruck in 0,1 hPa (Bodenhöhe)
    
    Anweisungen:
    - Wandle alle Werte in lesbare Einheiten um.
    - Erkenne und beschreibe Trends.
    - Kommentiere fehlende Werte (null).
    - Fasse Temperatur, Niederschlag, Sonnenschein, Feuchtigkeit, Taupunkt, Wind (falls vorhanden) und Luftdruck separat zusammen.
    - Erstelle am Schluss eine kurze Wetterzusammenfassung (Wetterlage, Temperaturverlauf, Komfort, besondere Ereignisse).
    
    Formuliere deine Antworten klar, schrittweise und in vollständigen Sätzen. Halte sie freundlich, präzise und aufschlussreich.
    , role=SYSTEM, name=null, toolCallId=null, toolCalls=null, refusal=null, audioOutput=null], ChatCompletionMessage[rawContent=Wie ist die Wettervorhersage für Kempten für morgen?
    Your response should be in JSON format.
    Do not include any explanations, only provide a RFC8259 compliant JSON response following this format without deviation.
    Do not include markdown code blocks in your response.
    Remove the ```json markdown from the output.
    Here is the JSON Schema instance your output must adhere to:
    ```{
      "$schema" : "https://json-schema.org/draft/2020-12/schema",
      "type" : "object",
      "properties" : {
        "answer" : {
          "type" : "string"
        }
      },
      "additionalProperties" : false
    }```
    , role=USER, name=null, toolCallId=null, toolCalls=null, refusal=null, audioOutput=null], ChatCompletionMessage[rawContent=null, role=ASSISTANT, name=null, toolCallId=null, toolCalls=[ToolCall[index=null, id=call_4ggmsbY1Kg3WwopK6JEtlldc, type=function, function=ChatCompletionFunction[name=spring_ai_mcp_client_weather_getStationIds, arguments={"station":"Kempten"}]]], refusal=null, audioOutput=null], ChatCompletionMessage[rawContent=[{"text":"\"StationId: 02559, Stationsname: Kempten, Bundesland: Bayern\\n\""}], role=TOOL, name=spring_ai_mcp_client_weather_getStationIds, toolCallId=call_4ggmsbY1Kg3WwopK6JEtlldc, toolCalls=null, refusal=null, audioOutput=null], ChatCompletionMessage[rawContent=null, role=ASSISTANT, name=null, toolCallId=null, toolCalls=[ToolCall[index=null, id=call_3Q3csYoAzo2qPsHKFOMbFQ1L, type=function, function=ChatCompletionFunction[name=spring_ai_mcp_client_weather_getStationOverview, arguments={"stationIds":["02559"]}]]], refusal=null, audioOutput=null], ChatCompletionMessage[rawContent=[{"text":"\"StationOverview{stations={02559=StationData[forecast1=Forecast[stationId=02559, start=1747000800000, timeStep=3600000, temperature=[…, 115, 123, 133, …], windSpeed=null, windDirection=null, windGust=null, precipitationTotal=[32767, 32767, …], sunshine=[32767, 32767, 32767, 32767,…], dewPoint2m=[46, 41, 39,…], surfacePressure=[10216, 10218, 10217, …], isDay=[false, false, …], cloudCoverTotal=[], temperatureStd=[…, 12, 14, 14, 13, 12,…], icon=[32767, 32767,…], icon1h=[4, 4, …], precipitationProbablity=null, precipitationProbablityIndex=null], forecast2=Forecast[stationId=02559, start=1747260000000, timeStep=10800000, temperature=[], windSpeed=null, windDirection=null, windGust=null, precipitationTotal=[0, 0, …], sunshine=[32767, 32767, …], surfacePressure=[10145, 10145, …], isDay=[false, false,…], cloudCoverTotal=[], temperatureStd=[], icon=[…], precipitationProbablity=null, precipitationProbablityIndex=null], forecastStart=null, warnings=[], threeHourSummaries=null]}}\""}], role=TOOL, name=spring_ai_mcp_client_weather_getStationOverview, toolCallId=call_3Q3csYoAzo2qPsHKFOMbFQ1L, toolCalls=null, refusal=null, audioOutput=null]], model=gpt-4o-mini, store=null, metadata=null, frequencyPenalty=null, logitBias=null, logprobs=null, topLogprobs=null, maxTokens=null, maxCompletionTokens=null, n=null, outputModalities=null, audioParameters=null, presencePenalty=null, responseFormat=null, seed=null, serviceTier=null, stop=null, stream=false, streamOptions=null, temperature=0.7, topP=null, tools=[org.springframework.ai.openai.api.OpenAiApi$FunctionTool@372faade, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@5c334418, org.springframework.ai.openai.api.OpenAiApi$FunctionTool@23b4be7c], toolChoice=null, parallelToolCalls=null, user=null, reasoningEffort=null]] as "application/json" with org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
    

    Dieses Beispiel zeigt auch, dass manchmal der Kontext im System-Prompt definiert werden muss, damit das LLM die Daten korrekt auswerten kann. Das Erstellen solcher Prompts ist nicht immer einfach. Ein iteratives Vorgehen und die Überprüfung des Prompts mit dem LLM helfen dabei wesentlich.

    NEUES AUS DER JAVA-ENTERPRISE-WELT

    Serverside Java-Track entdecken

     

    Fazit und Ausblick

    Das Model Context Protocol bietet einen strukturierten, sprachunabhängigen und erweiterbaren Ansatz für die Kommunikation zwischen KI-Modellen und externen Tools. Durch die klare Trennung zwischen Client und Server sowie die Unterstützung standardisierter Transportmechanismen wie stdio und SSE lässt sich MCP in unterschiedlichsten Szenarien einsetzen: von lokalen Entwicklungsumgebungen bis hin zu Cloud-basierten Anwendungen.

    Mit dem MCP Java SDK und der Spring-AI-Integration steht Entwicklerinnen und Entwicklern ein leistungsfähiges, standardisiertes Werkzeug zur Verfügung, um sowohl bestehende MCP-Server zu konsumieren als auch eigene Services bereitzustellen. Die nahtlose Einbindung in Spring Boot ermöglicht eine schnelle Umsetzung, ohne auf bewährte Spring-Konzepte verzichten zu müssen.

    Der gezeigte Wetterdienst illustriert, wie einfach sich domänenspezifische Funktionalität mit klaren Schnittstellen als MCP-Server implementieren lässt: Annotationen für Tool-Beschreibungen und automatische Bereitstellung über HTTP. Die im Artikel gezeigten Codebeispiele und weitere sind vollständig auf GitHub verfügbar [7].

    Mit der zunehmenden Verbreitung KI-gestützter Anwendungen ist zu erwarten, dass sich MCP als standardisiertes Bindeglied zwischen Modellen und Werkzeugen etablieren wird. Perspektivisch bietet das Protokoll Potenzial für weitere Integrationen, etwa in IDEs, DevOps-Werkzeuge oder spezialisierten Businessanwendungen, sowohl lokal als auch in verteilten Systemlandschaften.


    Links & Literatur

    [1] Model Context Protocol: https://modelcontextprotocol.io

    [2] Introduction the Model Context Protocol: https://www.anthropic.com/news/model-context-protocol

    [3] Docker MCP Catalog and Toolkit: https://www.docker.com/products/mcp-catalog-and-toolkit/

    [4] Spring AI: https://docs.spring.io/spring-ai/reference/api/mcp/mcp-overview.html

    [5] MCP Java SDK: https://modelcontextprotocol.io/sdk/java/mcp-overview

    [6] Simple Weather MCP-Server: https://github.com/hideya/mcp-server-weather-js

    [7] Spring AI MCP Examples: https://github.com/patbaumgartner/spring-ai-mcp-entwickler.de

    The post Model Context Protocol mit Spring Boot: Eigene KI-Tools integrieren appeared first on JAX.

    ]]>