{ }
GET
REST API · Deprecation · Breaking Changes · Versionierung · Evolution
Deprecations, Breaking Changes
und API-Evolution praktisch steuern

Eine REST-API, die niemals Breaking Changes durchführt, ist eine API, die niemals wirklich weiterentwickelt wird. Wer Deprecations, Sunset-Dates und Migrationspfade von Anfang an einplant, kann APIs gezielt weiterentwickeln, ohne Integratoren zu verlieren oder ohne Vorwarnung zu brechen.

20 Min. Lesezeit Deprecation-Header · Sunset · Versionierung · Migration-Guide REST · OpenAPI · Symfony

1. Was Breaking Changes wirklich bedeuten

Ein Breaking Change in einer REST-API ist jede Änderung, die dazu führt, dass ein bestehender, korrekt implementierter Client nach dem Update nicht mehr funktioniert oder falsches Verhalten zeigt – ohne dass der Client-Code geändert wurde. Das klingt einfach, ist in der Praxis aber subtil: Das Umbenennen eines JSON-Feldes ist ein offensichtlicher Breaking Change. Das Ändern des Datentyps eines Feldes von String zu Integer, das Entfernen eines optionalen Feldes, das manche Clients auswerten, das Einschränken der erlaubten Werte eines Enum-Felds oder das Verändern des Paginierungsverhaltens – all das sind Breaking Changes, die nicht immer als solche erkannt werden.

Der häufigste organisatorische Fehler: Das Team, das die API weiterentwickelt, hat keine vollständige Liste der aktiven Integratoren und keinen Überblick darüber, welche Felder und Endpunkte von wem genutzt werden. In dieser Situation wird jede Änderung zum Risiko, weil man nicht weiss, wen man bricht. Die Lösung ist nicht, keine Änderungen durchzuführen, sondern einen strukturierten Prozess zu etablieren: Klassifikation von Änderungen, Deprecation-Signalisierung, Sunset-Dates und ein Migrationspfad für jeden Breaking Change. Teams, die diesen Prozess eingerichtet haben, können ihre APIs tatsächlich weiterentwickeln, statt für immer an alten Designentscheidungen festzuhängen.

2. Breaking vs. Non-Breaking: Klassifikation in der Praxis

Nicht jede Änderung ist ein Breaking Change. Die Unterscheidung ist entscheidend, weil sie bestimmt, ob eine Änderung ohne Versionswechsel und ohne Vorankündigung durchgeführt werden kann oder nicht. Non-Breaking Changes sind additive Änderungen: Ein neues optionales Feld im Response hinzufügen, einen neuen optionalen Query-Parameter ergänzen, einen neuen Endpunkt hinzufügen, oder einen bestehenden Fehlercode detaillierter dokumentieren. Korrekt implementierte Clients ignorieren unbekannte Felder und funktionieren weiterhin. RFC 7231 empfiehlt für JSON-APIs ausdrücklich, unbekannte Felder zu tolerieren.

Breaking Changes umfassen: Felder entfernen oder umbenennen, Datentypen ändern, Enum-Werte entfernen (neue hinzufügen ist non-breaking), Pflichtfelder in Requests hinzufügen, HTTP-Methoden oder Pfade ändern, Statuscodes für bestehende Szenarien ändern, und grundlegend anderes Fehlerverhalten. Eine hilfreiche Prüffrage: "Würde ein Client, der heute korrekt nach Dokumentation implementiert ist, nach dieser Änderung ohne Codeänderung weiterhin korrekt funktionieren?" Wenn nein: Breaking Change. Diese Klassifikation sollte in einem Änderungsprotokoll (CHANGELOG) für jedes API-Release festgehalten werden, damit Integratoren auf einen Blick sehen, was eine neue Version für sie bedeutet.

# Beispiel-CHANGELOG für API v2.3.0 — Klassifikation jeder Änderung
## API v2.3.0 — 2026-05-10

