Die teure Entscheidung fällt selten beim ersten API-Aufruf. Sie fällt, wenn ein Framework und ein Partner-SDK zusammen in die Fachlogik rutschen und später niemand mehr weiß, welche Änderung welche Schnittstelle betrifft. Meine Position: Als Junior solltest du eine externe Integration zunächst kleiner bauen, als es die Dokumentation nahelegt. Nicht aus Misstrauen gegen Tools, sondern weil du ihre laufende Wartung mit übernimmst.
Der erste erfolgreiche Request verdeckt die eigentliche Wartungsarbeit
Ein SDK liefert dir nach wenigen Zeilen eine Antwort, das Framework wandelt sie in ein Objekt um, und der erste Test ist grün. Das fühlt sich nach erledigter Integration an. Tatsächlich hast du zunächst nur den Normalfall geprüft. Ob die Antwort morgen noch dieselben Felder enthält, ob ein Timeout einen bereits ausgeführten Auftrag verdeckt und wer einen abgelaufenen Zugang erneuert, bleibt offen. Diese Fragen sind keine Randfälle, weil sie nach dem Go-live ohne deine Anwesenheit beantwortet werden müssen.
Ich lese den Beitrag Warum du als Junior nicht immer zum Standard-Framework greifen solltest als Warnung vor reflexhaften Entscheidungen; bei einer Fremd-API halte ich jedoch die Grenze um das SDK für wichtiger als die Wahl des Web-Frameworks. FastAPI oder Django können beide gut passen, solange weder Views noch Hintergrundjobs direkt von den Antwortklassen eines Partners abhängen. Sonst wird aus einer Änderung am Partnerformat eine Suche durch Controller, Datenbankcode und Tests, weil jedes dieser Teile dieselben Fremdbegriffe verwendet.
Stell dir einen Dienst vor, der Aufträge an einen Partner überträgt. Dessen Antwort enthält eine ID, einen Status und einen Zeitstempel. Deine Anwendung benötigt intern vielleicht nur eine Partner-ID und die Aussage „angenommen“. Trotzdem ist es verlockend, die ganze JSON-Antwort in PostgreSQL 16 abzulegen und das SDK-Objekt an andere Funktionen weiterzureichen. Bei der nächsten SDK-Aktualisierung müssen diese Funktionen dann mitgeprüft werden, weil sie stillschweigend auf Felder zugreifen können, die nie Teil deiner eigenen Schnittstelle waren.
Das Wartungsproblem beginnt sogar vor einer sichtbaren API-Änderung. Ein OAuth-2.0-Token nach RFC 6749 kann ablaufen, ein Secret kann rotiert werden, und ein erfolgreicher HTTP-Request kann zurückkommen, nachdem dein Worker längst aufgegeben hat. Für dich heißt „funktioniert“ daher nicht: Ein Request liefert HTTP 200. Es heißt: Du kannst nach einem unklaren Ausgang feststellen, ob der Auftrag ausgeführt wurde, ohne ihn versehentlich erneut auszulösen. Diese Eigenschaft muss deine Anwendung herstellen, falls der Partner sie nicht zuverlässig anbietet.
Eine kleine eigene Schnittstelle erspart dir später große Suchaktionen
Ich würde das Partner-SDK nicht direkt in Controller, Celery-Tasks und Datenbankmodelle injizieren, weil dann jeder Versionswechsel an mehreren Stellen fachlich bewertet werden muss. Stattdessen würde ich eine schmale Funktion wie submit_order(order) definieren, die einen eigenen Rückgabetyp liefert und partnerbezogene Fehler in wenige interne Kategorien übersetzt. Das ist keine Aufforderung, ein universelles Integrations-Framework zu bauen: Eine zusätzliche Abstraktion lohnt sich nur, wenn sie eine konkrete Abhängigkeit einschließt.
Der Vergleich ist praktisch. SDK direkt verwenden gewinnt bei einem kurzlebigen internen Prototyp, wenn du Fehler manuell behebst und kein zweiter Programmteil die Antwort nutzt; es kostet dich wenig Startzeit, lässt aber Partnerdetails überall dort zurück, wo du sie verwendest. Eigener Adapter um das SDK gewinnt bei einem dauerhaft betriebenen Ablauf, weil Formatänderungen und Fehlerübersetzung an einer Stelle landen; er kostet dich eine zusätzliche Datei, Vertragstests und die Disziplin, seine Schnittstelle nicht bei jedem neuen SDK-Feld aufzublähen. Keiner der Ansätze ist automatisch sauberer: Entscheidend ist, wer nach einem fehlgeschlagenen Nachtlauf die Ursache finden muss.
OpenAPI 3.1 und JSON Schema 2020-12 helfen dir, erwartete Felder zu beschreiben. Sie nehmen dir aber nicht die Entscheidung ab, wie deine Anwendung auf ein fehlendes Pflichtfeld reagiert. Der folgende ausführbare Ausschnitt für Python 3.11 prüft einen kleinen, bereinigten Antwortmitschnitt. Lege ihn etwa als check_partner.py ab und starte ihn mit python check_partner.py:
import json
from datetime import datetime, timezone
payload = json.loads('{"id":"p-17","updated_at":"2026-09-30T08:00:00Z"}')
required = {"id", "updated_at"}
missing = required - payload.keys()
if missing:
raise ValueError(f"Fehlende Felder: {sorted(missing)}")
updated_at = datetime.fromisoformat(payload["updated_at"].replace("Z", "+00:00"))
if updated_at.tzinfo is None:
raise ValueError("Zeitstempel ohne Zeitzone")
print(payload["id"], updated_at.astimezone(timezone.utc).isoformat())
Das ist noch kein vollständiger Vertragstest. Es zeigt aber eine nützliche Grenze: Der Partner darf zusätzliche Felder senden, während die zwei benötigten Felder und die Zeitzone geprüft werden. Ersetze den eingebauten Mitschnitt durch anonymisierte Antworten aus deiner Testumgebung und lass den Test mit pytest laufen. HTTPX und respx können zusätzlich Timeouts sowie HTTP 429 simulieren; solche Tests sind wertvoller als ein weiterer Test für HTTP 200, weil gerade Drosselung und unklare Antworten über Wiederholungen entscheiden.
Ich lese den Beitrag Dein erstes Partner-Tool kostet dich mehr Wartung als Code als Warnung vor dem laufenden Aufwand, würde ihn aber enger fassen: Am teuersten ist nicht jedes zusätzliche Tool, sondern eine unklare Zuständigkeit zwischen deinem Code und dem Partner. Ein Adapter macht diese Zuständigkeit prüfbar. Wenn sein Test rot wird, weißt du, welche Annahme über die Fremdantwort nicht mehr gilt, statt zuerst die gesamte Anwendung durchsuchen zu müssen.
Retries sind eine fachliche Entscheidung und keine bequeme Einstellung
Viele HTTP-Clients können Requests wiederholen. Ob sie es dürfen, hängt von der Aktion ab: Ein erneut geladener Status verursacht normalerweise etwas anderes als ein erneut angelegter Auftrag. HTTP 429 signalisiert Drosselung; ein Timeout sagt dagegen nicht, ob der Partner den Auftrag verarbeitet hat. Wer beides gleich behandelt, riskiert Duplikate, weil die zweite Übertragung nach einem Timeout zusätzlich zur ersten erfolgreich sein kann.
Falls der Partner Idempotency-Keys unterstützt, gib demselben fachlichen Auftrag bei einem Retry denselben Schlüssel. Stripe beschreibt für seine API, dass Idempotency-Keys nach mindestens 24 Stunden entfernt werden können; das ist eine vom Anbieter veröffentlichte Grenze und keine Zusage für beliebig spätes Wiederholen. Prüfe deshalb die Regeln deines Partners, bevor du eine Queue mehrere Tage lang erneut zustellen lässt. Ohne passende Zusage brauchst du eine Abfrage des Auftragsstatus oder einen manuellen Klärungsweg für ungewisse Ergebnisse.
Für einen ersten Betriebsentwurf würde ich 30 Sekunden als einzustellende Obergrenze für einen einzelnen Versuch notieren, nicht als allgemeingültigen Timeout. Liegt die übliche Antwortzeit weit darunter, bindet ein längerer Timeout Worker unnötig; dauert die Verarbeitung regelmäßig länger, musst du eher den Auftragsstatus abfragen als den Request offen halten. Miss die tatsächlichen Antwortzeiten über 14 Tage, bevor du die Grenze festschreibst. Dieser Zeitraum ist ein Vorschlag für deine Messung, kein bereits beobachteter Wert.
Celery 5.4 kann verzögerte Wiederholungen ausführen, Redis kann als Broker dienen, und PostgreSQL kann festhalten, welcher fachliche Auftrag mit welchem Schlüssel gesendet wurde. Die Kombination garantiert trotzdem keine korrekte Zustellung, weil Queue und Datenbank nicht automatisch dieselbe Transaktion teilen. Halte deshalb den Zustand so fest, dass ein Worker nach einem Neustart entscheiden kann: neu senden, Status beim Partner abfragen oder zur Prüfung anhalten. Schreibe diese Entscheidung für jeden Fehlerfall auf, bevor du einen allgemeinen Retry einschaltest.
Auch Meldungen brauchen eine Grenze. Ein Prometheus-Zähler für fehlgeschlagene Übertragungen und ein Sentry-Ereignis mit deiner internen Auftrags-ID helfen bei der Diagnose, weil sie Fehler einer konkreten Verarbeitung zuordnen. Ein Alarm bei jedem einzelnen Timeout erzeugt dagegen unnötige Arbeit, falls der nächste Versuch zuverlässig erfolgreich ist. Starte mit einer Schwelle, die du anhand echter Läufe anpasst, und prüfe im Alarmtext, ob jemand den Auftrag erneut senden darf. Ein Alarm ohne diese Information verschiebt die Unsicherheit nur an die nächste Person.
Die Pflege beginnt schon vor dem nächsten Feature
Nach der Einführung ändern sich nicht nur API-Antworten. Ein SDK erhält neue Abhängigkeiten, ein Token verliert seine Berechtigung, eine Sandbox verhält sich anders als das produktive System. Renovate oder Dependabot können Aktualisierungen sichtbar machen; sie können dir nicht sagen, ob ein neuer SDK-Release die Bedeutung eines Partnerstatus verändert. Plane deshalb für jede Aktualisierung einen kleinen Nachweis: Vertragstests, einen Durchlauf gegen die Sandbox und eine Person, die die Änderung freigibt.
Ich würde außerdem keine produktive Antwort ungefiltert als Test-Fixture einchecken, weil dort personenbezogene Daten oder Zugangsinformationen stehen können. Erstelle stattdessen einen bereinigten Mitschnitt, der die tatsächlich benötigten Felder und problematische Formen wie fehlende Zeitzonen bewahrt. Bei einem neuen Partnerfehler ergänzt du genau diesen Fall. So wächst deine Testsammlung aus beobachteten Störungen und nicht aus einer erfundenen Liste aller denkbaren API-Antworten.
GitHub Actions kann diese Tests bei Pull Requests ausführen, doch ein grüner Lauf beweist nur, dass deine bekannten Beispiele noch passen. Eine produktive Gegenprobe bleibt nötig, wenn der Partner Änderungen ohne neue SDK-Version ausrollt. Lege fest, wer die Changelog-Mails liest, wer Secrets rotieren darf und wo ein fehlgeschlagener Auftrag sichtbar wird. Diese Aufgaben wirken klein, bis sie niemandem gehören; dann dauert die Behebung länger als die ursprüngliche Implementierung.
Ein offener Wartungsfall ist der bessere Start als noch ein schneller Request
Nimm den letzten Partner-Request aus deinem Projekt und zeichne seinen Weg bis zur gespeicherten Antwort nach. Markiere jede Stelle, die ein Feld des Partners direkt liest. Wähle dann einen unklaren Ausgang – etwa einen Timeout nach dem Senden – und schreibe auf, was der nächste Lauf konkret tut. Wenn du das nicht beantworten kannst, ist genau dort deine erste Wartungsaufgabe.


