#Roadmap, Risiko-Management, Cutover und Teamprozess
Migration Slices, Scoring, Definition of Ready/Done, Architekturentscheidungen, Release-Strategien und Rollback.
#Migration Roadmap, Team-Prozess, Priorisierung, Risiko-Management und Cutover
Dieser Abschnitt macht aus der technischen Modernisierung einen steuerbaren Migrationsprozess. Bis hierhin hast du gelernt, wie man Legacy Enterprise Java mit EJB, JTA, JMS, SOAP, JSP, JPA, JNDI, Application Servern und Deployment-Artefakten analysiert und technisch modernisiert. Jetzt geht es darum, daraus eine Roadmap zu bauen, die ein Team tatsächlich umsetzen kann.
Der Kern dieses Abschnitts:
Modernisierung ist kein Refactoring-Projekt.
Modernisierung ist ein Risiko-Reduktionsprogramm mit technischen Lieferobjekten.
#Warum eine Migration Roadmap notwendig ist
Ein altes Enterprise-Java-System kann selten in einem großen Schritt modernisiert werden. Die Risiken sind zu hoch:
- unbekannte Transaktionssemantik
- alte Application-Server-Konfiguration
- JNDI-Abhängigkeiten
- SOAP-Verträge mit externen Partnern
- JMS-Retry-Verhalten
- fehlende Tests
- Businesslogik in JSPs
- Datenbank-Trigger und Stored Procedures
- manuelle Deployments
- implizite Security-Regeln
Eine Roadmap verhindert, dass das Team zufällig an einzelnen Klassen arbeitet. Sie beantwortet:
Was modernisieren wir zuerst?
Warum genau das?
Wie messen wir Fortschritt?
Wann ist ein Slice fertig?
Wann dürfen wir in Produktion gehen?
Wie rollen wir zurück?
Die Roadmap ist also nicht nur ein Plan, sondern ein Kontrollsystem.
#Von Komponenten zu Migration Slices
Modernisiere nicht nach Technologieblöcken.
Schlecht:
Sprint 1: alle EJBs ersetzen
Sprint 2: alle JSPs ersetzen
Sprint 3: alle SOAP Clients ersetzen
Besser:
Slice 1: Bestellung anlegen
Slice 2: Zahlung verarbeiten
Slice 3: Rechnung erzeugen
Slice 4: Kunde synchronisieren
Slice 5: Stammdaten importieren
Ein Migration Slice ist ein fachlicher Durchstich:
Entry Point
↓
Application Service / EJB
↓
Transaktion
↓
Datenbank
↓
Messaging / SOAP / Batch
↓
Betrieb / Monitoring / Tests
Jeder Slice liefert sichtbaren Fortschritt.
#Slice-Kandidaten sammeln
Lege eine Datei an:
docs/migration-slices.md
Vorlage:
# Migration Slices
| ID | Slice | Entry Point | Fachlicher Wert | Risiko | Aufwand | Empfehlung |
|---|---|---|---|---|---|---|
| S-001 | Bestellung anlegen | JSP/SOAP | hoch | mittel | mittel | zuerst |
| S-002 | Zahlung verarbeiten | EJB/SOAP | sehr hoch | hoch | hoch | später |
| S-003 | Rechnung erzeugen | JMS/MDB | hoch | hoch | mittel | nach S-001 |
| S-004 | Kunde ändern | JSP/SOAP | mittel | niedrig | niedrig | guter Start |
| S-005 | Nachtlauf Abrechnung | Timer/Batch | hoch | sehr hoch | hoch | nicht zuerst |
Sammle zuerst 10–20 Slices, bevor du priorisierst.
#Slice-Bewertung mit Score
Bewerte jeden Slice mit 1–5 Punkten.
| Kriterium | Bedeutung | 1 | 5 |
|---|---|---|---|
| Business Value | fachlicher Nutzen | kaum relevant | kritisch |
| Änderungsdruck | wie oft wird dort gearbeitet? | selten | ständig |
| Risiko | technisches/operatives Risiko | niedrig | sehr hoch |
| Verstehbarkeit | wie gut ist der Flow bekannt? | sehr gut | unbekannt |
| Testbarkeit | wie gut absicherbar? | gut | schlecht |
| Abhängigkeiten | andere Systeme betroffen? | wenige | viele |
Beispiel:
| Slice | Value | Änderungsdruck | Risiko | Unklarheit | Testbarkeit schlecht | Summe |
|---|---:|---:|---:|---:|---:|---:|
| Kunde ändern | 3 | 4 | 2 | 2 | 2 | 13 |
| Bestellung anlegen | 5 | 5 | 3 | 3 | 3 | 19 |
| Payment | 5 | 4 | 5 | 4 | 5 | 23 |
| Billing Batch | 5 | 3 | 5 | 5 | 5 | 23 |
Wichtig:
Der höchste Score ist nicht automatisch der erste Slice.
Der erste Slice sollte lernreich, aber nicht existenzgefährdend sein.
#Der richtige erste Slice
Der erste Slice sollte diese Eigenschaften haben:
- fachlich verständlich
- nicht sicherheitskritisch
- nicht paymentkritisch
- nicht batchkritisch
- mittlere Komplexität
- gute manuelle Testbarkeit
- klare Entry Points
- begrenzte externe Abhängigkeiten
Gute erste Slices:
- Kunde aktualisieren
- Adresse ändern
- Bestellung ohne Payment anlegen
- Dokument hochladen
- interne Stammdaten pflegen
- einfacher SOAP Client kapseln
Schlechte erste Slices:
- Login/Security
- Payment
- Billing
- große Nachtläufe
- regulatorische Reports
- komplexe MDB Retry-Flows
Ziel des ersten Slices ist nicht maximaler Business Value. Ziel ist, die Modernisierungsmethode im echten Projekt zu validieren.
#Definition of Ready für einen Slice
Ein Slice ist bereit für Umsetzung, wenn diese Fragen beantwortet sind:
# Definition of Ready
## Fachlich
- [ ] Fachlicher Zweck verstanden
- [ ] Fachlicher Owner bekannt
- [ ] Akzeptanzkriterien bekannt
- [ ] Kritische Fehlerfälle bekannt
## Technisch
- [ ] Entry Points dokumentiert
- [ ] Call Chain dokumentiert
- [ ] Transaktionsgrenzen dokumentiert
- [ ] DB Writes bekannt
- [ ] JMS/SOAP/Batches bekannt
- [ ] Security-Regeln bekannt
- [ ] Runtime-Ressourcen bekannt
## Qualität
- [ ] Smoke Test möglich
- [ ] Charakterisierungstest möglich oder manuelles Testskript vorhanden
- [ ] Rollback-Plan existiert
- [ ] Monitoring/Logs reichen für Betrieb
Ohne Definition of Ready beginnt das Team zu früh mit Codeänderungen.
#Definition of Done für einen Slice
Ein Slice ist fertig, wenn nicht nur der Code gebaut wird.
# Definition of Done
## Code
- [ ] Fachlogik aus Legacy-Komponente extrahiert oder gekapselt
- [ ] Ports/Adapter eingeführt, wo sinnvoll
- [ ] Transaktionsverhalten bewusst beibehalten oder dokumentiert geändert
- [ ] Keine neuen versteckten JNDI-Abhängigkeiten
- [ ] Keine neuen direkten SOAP/JMS-Abhängigkeiten in der Fachlogik
## Tests
- [ ] Unit Tests für Plain-Java-Kern
- [ ] Adapter- oder Integrationstest für kritische Infrastruktur
- [ ] Charakterisierungstest für Legacy-Verhalten
- [ ] Smoke Test nach Deployment
## Betrieb
- [ ] Logs mit Correlation ID
- [ ] relevante Metriken vorhanden
- [ ] Fehlerfälle dokumentiert
- [ ] Rollback getestet oder plausibel
## Dokumentation
- [ ] Transaction Map aktualisiert
- [ ] Runtime Resources aktualisiert
- [ ] Modernization Candidate aktualisiert
- [ ] Risiko-Backlog aktualisiert
Merksatz:
Ein Slice ist nicht fertig, wenn der Code schöner ist.
Er ist fertig, wenn das Risiko reduziert und der Betrieb abgesichert ist.
#Migration Board aufbauen
Nutze ein Board mit diesen Spalten:
Backlog
↓
Analyse
↓
Ready
↓
In Umsetzung
↓
In Test
↓
Shadow / Parallel Run
↓
Cutover geplant
↓
Live
↓
Stabilisiert
↓
Legacy entfernt
Warum so viele Spalten?
Weil Legacy-Modernisierung nicht mit Merge endet. Der kritische Teil ist oft:
Deployen
Beobachten
Vergleichen
Cutover
Rollback-Fähigkeit
Altcode entfernen
#Team-Rollen in der Modernisierung
Ein Legacy-Modernisierungsteam braucht mehrere Perspektiven.
| Rolle | Verantwortung |
|---|---|
| Tech Lead | Zielarchitektur, technische Entscheidungen, Schnittgrenzen |
| Legacy Expert | Container-Verhalten, alte Semantik, historische Gründe |
| Domain Expert | fachliche Regeln, Sonderfälle, Akzeptanz |
| QA/Test Engineer | Charakterisierungstests, Regression, Testdaten |
| Operations/SRE | Deployment, Monitoring, Rollback, Runbooks |
| Security Owner | Rollen, AuthN/AuthZ, Secrets, Compliance |
| Product Owner | Priorisierung, Business Value, Cutover-Freigabe |
Wenn eine dieser Perspektiven fehlt, entstehen blinde Flecken.
Beispiel:
Ein Entwickler ersetzt REQUIRES_NEW sauber technisch.
Aber nur der Fachbereich weiß, ob Audit auch bei Rollback bestehen bleiben muss.
#Architekturentscheidungen dokumentieren
Nutze ADRs: Architecture Decision Records.
Datei:
docs/adr/0001-use-outbox-for-order-events.md
Vorlage:
# ADR 0001: Outbox für Order Events verwenden
## Status
Accepted
## Kontext
Der Legacy-Code sendet JMS Messages direkt innerhalb des CreateOrder Use Cases.
Bei Fehlern kann DB/JMS-Inkonsistenz entstehen.
## Entscheidung
Order-Events werden zuerst in einer Outbox-Tabelle gespeichert.
Ein separater Publisher versendet sie an JMS.
## Konsequenzen
Positiv:
- DB und Event-Erzeugung sind atomar.
- Retry ist kontrollierbar.
- Consumer können idempotent gemacht werden.
Negativ:
- zusätzliche Tabelle
- Publisher nötig
- Monitoring nötig
- at-least-once Delivery statt exactly-once
ADRs verhindern, dass Entscheidungen später vergessen oder erneut diskutiert werden.
#Risiko-Register führen
Lege an:
docs/risk-register.md
Vorlage:
# Risk Register
| ID | Risiko | Bereich | Wahrscheinlichkeit | Auswirkung | Gegenmaßnahme | Owner | Status |
|---|---|---|---|---|---|---|---|
| R-001 | SOAP Payment läuft innerhalb DB-TX | Transaktion | hoch | hoch | TX splitten, Idempotency Key | Tech Lead | offen |
| R-002 | InvoiceMDB nicht idempotent | JMS | mittel | hoch | processed_message Tabelle | Backend | in Arbeit |
| R-003 | Audit REQUIRES_NEW Semantik unbekannt | JTA | mittel | mittel | Charakterisierungstest | QA | offen |
| R-004 | JNDI-Konfig driftet zwischen Umgebungen | Runtime | hoch | mittel | Config-Inventar, Smoke Test | Ops | offen |
Bewertung:
Wahrscheinlichkeit: niedrig / mittel / hoch
Auswirkung: niedrig / mittel / hoch
Das Risiko-Register sollte in jedem Modernisierungs-Review aktualisiert werden.
#Change-Kategorien definieren
Nicht jede Änderung braucht denselben Prozess.
| Kategorie | Beispiel | Risiko | Prozess |
|---|---|---|---|
| Safe Structural | Interface einführen, Klasse extrahieren | niedrig | normaler PR |
| Behavior Preserving | Adapter bauen, Handler extrahieren | mittel | Testnachweis nötig |
| Behavior Changing | Payment async, Statusmodell ändern | hoch | Fachfreigabe, Rollback-Plan |
| Runtime Changing | JNDI, Datasource, Queue, Config | hoch | Ops-Freigabe, Smoke Test |
| Contract Changing | SOAP/WSDL, JMS Payload, REST API | sehr hoch | Versionierung, Partnerkommunikation |
| Data Changing | Schema, Backfill, Constraint | hoch | Migration Plan, DB Rollback |
So wird klar, wann eine Änderung nur technisch ist und wann sie fachlich/operativ kritisch wird.
#Release-Strategie für Legacy-Modernisierung
Vermeide seltene Mega-Releases.
Besser:
kleine Releases
klare Slices
Feature Flags
schnelle Rollbacks
stabile Smoke Tests
Mögliche Release-Kadenz:
| Release-Typ | Frequenz | Inhalt |
|---|---|---|
| Refactoring Release | wöchentlich | Struktur, Tests, Logging, keine Verhaltensänderung |
| Slice Release | alle 2–4 Wochen | ein fachlicher Slice modernisiert |
| Platform Release | selten | Java/App Server/Spring/Jakarta Upgrade |
| Data Release | geplant | Schema/Backfill/Constraints |
Regel:
Mische möglichst nicht Platform Upgrade, DB Migration und fachliche Änderung in einem Release.
#Feature Flags für Cutover
Feature Flags helfen, neue Implementierungen kontrolliert zu aktivieren.
Beispiel:
features.order.create.new-core=false
features.payment.outbox=false
features.invoice.order-paid-consumer=false
Code-Idee:
public OrderId createOrder(CreateOrderRequest request) {
if (featureFlags.useNewOrderCore()) {
return newOrderFacade.createOrder(request);
}
return legacyOrderService.createOrder(request);
}
Regeln:
- Flags müssen dokumentiert sein.
- Flags brauchen Owner.
- Flags müssen nach Cutover entfernt werden.
- Flags dürfen keine dauerhafte Architektur werden.
Datei:
docs/feature-flags.md
Vorlage:
| Flag | Zweck | Default | Owner | Entfernen nach |
|---|---|---|---|---|
| features.order.create.new-core | neuer Order Use Case | false | Backend | S-001 stabil |
| features.payment.outbox | Payment über Outbox | false | Backend/Ops | S-002 stabil |
#Parallel Run und Shadow Mode
Bei kritischen Slices kannst du neue Logik parallel ausführen, ohne Ergebnis produktiv zu nutzen.
Produktiver Request
↓
Legacy verarbeitet wirklich
↓
Neue Implementierung berechnet shadow Ergebnis
↓
Vergleich wird geloggt
Beispiel:
public CreateOrderResponse createOrder(CreateOrderRequest request) {
CreateOrderResponse legacyResponse = legacyService.createOrder(request);
if (featureFlags.shadowNewOrderCore()) {
try {
ShadowResult shadowResult = newOrderCore.simulate(request);
comparisonLogger.compare(legacyResponse, shadowResult);
} catch (Exception e) {
log.warn("Shadow execution failed", e);
}
}
return legacyResponse;
}
Shadow Mode eignet sich für:
- Preisberechnung
- Validierungslogik
- Mapping
- Read Models
- SOAP Response Transformation
Vorsicht:
Shadow Mode darf keine echten Side Effects auslösen.
Keine DB Writes, keine JMS Sends, keine SOAP Mutations.
#Cutover-Strategien
Es gibt mehrere Cutover-Formen.
| Strategie | Beschreibung | Geeignet für |
|---|---|---|
| Big Switch | kompletter Wechsel zu Zeitpunkt X | kleine, gut getestete Slices |
| Feature Flag | neue Logik schrittweise aktivieren | Services mit sauberem Routing |
| Canary | kleiner Nutzer-/Traffic-Anteil zuerst | Web/API Slices |
| Parallel Run | Legacy und Neu vergleichen | Berechnungen, Reads, Mappings |
| Strangler | neue Entry Points übernehmen langsam | größere Monolith-Ablösung |
| Blue-Green | neue Umgebung komplett parallel | gut automatisierte Deployments |
Für Legacy Enterprise Java ist oft realistisch:
Feature Flag + Smoke Test + schneller Rollback
Bei sehr kritischen Slices:
Shadow Mode → Feature Flag → Canary → vollständiger Cutover
#Rollback-Strategie pro Slice
Jeder Slice braucht einen Rollback-Plan.
Vorlage:
# Rollback Plan: S-001 Create Order
## Was wird aktiviert?
- Neuer CreateOrderUseCase hinter Feature Flag
- Outbox PaymentRequested optional
## Rollback-Auslöser
- Fehlerrate > 2 %
- Payment UNKNOWN steigt stark
- Outbox Pending älter als 10 Minuten
- fachliche Inkonsistenz in Order Status
## Rollback-Aktion
1. Feature Flag `features.order.create.new-core=false`
2. Deployment nicht zwingend zurückrollen
3. Outbox Publisher pausieren, falls betroffen
4. Pending Events prüfen
5. Fachliche Korrektur nach Runbook
## Daten-Rollback
- Keine destruktive Schemaänderung
- Neue Spalten bleiben bestehen
- Backfill nicht rückgängig nötig
## Kommunikation
- Ops informiert
- Fachbereich informiert
- Incident Channel aktiv
Wichtig:
Rollback ist nicht nur Git revert.
Rollback betrifft Daten, Messages, externe Systeme und Fachzustände.
#Kommunikationsplan
Legacy-Modernisierung betrifft oft mehr Menschen als gedacht.
Stakeholder:
- Fachbereich
- Support
- Operations
- Security
- externe Partner
- QA
- Management
- andere Entwicklungsteams
Für jeden kritischen Slice brauchst du:
| Zielgruppe | Information | Zeitpunkt |
|---|---|---|
| Fachbereich | fachliche Änderung, Testfälle, Cutover | vor Testphase |
| Operations | Deployment, Flags, Monitoring, Rollback | vor Release |
| Support | neue Fehlermeldungen, Runbook | vor Go-Live |
| Security | neue Rollen/Secrets/Endpoints | vor Security Review |
| Partner | SOAP/JMS/API-Vertrag falls betroffen | frühzeitig |
Kommunikation verhindert, dass technische Modernisierung als unerwartete Produktänderung wahrgenommen wird.
#Migrationsmetriken
Du brauchst Metriken für Fortschritt, nicht nur Story Points.
| Metrik | Warum wichtig? |
|---|---|
| Anzahl dokumentierter Entry Points | Sichtbarkeit |
| Anzahl Slices mit Transaction Map | Risikokontrolle |
| Anzahl Slices mit Tests | Änderbarkeit |
| Anzahl EJBs dünner gemacht | Container-Kopplung sinkt |
| Anzahl direkter JNDI-Lookups | Runtime-Kopplung sichtbar |
| Anzahl SOAP Calls innerhalb TX | kritisches Risiko |
| Anzahl nicht-idempotenter MDBs | Messaging-Risiko |
| Outbox Pending/Dead Events | Betriebsfähigkeit |
| Testlaufzeit und Stabilität | CI-Fähigkeit |
| Deployment-Frequenz | Lieferfähigkeit |
Beispiel-Dashboard:
| Bereich | Start | Aktuell | Ziel |
|---|---:|---:|---:|
| Direkte JNDI-Lookups | 87 | 54 | < 10 |
| SOAP innerhalb TX | 14 | 8 | 0 |
| MDBs ohne Idempotenz | 11 | 6 | 0 |
| Slices mit Transaction Map | 0 | 5 | 15 |
| Smoke Tests | 0 | 8 | 20 |
Diese Metriken zeigen echte Modernisierung, nicht nur Codebewegung.
#Roadmap für Complex Slice 001
Für unseren komplexen Order-Payment-Slice sieht eine sinnvolle Roadmap so aus.
#Phase 1: Verstehen
| Aufgabe | Ergebnis |
|---|---|
| Call Chain dokumentieren | create-order.jsp → EJB → DB → SOAP → JMS → MDB |
| Transaction Map erstellen | REQUIRED, REQUIRES_NEW, SOAP innerhalb TX sichtbar |
| Runtime Resources erfassen | Datasource, Queue, SOAP Endpoint, JNDI |
| Security prüfen | Rollen und Principal für Order/Payment |
#Phase 2: Absichern
| Aufgabe | Ergebnis |
|---|---|
| Characterization Test | aktuelles Verhalten dokumentiert |
| Use Case Unit Tests | neuer Plain-Java-Kern testbar |
| JMS Idempotenztest | doppelte Invoice verhindert |
| Smoke Test | Deployment und Hauptflow prüfbar |
#Phase 3: Struktur schaffen
| Aufgabe | Ergebnis |
|---|---|
| CreateOrderCommand einführen | Transport-DTO getrennt |
| CreateOrderUseCase extrahieren | Fachlogik außerhalb EJB |
| Ports definieren | JPA/SOAP/JMS/Audit gekapselt |
| Legacy Adapter bauen | alte Technik hinter Interfaces |
| EJB dünn machen | Container bleibt außen |
#Phase 4: Risiko reduzieren
| Aufgabe | Ergebnis |
|---|---|
| SOAP aus CreateOrder-TX lösen | kürzere DB Locks |
| Payment Statusmodell verbessern | PAYMENT_PENDING/UNKNOWN sichtbar |
| Outbox einführen | zuverlässige Events |
| OrderPaid statt OrderCreated verwenden | Invoice fachlich korrekt ausgelöst |
| Processed Message Tabelle | idempotenter Consumer |
#Phase 5: Zielplattform vorbereiten
| Option | Vorbereitung |
|---|---|
| Spring Boot | UseCase als Bean, @Transactional Facade, @JmsListener |
| Jakarta EE | CDI, JAX-RS, Jakarta Transactions, Jakarta Messaging |
| Quarkus | CDI, RESTEasy, Scheduler, Messaging, Health |
#Phase 6: Cutover
| Schritt | Aktion |
|---|---|
| 1 | Feature Flag für neuen Kern einführen |
| 2 | Shadow Mode für Mapping/Validierung nutzen |
| 3 | kleinen Traffic-Anteil aktivieren, falls möglich |
| 4 | Outbox-Monitoring aktiv prüfen |
| 5 | Rollback-Plan bereithalten |
| 6 | Legacy-Code nach Stabilisierung entfernen |
#Definition of Success
Der Slice ist erfolgreich, wenn:
- CreateOrder fachlich gleich oder bewusst verbessert ist
- Payment nicht mehr innerhalb einer langen DB-TX läuft
- Folgeprozesse über Outbox laufen
- Invoice-Verarbeitung idempotent ist
- Logs/Metrics den Flow sichtbar machen
- Rollback ohne Datenverlust möglich ist
- der neue Kern plattformunabhängig testbar ist
#Zusammenfassung Roadmap, Risiko-Management und Cutover
Dieser Block macht aus technischer Modernisierung ein steuerbares Programm.
Die wichtigsten Punkte:
1. Modernisiere in fachlichen Slices, nicht in Technologieschichten.
2. Beginne mit einem mittleren Slice, nicht mit dem gefährlichsten.
3. Nutze Definition of Ready und Definition of Done.
4. Dokumentiere Architekturentscheidungen als ADRs.
5. Führe ein Risiko-Register.
6. Plane Cutover und Rollback vor dem Go-Live.
7. Nutze Feature Flags, Shadow Mode und kleine Releases.
8. Miss Modernisierungsfortschritt mit technischen Risikometriken.
9. Entferne Legacy erst nach Stabilisierung.
10. Kommunikation ist Teil der Architekturarbeit.
Merksatz:
Eine gute Modernisierung ist nicht die, die am schnellsten neuen Code schreibt.
Eine gute Modernisierung ist die, die kontrolliert Risiko reduziert und lieferfähig bleibt.