REST-Schnittstellen mit klaren Verträgen und Versionsprüfung planen
Eine künstliche Aufgabenressource verbindet erlaubte Eingaben, maschinenlesbare Fehler und konkurrierende Änderungen. Mit vollständigem Node.js-Experiment.

Eine brauchbare REST-Schnittstelle beginnt mit einer verständlichen Vereinbarung: Welchen fachlichen Gegenstand darf ein Client sehen, was darf er ändern und woran erkennt er einen Konflikt? Pfadnamen und JSON allein beantworten diese Fragen nicht. Wir planen eine kleine Aufgabenressource und verfolgen zwei konkurrierende Änderungen bis zur tatsächlichen Serverantwort. Das vollständige Node.js-Beispiel wurde mit künstlichen Daten über echte HTTP-Anfragen ausgeführt. Es ist ein begrenztes Lehrmodell für einen Vertrag, keine fertige öffentlich erreichbare Kundenverwaltung.
Das Ergebnis vor dem ersten Endpunkt festlegen
Unser fiktives Redaktionsteam bearbeitet die Aufgabe mit der Kennung 7. Ihr Titel lautet zunächst „Anfrage prüfen“, ihr Bearbeitungszustand ist offen. Zwei Personen können dieselbe Aufgabe laden. Eine Person erledigt sie, während die andere noch einen alten Entwurf auf dem Bildschirm sieht. Die Schnittstelle soll die zweite Person dann auf den veränderten Stand hinweisen. Sie darf den bereits gespeicherten Titel nicht still durch einen fremden Entwurf ersetzen. Dieses konkrete Ziel macht spätere Entscheidungen überprüfbar.
Vor der technischen Umsetzung schreiben wir deshalb drei Abnahmesätze auf. Lesen verändert den Gegenstand nicht. Eine erlaubte Änderung liefert den danach geltenden Zustand zurück. Eine Änderung mit überholter Versionskennung lässt den Gegenstand unverändert. Hinzu kommt eine Grenze: Die Beispielaufgabe lebt nur im Speicher eines einzelnen Prozesses. Ein Neustart verwirft sie. Eine echte Anwendung braucht zusätzlich dauerhafte Speicherung, Berechtigungen und ein Verfahren, das Versionsprüfung und Änderung auch bei mehreren Prozessen atomar verbindet.
REST und eine praktische HTTP-API sauber unterscheiden
REST bezeichnet bei Roy Fielding einen Architekturstil mit zusammenwirkenden Bedingungen, darunter eine einheitliche Schnittstelle und eine zustandslose Kommunikation. Das bloße Übertragen von JSON über HTTP erfüllt diesen Ansatz nicht automatisch. Insbesondere fehlen vielen gewöhnlich als REST bezeichneten Schnittstellen die steuernden Hypermedialinks. Unser kleines Beispiel ist daher bewusst eine ressourcenorientierte HTTP-API mit einem fest bekannten Endpunkt. Es behauptet keine vollständige Umsetzung des REST-Architekturstils. Diese begriffliche Abgrenzung folgt dem Originalkapitel über REST.
Für die Planung ist die Unterscheidung nützlich. Ein Team kann einen bewusst einfachen Vertrag bauen, ohne seinem System Eigenschaften zuzuschreiben, die es nicht besitzt. Unsere Clients kennen den Aufgabenpfad durch die Dokumentation. Ein späteres System könnte geeignete Folgeaktionen als Links ausliefern. Ob dieser zusätzliche Schritt sinnvoll ist, hängt vom Integrationsbedarf ab. Er ersetzt weder die Beschreibung des Zustandsmodells noch die Prüfung, ob eine Person gerade diese Aufgabe ändern darf.
Den Gegenstand und seine veränderbaren Felder beschreiben
Eine Aufgabe hat in diesem Beispiel vier sichtbare Angaben: Kennung, Titel, Zustand und Revision. Die Kennung bleibt fest. Die Revision erzeugt ausschließlich der Server. Der Client darf beim Ersetzen nur Titel und Zustand schicken. Damit unterscheiden wir ausdrücklich zwischen der Darstellung einer Aufgabe und den erlaubten Eingaben für ihre Änderung. Würde man jedes eingehende JSON-Feld ungeprüft in das gespeicherte Objekt übernehmen, könnte ein Client die Revision selbst setzen oder später ergänzte interne Felder beeinflussen.
Der Titel enthält hier ein bis hundert Unicode-Codepunkte und hat keine äußeren Leerzeichen. Der Zustand ist genau einer von zwei technischen Werten: open oder done. Andere Werte werden nicht geraten und zusätzliche Felder werden abgewiesen. Diese Regeln sind eigene Entscheidungen unseres Lehrvertrags. Sie sind keine universellen Anforderungen an Aufgabenprogramme. Vor allem ist die hundert Zeichen lange Grenze weder eine Datenbankgrenze noch ein Beweis dafür, dass der Text in jeder Oberfläche bequem lesbar bleibt.
| Angabe | Beim Lesen | Beim Ersetzen |
|---|---|---|
| Kennung | 7 | Nur durch den Pfad bestimmt |
| Titel | Aktueller Text | Ein bis hundert Codepunkte ohne Randabstand |
| Zustand | open oder done | Genau einer dieser beiden Werte |
| Revision | Vom Server erzeugte Zahl | Kein erlaubtes Eingabefeld |
Einen kleinen und eindeutig begrenzten Pfad wählen
Der einzige Ressourcenpfad lautet /api/tasks/7. Er benennt den Gegenstand unabhängig von einer Schaltfläche der Oberfläche. Eine Schaltfläche „Erledigen“ muss deshalb keinen neuen Pfad mit ihrem deutschen Namen erzwingen. Sie kann den erlaubten Zustand dieser Aufgabe ändern. Für das Lehrmodell werden eine andere Kennung, eine führende Null und zusätzliche Suchparameter bewusst nicht akzeptiert. Das ist eine enge Auswahl, die wir testen können. Es ist keine Empfehlung, produktive Schnittstellen grundsätzlich ohne Suchparameter zu bauen.
Ein echtes Aufgabenverzeichnis hätte einen eigenen Vertrag für Listen, Filter und Seitenaufteilung. Dort müssten wir zum Beispiel entscheiden, ob eine sortierte Liste während paralleler Änderungen stabil durchlaufen werden kann. Diese zusätzliche Aufgabe wird hier nicht hinter einem scheinbar fertigen Listenendpunkt versteckt. Ebenso fehlen das Anlegen und Löschen von Aufgaben. Ein kleiner dokumentierter Umfang ist für dieses Experiment aussagekräftiger als viele Endpunkte, deren Verhalten bei Fehlern oder Wiederholung offenbleibt.
Methoden nach ihrer Wirkung auswählen
Unser Vertrag erlaubt GET zum Lesen, HEAD für dieselben Antwortmetadaten ohne Antwortinhalt und PUT zum vollständigen Ersetzen der beiden veränderbaren Felder. Andere Methoden erhalten auf diesem bekannten Gegenstand eine Antwort mit 405 und einer Allow-Angabe. Die allgemeinen Eigenschaften sicherer und idempotenter Methoden sowie die starke Prüfung mit If-Match beschreibt RFC 9110. Idempotenz betrifft dabei die beabsichtigte Wirkung, nicht zwingend identische Antworten auf jede Wiederholung.
In unserer eigenen Umsetzung bleibt eine erneute Übermittlung derselben aktuellen Werte ohne neue fachliche Änderung. Wir erzeugen dafür keine zusätzliche Revision. Eine Wiederholung mit einer inzwischen alten Vorbedingung kann dennoch abgewiesen werden. Der Client muss dann den aktuellen Gegenstand lesen und seinen Entwurf vergleichen. Das ist gerade beim verlorenen Antwortpaket wichtig: Aus einer fehlenden Antwort kann er nicht schließen, dass der Server die erste Änderung verworfen hat. Eine zweite Anfrage braucht eine bewusste Wiederherstellungsstrategie.
Antworten als nachvollziehbare Vereinbarung behandeln
Der Server liefert bei erfolgreichem Lesen den aktuellen Gegenstand als JSON und eine Versionskennung im ETag-Header. Bei erfolgreicher Änderung antwortet er ebenfalls mit dem aktuellen Gegenstand. So muss die Oberfläche nicht aus ihrem eigenen alten Eingabeformular ableiten, was der Server nun tatsächlich gespeichert hat. Im Experiment beginnt die Kennung mit "task-7-r1". Nach der Änderung des Zustands entsteht "task-7-r2". Diese Zeichenfolge ist undurchsichtig zu behandeln. Clients sollen sie zurückschicken und nicht selbst hochzählen.
Alle Antworten des Lehrservers tragen Cache-Control: no-store. Damit wird für diesen Versuch kein wiederverwendbarer Cachevertrag eingeführt. Der ETag dient hier zur Versionsprüfung beim Schreiben. Ein späteres Cachekonzept müsste gesondert unter anderem die Auswahl von Darstellungen und die Behandlung bedingter Leseanfragen festlegen. Dieses Beispiel implementiert keine allgemeine HTTP-Cachemaschine und keine vollständige Verarbeitung aller möglichen Vorbedingungsheader. Seine überschaubare Oberfläche ist die Grundlage seiner überprüfbaren Aussagen.

