Playbook · Case Study TINA

Legacy → Angular: Frontend-Migration durch KI-Agenten Legacy → Angular:
Frontend-Migration durch KI-Agenten

Ein übertragbares Rezept, wie eine KI-Agenten-Session ein komplettes Legacy-Frontend (~4.500 LOC Aurelia/VoltoSPA, ~1 % Testabdeckung) selbständig in eine Angular-21-App mit ~90 % Coverage portiert — inklusive der Härtefälle, die wirklich wehtun.

Angular 21 (Signals, zoneless) · PrimeNG 21 · Tailwind 4 · Node 22 — ersetzt Aurelia/VoltoSPA, Webpack 4, Node 11 · Zahlen-Stand 2026-07-05

Die Case Study in Zahlen

Bereiche · Views
12/40
Legacy-Inventar
LOC Legacy
~4.500
Aurelia + VoltoSPA · Webpack 4 · Node 11
Bereiche portiert
10/12
Stand 2026-07-05
Line-Coverage
89,7%
Gate: 80 % bricht den Build
Vitest-Tests
287+
+ 60+ Playwright-e2e
ESLint-Findings
0
strict · Maven verify grün

Portierungsstand

Donut-Diagramm: 10 von 12 Legacy-Bereichen portiert, 2 Bereiche offen
10 von 12 Bereichen portiert — contractors und admin sind per User-Entscheidung vertagt.

10/12 in drei Tagen

Die Reihenfolge macht den Unterschied: Quick Wins zuerst härten die Muster, der größte Brocken (stammdaten) kommt zuletzt, wenn das Rezept sitzt. contractors und admin sind bewusst vertagt — der User entscheidet, ob sie überhaupt portiert werden.

10 Bereiche portiert 2 vertagt (User-Entscheidung)

Der Umfang

Treemap: LOC je Legacy-Bereich — stammdaten 1.602, auftragsuebersicht 1.160, produkttypen 463, betriebstypzuordnungen 365, zuweisungsgruppen 296, communication 133, reports 120, produktuebersicht 103, maildomaene 102, admin/contractors/home 112
Fläche = LOC im Legacy-Frontend (Aurelia/VoltoSPA). stammdaten ist der größte Brocken — deshalb wandert er ans Ende des Portierungs-Rezepts.

Der Agent-Loop

Flowchart: Portierungs-Loop vom Bereich-Wählen über Legacy-Lesen, Feature-Bau, UI, Tests, Definition-of-Done-Gate bis zum User-Review — gestrichelt der Fix-Loop zurück zum Legacy-Lesen
Der Loop, mit dem Bereich für Bereich portiert wurde — solide Kanten sind Agenten-Schritte, die gestrichelte Kante ist der Fix-Loop nach dem User-Review.

Die vier Phasen