### Non-Breaking Changes (safe to adopt without code changes)
- GET /orders: Added optional field `tags` (array of strings) to response
- GET /orders: Added optional query parameter `?tags=` for filtering
- POST /orders: Field `notes` is now optional (was already optional, now documented)
- New endpoint: GET /orders/{id}/timeline — order history events

### Deprecations (still functional, removed in v3.0.0 on 2027-01-01)
- GET /orders: Field `customer_name` deprecated. Use `customer.fullName` instead.
  Deprecation: true; Sunset: Thu, 01 Jan 2027 00:00:00 GMT
- GET /legacy/orders: Entire endpoint deprecated. Use GET /orders instead.

### Breaking Changes (v3.0.0 — announced 2025-11-01, effective 2027-01-01)
- REMOVED: Field `customer_name` (use `customer.fullName`)
- REMOVED: GET /legacy/orders endpoint
- CHANGED: `status` field now returns enum instead of string (new values: pending, confirmed, shipped, delivered, cancelled)

3. Deprecation-Header und Sunset-Dates korrekt setzen

HTTP definiert zwei Standard-Header für Deprecations: Deprecation und Sunset. Der Deprecation-Header markiert einen Endpunkt oder eine API-Version als veraltet. Er enthält entweder true (sofort deprecated) oder ein RFC-7231-Datum (ab wann deprecated). Der Sunset-Header kommuniziert das geplante Abschaltdatum als HTTP-Datum. Zusätzlich kann ein Link-Header auf die Dokumentation der Deprecated-Ressource oder den Migrationsleitfaden verweisen. Diese drei Header sind in RFC 8594 (Sunset HTTP Header) und dem Deprecation-HTTP-Header-Entwurf standardisiert.

In Symfony setzt man diese Header am einfachsten über einen Event-Subscriber, der Response-Header für markierte Routen ergänzt. Alternativ lassen sich Deprecation-Attribute auf Controller-Methoden definieren, die ein zentraler Middleware-Layer ausliest. Wichtig: Deprecation-Header müssen auf jede Response des deprecated Endpunkts gesetzt werden, nicht nur bei der ersten Anfrage. Integratoren, die ihre Logs nicht täglich auswerten, sollen trotzdem irgendwann auf den Header stoßen. Monitoring-Tools wie Datadog oder Grafana können Deprecation-Header erkennen und Alerts generieren, wenn Clients noch nach dem Sunset-Date anfragen.

<?php
// Symfony EventSubscriber: Deprecation-Header für markierte Routen setzen
declare(strict_types=1);

namespace App\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

/**
 * Sets RFC 8594 Deprecation and Sunset headers on deprecated API routes.
 * Routes opt-in via _deprecation route attribute.
 */
final class ApiDeprecationSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [KernelEvents::RESPONSE => 'onKernelResponse'];
    }

    public function onKernelResponse(ResponseEvent $event): void
    {
        $request  = $event->getRequest();
        $response = $event->getResponse();

        /** @var array{sunset?: string, link?: string}|null $deprecation */
        $deprecation = $request->attributes->get('_deprecation');

        if ($deprecation === null) {
            return;
        }

        // RFC 8594: Deprecation header — value "true" or HTTP-date
        $response->headers->set('Deprecation', 'true');

        if (isset($deprecation['sunset'])) {
            // Sunset header: RFC 7231 HTTP-date format
            $response->headers->set(
                'Sunset',
                (new \DateTimeImmutable($deprecation['sunset']))->format(\DateTimeInterface::RFC7231)
            );
        }

        if (isset($deprecation['link'])) {
            $response->headers->set(
                'Link',
                sprintf('<%s>; rel="deprecation"', $deprecation['link'])
            );
        }
    }
}

4. Versionierungsstrategien: URL, Header, Query-Parameter

Die drei gängigen Versionierungsstrategien für REST-APIs sind URL-Versionierung (/api/v1/orders), Header-Versionierung (Accept: application/vnd.mironsoft.v2+json) und Query-Parameter-Versionierung (/api/orders?version=2). Jede hat andere Konsequenzen für Caching, Routing, Client-Komplexität und Dokumentation.

