API-Änderungen

Webhook-Changelogs: Der Breaking Change, den niemand wollte

5 Min. Lesezeit

Ein REST-API-Changelog existiert, weil ein Aufrufer eine Antwort ablehnen kann, die er nicht versteht, oder zumindest laut genug einen Fehler loggt, dass es jemand bemerkt. Ein Webhook-Empfänger tut selten eines von beidem. Er bekommt ein POST, liest die Felder, die er erwartet, und wenn ein Feld verschoben wurde, den Typ gewechselt hat oder verschwunden ist, stürzt der Endpunkt entweder lautlos in einem Hintergrundjob ab, den niemand beobachtet, oder, schlimmer, läuft mit einem falschen Wert weiter, den er nie validiert hat. Was ist ein Breaking Change behandelt die allgemeine Definition; eine Webhook-Payload braucht ihre eigene Antwort, weil das Fehlerbild anders ist als bei einem Endpunkt, den jemand absichtlich aufruft.

Warum bricht eine Webhook-Payload-Änderung anders als eine Änderung der API-Antwort?

Weil die Richtung der Anfrage umgekehrt ist. Ein REST-Aufrufer initiiert den Call und kann einen Versions-Header hinzufügen, bei einem 4xx erneut versuchen oder einen Deprecation-Hinweis in der Antwort lesen. Ein Webhook-Empfänger hat nichts davon initiiert: euer Server hat entschieden zu senden, wann er sendet, und welche Form der Body hat. Der einzige Hebel des Empfängers ist die Validierung, die er beim Bau der Integration geschrieben hat, und die meisten Integrationen werden einmal gebaut, funktionieren, und werden nie wieder angefasst, bis sie brechen. Diese Asymmetrie ist der ganze Grund, warum eine Webhook-Payload-Änderung mehr Vorsicht verdient als dieselbe Änderung in einem Antwort-Body, den ein Aufrufer aktiv angefragt hat.

Was zählt in einer Webhook-Payload tatsächlich als Breaking Change?

ÄnderungBreaking für die meisten Empfänger
Ein neues Feld hinzufügenNein, wenn Empfänger unbekannte Felder ignorieren (diese Annahme prüfen, nicht voraussetzen)
Ein Feld entfernenJa, wenn irgendetwas es liest
Ein Feld umbenennenJa, faktisch identisch mit dem Entfernen des alten
Den Typ eines Feldes ändern (String zu Objekt)Ja, fast immer
Felder im JSON-Body umsortierenNein, für jeden Empfänger, der nach Schlüssel parst, was alle sollten
Den Event-Namen oder -Typ ändernJa, wenn Empfänger danach filtern oder routen

Die Zeile „ein Feld hinzufügen ist sicher” ist die, auf die sich Teams am meisten verlassen und die es wert ist, geprüft statt angenommen zu werden. Ein nachsichtiger JSON-Parser ignoriert unbekannte Felder standardmäßig, aber ein Empfänger, der in ein striktes Schema deserialisiert, mehrere typisierte Sprachen tun das ohne zusätzliche Konfiguration, kann die ganze Payload ablehnen, sobald ein unerwartetes Feld auftaucht. Ein Feld hinzuzufügen ist für euren Webhook nur sicher, wenn ihr wisst, wie Empfänger parsen, nicht weil JSON selbst nachsichtig ist.

Wie versioniert man eine Webhook-Payload?

Ähnlich wie bei einer API-Antwort, mit einem Unterschied: Der Empfänger schickt nie eine Anfrage, kann also keine Version anfordern, und der Absender muss sie angeben. Das kann im Body stehen oder in einem Request-Header der Zustellung selbst; GitHubs Zustellungen tragen X-GitHub-Event und X-GitHub-Hook-ID, und die Standard-Webhooks-Spezifikation legt ihre Metadaten in webhook-*-Header. Ein Versionsfeld in der Payload ("payload_version": 2) ist die günstigste Option und funktioniert, wenn Empfänger bereit sind, danach zu verzweigen. Ein versionierter Event-Typ (invoice.updated wird zu invoice.updated.v2 als eigenständigem Event, das ein Empfänger opt-in nutzt) ist mehr Arbeit beim Bau, bedeutet aber, dass die alte Form weiter an alle fließt, die nie migriert haben, was hier mehr zählt als bei einem REST-Endpunkt, weil ihr nicht jeden Empfänger anrufen könnt, um ihn zum Update aufzufordern. Eine Einstellung pro Subscription, gewählt bei der Registrierung des Webhook-Endpunkts, trifft die Entscheidung vorab statt bei jeder Zustellung zu verzweigen, und ist die richtige Wahl, wenn ihr schon einen Subscription-Datensatz habt, an den sie sich hängen lässt.

POST /empfaenger-endpunkt
{
  "event": "invoice.updated",
  "payload_version": 2,
  "data": { "invoice_id": "inv_123", "status": "paid" }
}

