API-Evolution (Framework-Leitfaden)
Wie Handler, Schemas und Events sich weiterentwickeln koennen, ohne in Versions-Zoo zu verfallen. Kumiko liefert Client und Server zusammen aus — die meisten “klassischen” API-Versionierungs-Probleme treten gar nicht auf. Was uebrig bleibt ist schlank.
Grundannahme
Kumiko-Apps shippen Client + Server zusammen. Die Mobile-App gehoert zur gleichen Deployment-Einheit wie der Server. Damit:
- Es gibt keine externen API-Clients die das Framework bedienen muss
- Wenn jemand eine externe API will, baut er die als eigenes Feature — mit seinen eigenen Versions-Regeln. Nicht Aufgabe des Frameworks.
- Die verbleibenden Evolution-Probleme sind interne: Refactoring, Aufraeumen, Long-Tail-Updates.
Dieses Dokument beschreibt die fuenf Werkzeuge die fuer genau diese internen Probleme Sinn machen. Nicht mehr.
1. Additive Aenderungen — ohne Ceremony
Der Regelfall. 95% der Schema-Aenderungen sind additiv:
// Vorherr.writeHandler("order.create", z.object({ customerName: z.string() }), ...);
// Nachher — einfach Feld dazur.writeHandler("order.create", z.object({ customerName: z.string(), notes: z.string().optional(), // NEU}), ...);Was additiv ist:
- Neues optionales Feld im Input
- Neues Feld im Output (Client ignoriert unbekannte Felder)
- Neuer Handler
- Neue Entity
- Neues Event
- Erweiterte Validierung die bisherige gueltige Werte weiterhin akzeptiert
Kein Marker, keine Version, keine Migration. Framework erkennt additive Aenderungen als ok.
2. Boot-Time Breaking-Change-Detection (opt-in Sicherheitsnetz)
Fuer Apps die einen Baseline-Schnappschuss ihrer API pflegen:
schemaBaseline: "schema-baseline.json",Beim Boot vergleicht das Framework das aktuelle Registry-Schema mit der Baseline-Datei:
| Diff | Reaktion |
|---|---|
| Additive Aenderung | Info-Log, Baseline-Update-Hinweis |
| Breaking Aenderung | Boot-Fehler mit Liste was genau breaking ist |
Beispiel-Output:
BREAKING CHANGES detected vs schema-baseline.json: order.create: - Field "customerName" removed (was required) - Field "customer.name" is now required (was optional)
If these changes are intentional: 1. Update affected clients (or use Deprecation-Marker for old field) 2. Regenerate baseline: yarn kumiko schema baseline-updateNutzen: im CI sichtbar, bevor Code-Review fertig ist. Entwickler sieht “ups, das wollte ich nicht breaking machen”.
Opt-in — nicht jedes Projekt will das pflegen.
3. Deprecation-Marker
Fuer den Fall wo du etwas aufraeumen willst, aber nicht sofort wegwerfen:
r.writeHandler("order.create", schema, handler, { deprecated: { since: "2026-04-01", until: "2026-10-01", // geplantes Entfern-Datum replacedBy: "order.submit", // optional, fuer Report reason: "Umbenennung zu konsistenter Nomenklatur", },});Wirkung:
- Response-Header
X-Kumiko-Deprecated: true; until=2026-10-01; replacedBy=order.submit - Log-Warning bei jedem Aufruf (rate-limited, nicht pro Request)
- Report-Handler
meta.deprecated.usage:
[ { handler: "order.create", lastUsed: "2026-04-14T10:00:00Z", calls7d: 142, distinctUsers: 12, until: "2026-10-01" }, ...]→ Operator sieht am Stichtag “wer nutzt das noch” und entscheidet ob’s jetzt wirklich weg kann.
Der Handler wird einfach entfernt wenn das Datum da ist. Kein v1/v2-Parallelbetrieb noetig.
4. Min-Client-Version-Gate — Hebel fuer Long-Tail
Nicht fuer “frisch deployed und alte Apps aussperren”, sondern fuer “nach 6 Monaten koennen wir endlich v1-Support droppen”.
clientVersions: { minimum: "1.5.0", // strikter Cut-Off recommended: "2.0.0", // Warning-Hinweis}Flow:
Client sendet Header: X-Client-Version: 1.3.0Server: 426 Upgrade Required Body: { minVersion: "1.5.0", currentVersion: "1.3.0", reason: "..." } → Client-UI zeigt Update-Screen
Client sendet Header: X-Client-Version: 1.7.0Server: OK Response-Header: X-Kumiko-Client-Status: outdated-warning; recommended=2.0.0 → Client-UI zeigt Banner "Update verfuegbar"
Client sendet Header: X-Client-Version: 2.1.0Server: OK, keine WarnungenEinsatz-Szenarien (alle Monate nach initialer Aenderung):
- “6 Monate sind rum, v1 droppen” → Gate umlegen
- “v1.2 hat einen Bug, Mindestversion v1.3” → Gate
- “Compliance verlangt neue UI → Mindestversion v2.0”
Ohne Mobile-App braucht’s das Gate gar nicht — Web-App kriegt den Refresh bei jedem Deploy.
5. Event-Schema-Version in Outbox
Das einzige Framework-interne Cross-Version-Szenario: Outbox-Events haben Zeit-Puffer. Ein Event kann 5 Minuten (oder laenger bei Dead-Letter) in der Outbox liegen, bevor ein Subscriber es verarbeitet. Wenn dazwischen deployed wurde, kann Subscriber v2 sein und das Event noch v1-Format.
Loesung: jeder Event-Payload traegt schemaVersion:
{ "type": "order.created", "schemaVersion": 1, "data": { "orderId": "...", "customerName": "..." }}Subscriber koennen:
- Additiv-kompatibel: unbekannte Felder ignorieren, alte Version akzeptieren
- Strict: bei Unterschied ablehnen → Event geht ins Dead-Letter, Operator-Entscheidung
Default-Verhalten: Framework-Subscriber sind additiv-tolerant. Feature-Autor kann strict setzen wenn wichtig.
Bump der schemaVersion bei wirklich breaking-Changes am Event-Payload. Additive Aenderungen: gleiche Version, Subscriber ignoriert neue Felder.
Was NICHT im Scope ist
- Parallel-Versionen im selben Handler-Namen (
order.createv1 + v2 gleichzeitig). Wenn wirklich mal gebraucht: zwei Handler mit verschiedenen Namen (order.create+order.submit). Kein Framework-Magic noetig. - Schema-Auto-Migration beim Request (v1-Request wird zu v2 transformiert). Zu komplex, Fehlerquelle, praktisch ueberfluessig.
- Auto-Version-Negotiation via Accept-Header / URL-Segment. Gibt’s in externen-API-Frameworks — Kumiko braucht’s nicht, weil keine externen Clients.
- Externe-API-Versionierung ueberhaupt. Wenn eine Kumiko-App eine externe HTTP/REST/GraphQL-API anbieten will: eigenes Feature, mit eigener Versions-Logik. Nicht Framework-Aufgabe.
Beispiel: Typischer Deprecation-Zyklus
Woche 0: Umbenennung beschlossen. order.create wird zu order.submit. - order.submit neu geschrieben (Kopie + Aufraeumen) - order.create bekommt deprecated-Marker: until "in 6 Monaten" - Client-Code nutzt bereits order.submit fuer alle neuen Aufrufe - order.create bleibt voll funktional
Woche 4: Mobile-App v2.0 released, nutzt order.submit - Adoption laeuft, User updaten
Woche 20: Report zeigt: 98% nutzen order.submit, nur 12 Aufrufe/Woche zu order.create - Entscheidung: "duerfte egal sein" ODER "lassen wir noch 4 Wochen"
Woche 24: order.create entfernt - Client auf v1.x sendet → 404, weiss aber schon dass er veraltet ist via X-Kumiko-Client-Status - Min-Version-Gate optional auf v2.0 gesetzt: alle verbliebenen User kriegen Update-PromptKein Parallel-Betrieb, keine Versions-Zoo, keine Migration. Nur: additiv, deprecated markieren, warten, wegnehmen.
Framework-Ausbau
| Ausbau | Warum |
|---|---|
deprecated-Option auf Handler-Definition | Marker-System |
| Response-Header-Injection bei deprecated-Call | Client-seitige Sichtbarkeit |
Report-Handler meta.deprecated.usage | Operator-Sicht |
| Breaking-Change-Detection + Baseline-Tool | Dev-Sicherheitsnetz (opt-in) |
X-Client-Version-Header-Parsing + Gate-Middleware | Min-Version-Gate |
Event-Payload-Wrapper mit schemaVersion | Outbox-Kompatibilitaet |
CLI: yarn kumiko schema baseline-update | Dev-Flow |
Alles klein, additiv zum bestehenden Framework.
Tests
Unit / Integration
- Additive Schema-Aenderung → gruen im Baseline-Check
- Breaking-Change → Boot-Fehler mit klarer Message
- Deprecated-Handler-Call → Response hat
X-Kumiko-Deprecated-Header - Deprecated-Report zeigt letzten Aufruf, Count, User-Anzahl
- Client mit Version unter Min → 426 mit strukturierter Response
- Client mit Version zwischen Min und Recommended → 200 mit Warning-Header
- Event mit
schemaVersion=1landet in Subscriber der v2 erwartet — additiv-tolerant geht durch, strict landet im Dead-Letter
Sample
Sample demonstriert Deprecation-Zyklus: altes Feld markieren, Report ansehen, nach Ablauf entfernen.
Build-Reihenfolge
deprecated-Option auf Handler + Response-Header- Deprecation-Report-Handler
- Schema-Baseline-Tool + Boot-Check (opt-in Config)
X-Client-Version-Header + Gate-Middleware- Event-Payload-Versioning
- Docs + Sample-Zyklus