URL-Versionierung ist die pragmatischste und am weitesten verbreitete Methode. Sie ist sofort in Logs und Proxies sichtbar, einfach zu routen, und browserfreundlich. Der Nachteil: Für jede neue Hauptversion muss eine komplett neue Route-Hierarchie aufgebaut werden, und beide Versionen müssen parallel betrieben werden, bis die alte Version abgeschaltet wird. Header-Versionierung hält URLs stabil und ist HTTP-konformer, erfordert aber Vary: Accept-Header für korrektes Caching und ist für manche Integratoren schwieriger zu implementieren. Query-Parameter-Versionierung ist die flexibelste, aber auch die am schlechtesten cachbare Variante und ist nur für APIs ohne Caching-Anforderungen geeignet. Die Empfehlung für die meisten öffentlichen und halboffenen APIs: URL-Versionierung mit klarer Sunset-Policy.

5. Migration-Guides schreiben, die Integratoren tatsächlich nutzen

Ein Migration-Guide, der nur aus einer Liste von Änderungen besteht, wird nicht genutzt. Integratoren brauchen konkrete Antworten auf drei Fragen: Was muss ich in meinem Code ändern? Wie teste ich, ob meine Migration korrekt ist? Wann muss ich spätestens migriert haben? Ein guter Migration-Guide beginnt mit einer Zusammenfassung der Breaking Changes in einem Satz pro Änderung, gibt dann für jede Änderung ein konkretes "Vorher/Nachher"-Codebeispiel, erklärt Randfälle und beschreibt, wie man während der Migration beide Versionen parallel nutzen kann.

Besonders hilfreich: Ein Kompatibilitätslayer für die Übergangszeit. Wenn v1 und v2 parallel laufen, können Clients schrittweise migrieren. Ein Endpunkt, der v1-Requests entgegennimmt und intern auf v2-Logik mappt, reduziert den Migrationsdruck auf Integratoren, die nicht sofort alles umschreiben können. Dieser Layer sollte mit einem klaren Endtermin kommuniziert werden und darf nicht dauerhaft bestehen bleiben – er ist ein Migrationshilfsmittel, kein permanenter Bestandteil der API.

# Migration Guide v1 → v2: Konkrete Vorher/Nachher-Beispiele

## Breaking Change: customer_name → customer.fullName

### v1 Response (deprecated, removed 2027-01-01)
GET /api/v1/orders/123
{
  "id": "123",
  "customer_name": "Max Mustermann",
  "total": 149.99
}

### v2 Response (current)
GET /api/v2/orders/123
{
  "id": "123",
  "customer": {
    "id": "CUST-42",
    "fullName": "Max Mustermann",
    "email": "max@example.com"
  },
  "total": 149.99
}

## Migrationspfad
1. Auf v2-Endpunkt umstellen: URL-Präfix /v1/ → /v2/ ändern
2. customer_name durch customer.fullName ersetzen
3. Neues Feld customer.id und customer.email nutzen (optional, non-breaking)
4. Mit Staging-System testen bevor Production-Umstellung
5. Spätestens bis 2026-10-01 migrieren (3 Monate vor Sunset)

6. Sunset-Monitoring und Nutzungsanalyse vor dem Abschalten

Einen Endpunkt abzuschalten, ohne zu wissen, ob noch jemand ihn nutzt, ist ein zuverlässiger Weg, einen Integrationsfehler in der Produktion zu verursachen. Sunset-Monitoring bedeutet, die tatsächliche Nutzung eines deprecated Endpunkts bis zum Abschalttermin zu verfolgen. Die Datenquelle dafür ist das API-Access-Log, aus dem man herausliest, wie viele Requests der deprecated Endpunkt pro Tag noch empfängt, von welchen Client-IPs oder API-Keys, und ob die Zahlen nach der Deprecation-Ankündigung abnehmen.