Wie wisst ihr überhaupt, wer zuhört?

Schlimmer als die entsprechende Version dieses Problems bei einem API-Changelog, weil ein Webhook kein eingehendes Anfrage-Log auf eurer Seite hat, das den Aufrufer nennt; ihr habt nur euer eigenes ausgehendes Zustell-Log, das euch sagt, dass ein Endpunkt einen 200 bekommen hat, nicht, was er mit dem Body gemacht hat. Verfolgt mindestens zwei Dinge: jeden registrierten Endpunkt mit einer Besitzerin, dieselbe Disziplin, die interne API-Changelogs für interne Konsumenten empfehlen, und eure Zustellungsfehlerquote pro Endpunkt nach einer Payload-Änderung. Ein Anstieg von 4xx- oder 5xx-Antworten von einem Endpunkt direkt nach einer Änderung ist das Nächste an einem Stack Trace, das ihr bekommt, und oft das einzige Signal, dass ein Empfänger kaputtgegangen ist, weil das Team, das ihn betreibt, es tagelang nicht bemerken könnte.

Sollte ein Webhook-Changelog vom API-Changelog getrennt sein?

Ein eigener Abschnitt auf derselben Seite, keine eigene Veröffentlichung. Ein API-Changelog legt schon fest, wer ihn liest und wie er abonniert wird; eine Webhook-Payload-Änderung gehört in denselben Feed, klar genug markiert, dass eine Empfänger-seitige Entwicklerin, die nach „betrifft das meine Integration” scannt, danach filtern kann, weil eine Webhook-Konsumentin oft keinen anderen Grund hat, einen allgemeinen API-Changelog zu prüfen, und ihn nur findet, wenn jemand sie direkt dorthin verlinkt.

Wie sieht ein angemessenes Deprecation-Fenster für eine Webhook-Payload aus?

Länger als die entsprechende REST-Deprecation, weil Migration auf der Empfängerseite meist heißt, dass ein zweites Team, mit dem ihr vielleicht keine direkte Verbindung habt, es bemerken, einplanen und ohne eigene Dringlichkeit ausliefern muss. Ein Monat ist eine vernünftige Untergrenze für ein Feld, das der Empfänger plausibel noch mit einer nachsichtigen Library parst; drei Monate oder mehr sind sicherer für eine Feldentfernung, die ein striktes Schema komplett ablehnen würde. Sendet die alte und neue Form gemeinsam während des Fensters, wenn machbar (das alte Feld status und sein Ersatz aus Version 2 in derselben Payload), weil ein Empfänger, der das alte Feld liest, weiterläuft, ohne seinen Code anzufassen, und einer, der schon migriert hat, das Feld, das er nicht mehr braucht, einfach ignoriert.

FAQ

Müssen Webhook-Konsumenten eine Payload-Änderung bestätigen, bevor sie live geht? Es gibt standardmäßig keinen Bestätigungsmechanismus, genau deshalb zählt das Deprecation-Fenster hier mehr als bei einer REST-API: Niemand bestätigt Bereitschaft, also muss das Fenster lang genug sein, dass die meisten Empfänger von selbst migrieren, bevor die alte Form verschwindet.

Sollten unbekannte Felder je als sicher zum Hinzufügen ohne Ankündigung gelten? Nur wenn geprüft, nicht angenommen wurde, dass eure Empfänger nachsichtig parsen. Ein Changelog-Eintrag kostet wenig und nimmt das Rätselraten heraus; unbekannte Felder auf der Annahme „JSON-Parser ignorieren Extras” lautlos hinzuzufügen, bricht jeden Empfänger mit strikter Deserialisierung.

Wie erkennt man am schnellsten einen kaputten Webhook-Empfänger nach einer Payload-Änderung? Eine Zustellungsfehlerquote pro Endpunkt, beobachtet in den Stunden direkt nach der Änderung. Sie sagt euch nicht, was kaputtgegangen ist, nur dass etwas kaputt ist, aber sie ist das früheste und oft einzige Signal, das ihr bekommt.

Hilft Retry-Logik Empfängern, eine Payload-Änderung zu überstehen? Nein. Ein Retry sendet dieselbe neue Payload erneut; er kehrt nicht zu einer Form zurück, die der Empfänger parsen kann. Eine Payload-Änderung bricht einen Empfänger bei der ersten Zustellung und jedem folgenden Retry identisch.


Die technischen Aussagen in diesem Artikel wurden nicht unabhängig geprüft. Wenn etwas nicht stimmt, sagen Sie es uns, und wir korrigieren es.

Mehr bei changeloop: Entwicklerdokumentation, Changelog-Tools im Vergleich

changeloop
Das Team hinter einem Changelog, das den Kreis schließt. Ihre Nutzer fragen, Ihr Team liefert, und wer gefragt hat, erfährt davon.