Zeitachse der Migration: Shell steht, Phase 0 Design-Gate, Phase 1 Fundament, Phase 2 Meilenstein, Phase 3 Rezept-Portierung
Drei Tage vom Design-Gate bis zum Rezept — die vier Phasen im Überblick.
  1. Phase 0 · 2026-07-03

    Design-Gate — vor jeder View-Portierung

    Zwei wirklich unterschiedliche, klickbare HTML-Mockups bauen; der User wählt, die Entscheidung wird protokolliert; erst danach werden Theme und Views umgesetzt. Nicht-visuelle Fundamentarbeit darf parallel laufen.

    Proposal A/B als Mockups User wählte B „Editorial" Gate protokolliert

  2. Phase 1 · 2026-07-03

    Fundament vor Features

    Vitest mit Coverage-Gate (80 % Lines) im Maven-Build · ESLint strict · Playwright mock-first (page.route auf /rest/**, Auth-Bypass über Config-Mock, optionales @smoke-Set) · NSwag-Client aus OpenAPI (generiert = nie editieren, coverage-/lint-exkludiert) · Core-Services (Notifications, Error-Interceptor, Loading, Downloads, Dates) · Shared-UI (TinaTable, FilterBar, ConfirmService, PageHeader, EmptyState).

    Vitest 47/47 · 98 % Lines Playwright 8/8 NSwag + experimentalDecorators

  3. Phase 2 · 2026-07-03

    Ein End-to-End-Meilenstein

    Genau einen komplexen Bereich komplett fertig bauen — Liste, Detail, Wizards, Aktionen, Export — inklusive Definition of Done. Das etabliert Muster und DoD für alles Weitere.

    Auftragsübersicht Ende-zu-Ende DoD als Muster für alle Bereiche

  4. Phase 3 · 2026-07-03–05

    Portierungs-Rezept

    Reihenfolge: Quick Wins zuerst, Muster wiederholen, der größte Brocken zuletzt. Je Bereich eine feste Checkliste: Legacy lesen → Gerüst → Data-Layer → UI → Texte → Tests → DoD → abhaken.

    reports → maildomaene → btmappings → zwg produkttypen → produktuebersicht → communication stammdaten + netzsegmente

Agentic Practice

Sequenzdiagramm: Agent liest Legacy-Code, lässt Tests und Lint laufen, meldet testbaren Zwischenstand an den User, User reviewt und entscheidet
Das Interaktionsprotokoll: lesen, prüfen, melden, abnehmen — jede Unklarheit geht zuerst zum User.
USER-GATES

Design-Auswahl, NSwag-Generierung, Scope-Entscheidungen: Der Mensch entscheidet, der Agent baut. Das Design-Gate liegt vor jeder View-Portierung.

ZWISCHENSTÄNDE

Nach jedem Bereich ein Meldepunkt an den User, manuelle Validierung am Dev-Server — statt einer großen Enthüllung am Ende.

KEIN GIT

Commits macht der User. Das hält die Kontrolle über den Verlauf dort, wo sie hingehört.

COVERAGE-GATE

Das Legacy-Frontend hatte ~1 % Tests. Das Sicherheitsnetz entsteht in der neuen App: 80 % Line-Coverage, sonst bricht der Build.

MOCK-FIRST-E2E

Playwright mit page.route auf alle /rest/**-Calls: deterministisch ohne Backend; ein optionales @smoke-Set läuft gegen echtes BFF + Keycloak.

GUARDRAILS

13 prüfbare Regeln, u. a. Standalone + Signals, kein any, Smart/Dumb-Trennung, Dateigrößen-Limits — nachprüfbar, nicht nur Appelle.

DEFINITION OF DONE

Je Bereich inklusive fachlicher Paritäts-Checkliste gegen die Legacy-Views — „fertig" heißt funktionsgleich, getestet und abgenommen.

DOKU-HYGIENE

Plan-Checkliste, Zwischenberichte, Session-Memory. Lektion: Drei Bereiche waren codiert, aber nirgends verzeichnet — die Inventur gegen das Legacy-Verzeichnis brachte es ans Licht.

ERST FRAGEN

Bei jeder Unklarheit stoppen und den User fragen, statt zu raten — billiger als ein falsch geratenes Verhalten.

Coding Hardness — die Härtefälle, die wirklich wehtun

  1. 1 Falle

    Blob-Falle im Error-Interceptor. NSwag requestet alles als blob — error.error war ein Blob, nie ein TinaErrorDTO. Die Unit-Tests waren grün, weil sie JSON direkt flushten. Fix: Blob-Body parsen und die causes (ErrorHelper) mit anzeigen.

  2. 2 Backend

    Backend-NPE-Pflichten. Sortierung und Sortierfeld sind Pflicht mit exakten Werten ("Descending", nicht DESC) — sonst NullPointerException im Backend. Solche Pflichten stehen nur im Legacy-Code, nicht in der OpenAPI.

  3. 3 Falle

    Plain-Object-Falle. Suchergebnis-Items sind Plain Objects — ohne DTO.fromJS()-Normalisierung crasht toJSON() am verschachtelten Vertragspartner. Generierte Clients sind keine Garantie.

  4. 4 Lint

    Native-Event-Kollisionen. Outputs wie search oder toggle kollidieren mit nativen DOM-Events (ESLint no-output-native) — searchChange und zelleToggle waren die Lösung.

  5. 5 Builder

    vi.mock-Hoisting vs. Angular-Builder. vi.mock-Factories brechen unter dem Builder (Hoisting) — die Lösung war ein DI-Token (AUTO_LOGIN_GUARD) statt Mock-Factories.

  6. 6 zoneless

    zoneless-Falle. computed() auf Formularwerten ist nicht reaktiv — Ableitungen laufen über toSignal(form.valueChanges) und werden als Inputs gereicht.

  7. 7 Tooling

    Playwright-Fallen. npm schluckt -- (webServer-Command braucht npx ng serve --port 4300), und workers: 1 verhindert parallele goto-Timeouts gegen den lokalen Dev-Server.