Wenn Requests kurz vor dem Sunset-Date nicht auf null zurückgehen, muss man handeln: Aktiv die noch aktiven Integratoren kontaktieren, das Sunset-Date verschieben oder einen letzten Reminder über alle verfügbaren Kanäle schicken. Das Abschalten eines Endpunkts, den noch ein Dutzend Clients täglich nutzen, ist kein technisches Versagen – es ist ein Kommunikationsversagen. Automatisierte Alerts, die das Team benachrichtigen, wenn ein deprecated Endpunkt am Sunset-Date noch Requests empfängt, sind ein wirksames Sicherheitsnetz. In Symfony lassen sich Request-Metriken über den Profiler, Prometheus oder direkt in Nginx-Logs auswerten.

7. Deprecations in OpenAPI dokumentieren

OpenAPI 3.x unterstützt das deprecated: true-Attribut auf Endpunkt-, Parameter- und Schema-Ebene. Das reicht für die technische Markierung, ist aber für Integratoren nicht ausreichend, weil OpenAPI kein Sunset-Datum und keinen Migrationspfad enthält. Die Lösung: Die description-Eigenschaft des deprecated Endpunkts mit einem strukturierten Deprecation-Block ergänzen, der Datum, Grund und Link zum Migration-Guide enthält. Mit dem Vendor-Extension-Mechanismus (x-sunset, x-deprecation-link) können Tools wie Redoc oder Stoplight diese Informationen hervorheben.

Für das API-Changelog eignet sich ein separates Dokument (CHANGELOG.md oder eine API-History-Seite im Portal), das unabhängig vom OpenAPI-Schema gepflegt wird und alle Versionen mit ihren Breaking Changes, Deprecations und Sunset-Dates auflistet. Integratoren, die eine neue Version adoptieren wollen, sollten den Changelog lesen können, ohne die gesamte OpenAPI-Datei zu vergleichen. Ein gut gepflegter Changelog ist oft das erste Dokument, das externe Teams lesen, bevor sie entscheiden, ob und wann sie upgraden.

8. Versionierungsstrategien im Vergleich

Die Wahl der Versionierungsstrategie beeinflusst Caching, Routing, Client-Komplexität und die Sichtbarkeit in Logs über den gesamten Lebenszyklus der API.

Strategie Beispiel Vorteile Nachteile
URL-Versionierung /api/v2/orders Sichtbar in Logs, einfach zu routen, cachebar URL-Proliferation bei vielen Versionen
Header-Versionierung Accept: vnd.v2+json URLs stabil, HTTP-konform Vary-Header für Cache, Testaufwand höher
Query-Parameter /orders?v=2 Einfach für Clients ohne Header-Kontrolle Schlecht cachebar, unüblich in Public APIs
Kein Versioning Nur Non-Breaking Changes erlaubt Einfachste Client-Integration Evolutionshemmer, kein Escape-Hatch

In der Praxis kombinieren viele Teams URL-Versionierung für Hauptversionen mit field-level Deprecation-Markierungen für kleinere Änderungen. Das gibt Integratoren das beste Verhältnis aus Stabilität (URL-Versionen wechseln nur bei wirklich großen Breaking Changes) und Transparenz (deprecated Felder sind im Schema und in Response-Headern erkennbar). Die wichtigste Regel bleibt: Jede Strategie ist besser als keine, solange Sunset-Dates kommuniziert und eingehalten werden.

9. Zusammenfassung

API-Evolution ohne Breaking-Change-Management bedeutet, dass jede Änderung ein Risiko für alle Integratoren ist – und das führt entweder zu einer API, die niemals wirklich verbessert wird, oder zu einer API, die Integratoren ohne Vorwarnung bricht. Deprecation-Header und Sunset-Dates sind der HTTP-Standard für strukturiertes Veralten: Integratoren erfahren in jedem Response, was sich ändert und bis wann. Eine klare Klassifikation von Breaking und Non-Breaking Changes ermöglicht es, additive Änderungen ohne Versionswechsel durchzuführen und Breaking Changes transparent anzukündigen.