Verlorene Änderungen an einem konkreten Ablauf erkennen
Zwei Clients lesen zunächst dieselbe Revision. Client A sendet anschließend den unveränderten Titel mit dem Zustand erledigt und der gelesenen Kennung. Die Änderung gelingt. Client B sendet danach einen abweichenden Titel mit derselben alten Kennung. Der Server weist diesen Versuch mit 412 ab. In unserem tatsächlichen Versuch bleibt „Anfrage prüfen“ erhalten und der Zustand bleibt erledigt. Die Abweisung ist deshalb mehr als eine hübsche Meldung. Sie ist durch den danach erneut untersuchten Zustand belegt.
Fehlt die erforderliche Vorbedingung ganz, antwortet der Lehrserver mit 428 und erklärt, wie der Client zunächst lesen und die Kennung mitsenden kann. Der zusätzliche Status ist in RFC 6585, Abschnitt 3 definiert. Eine Stern-Vorbedingung prüft hingegen nur die Existenz des Gegenstands. Unser Modell akzeptiert sie entsprechend, aber sie beweist keinen bestimmten gelesenen Stand. Ein Client, der Überschreiben vermeiden will, verwendet die konkrete erhaltene Kennung.
Fehler in drei unterschiedliche Fragen zerlegen
Bei einem Fehler soll der Client unterscheiden können: Hat er die Übertragung falsch beschrieben, ist der Inhalt fachlich ungeeignet oder passt seine Kenntnis des Gegenstands nicht mehr? Im eigenen Vertrag führen falscher Medientyp, ungültiges JSON und überholte Revision deshalb zu unterschiedlichen Antworten. Die Statusübersicht von MDN bietet die allgemeine Einordnung. Die Auswahl und Reihenfolge der hier tatsächlich geprüften Fälle gehört zu unserem Beispiel.
Die Eingangsgrenze beträgt 4096 Bytes. Das ist eine Übertragungsgrenze und von den hundert Codepunkten im Titel getrennt. Ein langes JSON-Dokument kann die erste Grenze überschreiten, obwohl sein sichtbarer Titel kurz wäre. Umgekehrt kann eine kleine Anfrage einen nicht erlaubten Zustand enthalten. Fehler bei diesen beiden Prüfungen sollten nicht zusammenfallen. Die Grenze ist absichtlich klein gewählt, weil der Lehrfall nur zwei Eingabefelder benötigt. Für echte Dateien oder große Sammlungen wäre ein anderer Vertrag nötig.
| Tatsächlich gesendeter Fall | Antwort | Auswirkung |
|---|---|---|
| Unbekannter Pfad | 404 | Keine Änderung |
| POST auf die bekannte Aufgabe | 405 und Allow | Keine Änderung |
| Kein vereinbarter JSON-Medientyp | 415 | Keine Änderung |
| Ungültiges JSON | 400 | Keine Änderung |
| Zusätzliches Revisionsfeld | 422 | Keine Änderung |
| Fehlende Versionsvorbedingung | 428 | Keine Änderung |
| Überholte Versionskennung | 412 | Neueren Stand erhalten |
Maschinenlesbare Probleme ohne interne Details liefern
Die Fehlerantwort enthält einen Typ, eine kurze Statusbezeichnung, den konkreten numerischen HTTP-Status und eine konkrete Erklärung. Als Medientyp verwenden wir application/problem+json. Der allgemeine Aufbau folgt RFC 9457. Das Feld status enthält dabei den konkreten HTTP-Statuscode und muss zur tatsächlichen Antwort passen. Eine Oberfläche soll aus der fachlichen Erklärung einen passenden nächsten Schritt entwickeln können, ohne interne Datenbanknamen oder einen Stacktrace auszuwerten.
Unser Beispiel benutzt den allgemeinen Problemtyp about:blank. Für ein umfangreiches Produkt könnten dokumentierte eigene Problemtypen sinnvoll sein. Die stabile Maschinenentscheidung sollte dann an einem vereinbarten Typ oder Fehlercode hängen, nicht am genauen deutschen Satz. Sonst würde eine redaktionelle Verbesserung der Fehlermeldung unerwartet ein Clientprogramm verändern. Sensible Eingaben gehören auch nicht automatisch vollständig in die Erklärung. Für die Aufgabe reicht der Hinweis auf das erlaubte Format, ohne fremde Inhalte zurückzuspiegeln.
Das vollständige Experiment ausführen
Die folgende Datei läuft mit dem eingebauten HTTP-Modul von Node.js. Der direkte Start bindet ausschließlich an die Loopbackadresse. Sie enthält weder Zugangsdaten noch eine Verbindung zu einem Fremdsystem. Alle Änderungen betreffen die eine künstliche Speicheraufgabe. Die Node.js-Dokumentation beschreibt den verwendeten Serverbaustein. Ausgeführt wurde dieses konkrete Programm mit Node.js 24.22.1. Die verlinkte aktuelle Dokumentation kann eine neuere Laufzeitversion zeigen.
Speichere den Inhalt als api.mjs und starte ihn mit node api.mjs. Nach dem Lesen kann ein eigener Client die beiden veränderbaren Felder samt If-Match senden. Eine öffentlich erreichbare Freigabe des Lehrservers wäre kein sinnvoller nächster Schritt. Für einen realen Dienst muss die Anwendung zuerst die noch fehlenden Betriebs- und Berechtigungsentscheidungen schließen. Das Experiment erklärt bewusst den Vertrag und seine beobachteten Antworten, nicht einen fertig abgesicherten Dienst.
import {createServer,STATUS_CODES} from 'node:http';
import {pathToFileURL} from 'node:url';
export function makeDemo(){
let task={id:7,title:'Anfrage prüfen',state:'open',revision:1};
const tag=()=>`"task-7-r${task.revision}"`;
const server=createServer(async(req,res)=>{
const send=(status,value,headers={})=>{
const body=JSON.stringify(value);
res.writeHead(status,{'Content-Type':'application/json; charset=utf-8','Cache-Control':'no-store','Content-Length':Buffer.byteLength(body),...headers});
res.end(req.method==='HEAD'?'':body);
};
const problem=(status,detail,headers={})=>send(status,{type:'about:blank',title:STATUS_CODES[status],status,detail},{'Content-Type':'application/problem+json',...headers});
try{
if(req.url!=='/api/tasks/7')return problem(404,'Diese Beispielressource existiert nicht.');
if(!['GET','HEAD','PUT'].includes(req.method))return problem(405,'Diese Methode ist hier nicht vorgesehen.',{'Allow':'GET, HEAD, PUT'});
if(req.method!=='PUT')return send(200,task,{'ETag':tag()});
if(!/^application\/json(?:\s*;\s*charset=utf-8)?$/i.test(req.headers['content-type']??''))return problem(415,'JSON mit UTF-8 verwenden.');
let size=0;const chunks=[];
for await(const chunk of req){size+=chunk.length;if(size>4096)return problem(413,'Maximal 4096 Bytes senden.');chunks.push(chunk);}
let input;
try{input=JSON.parse(new TextDecoder('utf-8',{fatal:true}).decode(Buffer.concat(chunks)));}catch{return problem(400,'Der Inhalt muss gültiges UTF-8 und JSON sein.');}
if(!input||typeof input!=='object'||Array.isArray(input)||Object.keys(input).sort().join(',')!=='state,title'||typeof input.title!=='string'||input.title.trim()!==input.title||[...input.title].length<1||[...input.title].length>100||!['open','done'].includes(input.state))return problem(422,'Genau title und state im vereinbarten Format senden.');
const condition=req.headers['if-match'];
if(condition===undefined)return problem(428,'Zuerst lesen und den erhaltenen ETag als If-Match mitsenden.');
if(condition!=='*'&&!/^(?:W\/)?"[\x21\x23-\x7e\x80-\xff]*"(?:\s*,\s*(?:W\/)?"[\x21\x23-\x7e\x80-\xff]*")*$/.test(condition))return problem(400,'If-Match muss eine gültige Liste von Entity-Tags oder * sein.');
const candidates=condition.match(/(?:W\/)?"[^\"]*"/g)??[];
if(condition!=='*'&&!candidates.includes(tag()))return problem(412,'Der gelesene Stand ist nicht mehr aktuell. Erneut lesen und Änderungen vergleichen.');
// No await between revision check and update in this one-process fixture.
if(input.title!==task.title||input.state!==task.state)task={...task,...input,revision:task.revision+1};
return send(200,task,{'ETag':tag()});
}catch(error){console.error(error);if(!res.headersSent)problem(500,'Die Anfrage konnte nicht verarbeitet werden.');else res.destroy();}
});
server.requestTimeout=5000;server.headersTimeout=5000;
return {server,snapshot:()=>structuredClone(task)};
}
if(process.argv[1]&&import.meta.url===pathToFileURL(process.argv[1]).href){const {server}=makeDemo();server.listen(8787,'127.0.0.1',()=>console.log('http://127.0.0.1:8787/api/tasks/7'));}
Was die ausgeführten Prüfungen tatsächlich zeigen
Unser Prüfprogramm schickte 32 echte HTTP-Anfragen über eine dynamische Loopbackadresse und erfüllte 93 einzeln geprüfte Bedingungen. Es verglich unter anderem Antwortstatus, Medientyp, ETag, Allow-Angabe, leere HEAD-Antwort und den danach im Speicher liegenden Gegenstand. Ungültiges UTF-8 wurde tatsächlich gesendet. Auch eine syntaktisch gültige Liste von Entity-Tags mit einem Komma innerhalb eines Tags wurde verarbeitet. Eine schwache Kennung genügte der erforderlichen starken Übereinstimmung nicht.
Bei zwei gleichzeitig gestarteten Schreibanfragen mit derselben aktuellen Kennung gelang genau eine Änderung. Die zweite erhielt 412. Diese Beobachtung gilt für unseren einen Node-Prozess, weil zwischen Versionsvergleich und Änderung kein weiterer asynchroner Schritt liegt. Sie belegt keine Mehrprozesssicherheit. In einer Datenbankanwendung müsste dieselbe fachliche Bedingung atomar mit dem Schreiben ausgewertet werden. Ein zuvor ausgeführtes SELECT und ein späteres unbedingtes UPDATE könnten die gewonnene Sicherheit wieder verlieren.
Beschreibung, Sicherheit und Betrieb gemeinsam abnehmen
Für eine Übergabe lässt sich der Vertrag beispielsweise mit einer fest gewählten OpenAPI-Version beschreiben. Die OpenAPI-Spezifikation 3.1.1 ist hier eine bewusst benannte Fassung und kein Anspruch auf die jeweils neueste Ausgabe. Zu dokumentieren wären Pfad, Methoden, Eingabeschema, erlaubte Zustände, Header und Antworten. Eine lesbare Beschreibung ersetzt jedoch keine ausgeführten Gegenproben. Besonders wichtig ist, dass das Eingabeschema keine vom Server erzeugte Revision als beschreibbares Feld ausweist.
Die OWASP-Empfehlungen für REST-Sicherheit behandeln unter anderem HTTPS, Zugriffskontrolle, Eingabeprüfung und Medientypen. Unser Lehrserver ergänzt diese Aufgaben nicht heimlich durch einen Beispielschlüssel. Für einen realen Mandantenbetrieb brauchen wir eine überprüfte Identität und eine Berechtigung pro Gegenstand. Ein bekanntes Aufgabenkennzeichen ist keine Erlaubnis. Ebenso ist CORS keine Anmeldung und eine Versionskennung kein Geheimnis, das einen Zugriff rechtfertigt.
- Fachlichen Gegenstand und erlaubte Übergänge schriftlich festlegen.
- Lesbare Darstellung von veränderbaren Eingaben trennen.
- Methoden, Pfade, Grenzen und Fehlerfälle mit echten Beispielen dokumentieren.
- Überholte und fehlende Vorbedingungen getrennt prüfen.
- Wiederholungen nach unbekanntem Ausgang bewusst behandeln.
- Versionsprüfung und dauerhafte Änderung atomar verbinden.
- Objektberechtigung, Protokollierung und Betriebsgrenzen separat abnehmen.
- Client und Server bei Vertragsänderungen mit Gegenproben vergleichen.
Eine sinnvolle nächste Ausbaustufe wäre deshalb kein weiterer dekorativer Endpunkt. Wir würden die gleiche Aufgabe dauerhaft speichern, die erlaubte Person prüfen und genau denselben Konflikt erneut provozieren. Erst wenn dabei alter Entwurf und neuer Gegenstand sicher unterscheidbar bleiben, ist die ursprüngliche Anforderung erfüllt. Die Qualität der Schnittstelle zeigt sich daran, dass ein Client einen unerwarteten Zustand verstehen und kontrolliert weiterarbeiten kann.
Den Konflikt in der Oberfläche wirklich auflösen
Eine Antwort mit 412 sollte den alten Entwurf nicht löschen. Für unsere fiktive Aufgabe kann die Oberfläche drei Fassungen getrennt halten: den ursprünglich gelesenen Zustand, den eigenen noch nicht gespeicherten Entwurf und die gerade erneut gelesene Serverfassung. Nur so sieht die bearbeitende Person, ob sich derselbe Titel geändert hat oder lediglich der Bearbeitungszustand. Eine automatisch neu gesetzte Versionskennung mit anschließendem blindem Wiederholen würde die eigentliche Schutzwirkung umgehen. Sie macht aus einer erkannten Kollision wieder ein stilles Überschreiben.
Ein konkreter Vergleich hilft mehr als die allgemeine Meldung „Speichern fehlgeschlagen“. Im Experiment hatte die zweite Person einen fremden Titel vorbereitet, während der erste Vorgang den Zustand erledigt gesetzt hatte. Die Oberfläche kann beides sichtbar nebeneinander anbieten. Die Person entscheidet dann, welche Felder noch übernommen werden sollen. Für eine neu zusammengestellte Fassung wird der zuletzt gelesene ETag verwendet. Auch dieser zweite Versuch kann erneut kollidieren. Der Ablauf muss deshalb wiederholbar bleiben und darf nicht nach genau einem Konflikt seine Sicherheitsregel ausschalten.
Unbekannten Ausgang und bestätigten Fehler auseinanderhalten
Ein empfangener Fehlerstatus ist etwas anderes als eine unterbrochene Verbindung ohne auswertbare Antwort. Im ersten Fall hat unser Client eine konkrete Aussage des Dienstes. Im zweiten Fall fehlt ihm diese Aussage, auch wenn die Änderung bereits erfolgt sein könnte. Für die Aufgabe kann er nach erneutem Lesen prüfen, ob seine beabsichtigten Werte sichtbar sind. Ob diese Übereinstimmung für einen Geschäftsvorgang als Nachweis reicht, hängt jedoch vom Gegenstand ab. Ein Aufgabenstatus ist nicht mit einer Zahlung oder einer rechtsverbindlichen Freigabe gleichzusetzen.
Die Oberfläche sollte deshalb keine Erfolgsmeldung allein daraus ableiten, dass sie die Anfrage abgesendet hat. Sie braucht eine erfolgreiche passende Antwort oder einen vertraglich ausreichenden späteren Nachweis. Ebenso wenig soll sie nach einer Verbindungsstörung behaupten, es sei sicher nichts geschehen. Diese Trennung gehört in die Abnahme des Clients. Wir würden gezielt eine Antwort nach einer vorgenommenen Änderung unterbrechen und anschließend beobachten, welche Anzeige und welche Wiederholungsentscheidung daraus entstehen. Der vorliegende Prüfbericht enthält diesen Netzabbruchversuch nicht, weshalb er ihn auch nicht als bestanden ausweist.
Änderungen am Vertrag ohne stille Bedeutungswechsel einführen
Angenommen, später kommt der Zustand „wartet auf Rückfrage“ hinzu. Dann müssen wir nicht nur die Serverprüfung ergänzen. Ein bestehender Client könnte nur zwei Zustände darstellen und den unbekannten Wert fälschlich als offen interpretieren. Für die Übergabe brauchen wir deshalb eine dokumentierte Reaktion auf unbekannte Ausgabewerte. Ein zusätzlicher Pflichtwert beim Schreiben wäre eine andere Änderung: Alte Clients können ihn nicht liefern. Die Entscheidung über eine neue Vertragsfassung sollte diese konkreten Auswirkungen betrachten und nicht nur fragen, ob die JSON-Datei weiterhin syntaktisch gültig ist.
Unser enger Eingabevertrag weist zusätzliche Felder absichtlich zurück. Das erleichtert die Suche nach Schreibfehlern, erfordert bei Erweiterungen aber eine abgestimmte Einführung. Ein Team kann neue optionale Angaben zuerst serverseitig akzeptieren und erst danach sendende Clients ausrollen. Die Reihenfolge und die Rücknahmebedingungen gehören in denselben Änderungsplan. Für das Aufgabenbeispiel würden wir außerdem die bisherigen Gegenproben weiter ausführen. Ein neuer Zustand darf nicht versehentlich ermöglichen, dass eine vom Client gelieferte Revision die serverseitige Versionskontrolle überschreibt.