URL-Versionierung ist die pragmatischste Strategie für die meisten REST-APIs. Migration-Guides mit konkreten Vorher/Nachher-Beispielen erhöhen die Adoptionsrate erheblich. Sunset-Monitoring über API-Logs verhindert, dass Endpunkte abgeschaltet werden, die noch aktiv genutzt werden. Teams, die diesen Prozess etablieren, können ihre APIs tatsächlich weiterentwickeln – und behalten das Vertrauen ihrer Integratoren, weil Breaking Changes planbar, kommuniziert und testbar sind, bevor sie in Produktion gehen.

API-Evolution und Breaking Changes — Das Wichtigste auf einen Blick

Deprecation-Header

Deprecation: true und Sunset: {date} auf jeder Response des deprecated Endpunkts setzen. Link-Header zum Migration-Guide ergänzen. RFC 8594.

Breaking vs. Non-Breaking

Additive Änderungen (neue Felder, neue Endpunkte) sind safe. Alles, was bestehende Clients bricht, ist ein Breaking Change und braucht Ankündigung + Migrationspfad.

Sunset-Monitoring

Tatsächliche Nutzung deprecated Endpunkte über Access-Logs verfolgen. Alert wenn am Sunset-Date noch Requests eingehen. Nie blind abschalten.

Migration-Guide

Konkrete Vorher/Nachher-Beispiele. Testanleitung für Migration. Deadline klar kommunizieren. Kompatibilitätslayer für Übergangszeit anbieten.

10. FAQ: Deprecations, Breaking Changes und API-Evolution

1Was ist ein Breaking Change in einer REST-API?
Jede Änderung, die einen korrekt implementierten Client ohne Codeänderung bricht: Felder entfernen, umbenennen, Typen ändern, Pflichtfelder hinzufügen, Statuscodes für bestehende Szenarien ändern.
2Was sind Non-Breaking Changes?
Additive Änderungen: neue optionale Felder, neue optionale Parameter, neue Endpunkte, neue Enum-Werte (sofern Clients unbekannte tolerieren). Korrekt implementierte Clients funktionieren weiterhin.
3Was macht der Deprecation-Header?
Markiert Endpunkt als veraltet (RFC 8594). Mit Sunset-Header kommuniziert man das Abschaltdatum. Monitoring-Tools können beide Header erkennen und Alerts generieren.
4Welche Versionierungsstrategie empfiehlt sich?
URL-Versionierung (/api/v2/) für die meisten öffentlichen APIs: sichtbar in Logs, einfach zu routen, cachebar. Header-Versionierung HTTP-konformer, aber aufwendiger.
5Wie lang sollte die Sunset-Frist sein?
Öffentliche APIs: mindestens 12 Monate. Interne APIs oder kleine Integrator-Basis: 3–6 Monate. Lang genug, dass alle aktiven Integratoren migrieren können.
6Was ist Sunset-Monitoring?
Tatsächliche Nutzung deprecated Endpunkte bis zum Abschalttermin verfolgen. Über Access-Logs prüfen ob Requests auf null zurückgehen. Wenn nicht: aktiv kommunizieren oder Sunset verschieben.
7Deprecations in OpenAPI modellieren?
deprecated: true auf Endpunkt- oder Feld-Ebene. Sunset-Datum und Migration-Link in description oder als Vendor-Extension (x-sunset). Redoc und Stoplight heben deprecated-Markierungen hervor.
8Was ist ein Kompatibilitätslayer?
Temporärer Layer, der v1-Requests auf v2-Logik mappt. Gibt Integratoren mehr Zeit ohne sofortigen Codeänderungszwang. Nur mit klarem Endtermin – kein permanenter Bestandteil.
9Deprecated Endpunkt ohne Vorwarnung abschalten?
Nein, wenn noch aktiv genutzt. Sunset-Monitoring zeigt ob Requests eingehen. Bei positivem Befund: Integratoren kontaktieren, Sunset verschieben oder finalen Reminder senden.
10Struktur eines guten Migration-Guides?
1. Änderungen pro Satz zusammengefasst. 2. Vorher/Nachher-Beispiele. 3. Testanleitung. 4. Klares Deadline-Datum. 5. Ansprechpartner für Rückfragen.