GraphQL Deprecation Workflow: alte Felder sauber auslaufen lassen
AI generated
{ }
type
GraphQL · Schema-Lifecycle · Deprecation · Monitoring
GraphQL Deprecation Workflow
alte Felder sauber auslaufen lassen

Ein Feld einfach aus dem Schema zu löschen ist keine Deprecation, sondern ein Breaking Change. Ein sauberer GraphQL Deprecation Workflow markiert Felder mit der @deprecated-Direktive, misst ihre tatsächliche Nutzung und entfernt sie erst, wenn die Daten zeigen, dass kein Konsument mehr betroffen ist.

15 Min. Lesezeit @deprecated · Field Usage · Sunset-Fristen GraphQL · Schema-Lifecycle · DevOps

1. Warum Löschen keine Deprecation ist

Ein GraphQL-Feld einfach aus der SDL-Datei zu entfernen, weil es aus Sicht des Backend-Teams nicht mehr gebraucht wird, ist der häufigste Grund für kaputte Produktions-Frontends. GraphQL erlaubt es Konsumenten, exakt die Felder abzufragen, die sie brauchen, das bedeutet aber auch, dass jedes einzelne Feld potenziell irgendwo in einer Query verwendet wird, die niemand im Backend-Team kennt. Ein richtiger GraphQL Deprecation Workflow ersetzt das plötzliche Löschen durch einen mehrstufigen Prozess: markieren, messen, kommunizieren, erst dann entfernen.

Der Unterschied zwischen einer Breaking Change und einer sauberen Deprecation liegt nicht in der Technik, sondern in der Zeit. Ein Feld, das heute als @deprecated markiert wird, kann noch monatelang funktionieren, während parallel gemessen wird, wie oft es noch abgefragt wird. Erst wenn die Nutzung auf null gesunken ist oder eine vorher kommunizierte Frist abgelaufen ist, wird das Feld tatsächlich entfernt. Dieser Deprecation Workflow ist kein Nice-to-have, sondern die einzige Möglichkeit, ein wachsendes Schema langfristig wartbar zu halten, ohne bei jeder Aufräumaktion Kundenverbindungen zu riskieren.

2. Die @deprecated-Direktive richtig einsetzen

GraphQL bringt mit @deprecated(reason: String) eine eingebaute Direktive mit, die exakt für diesen Zweck gedacht ist. Das Feld bleibt technisch abfragbar, taucht aber in Tools wie GraphiQL, Apollo Studio oder GraphQL Playground mit einer durchgestrichenen Markierung und dem Grund auf. Der wichtigste Teil dieses Deprecation Workflows ist ein aussagekräftiger reason-Text: nicht nur "veraltet", sondern konkret, welches Ersatzfeld zu verwenden ist und bis wann das alte Feld verschwindet.

Ein häufiger Fehler: Entwickler markieren ein Feld als @deprecated, vergessen aber, dasselbe auf Enum-Werten und Argumenten zu tun, obwohl die Direktive seit der GraphQL-Spezifikationsversion von 2021 auch dort erlaubt ist. Ein Enum-Wert, der nicht mehr unterstützt wird, sollte genauso markiert werden wie ein Feld, sonst wissen Konsumenten nicht, dass sie ihre Filterlogik anpassen müssen. Konsequenter Einsatz der Direktive an allen Schema-Elementen ist die Grundlage jedes funktionierenden GraphQL Deprecation Workflows.


type Product {
  id: ID!
  sku: String!
  name: String!

  # Deprecated field — reason always names the replacement and the sunset date
  price: Float @deprecated(reason: "Use `priceV2: Money` instead. Removal planned for 2026-11-01.")

  priceV2: Money!
}

enum SortOrder {
  ASC
  DESC
  # Deprecated enum value — same directive, same convention
  RELEVANCE_DEPRECATED @deprecated(reason: "Use `RELEVANCE` instead. Removal planned for 2026-11-01.")
  RELEVANCE
}

type Query {
  products(
    # Deprecated argument — arguments support @deprecated since GraphQL spec 2021
    legacyFilter: String @deprecated(reason: "Use `filter: ProductFilterInput` instead.")
    filter: ProductFilterInput
  ): ProductConnection!
}

3. Nutzungs-Tracking: wer fragt das Feld noch ab?

Die @deprecated-Direktive allein löst kein Problem, sie macht es nur sichtbar. Der zentrale Baustein jedes echten Deprecation Workflows ist Nutzungs-Tracking auf Feldebene: Wie oft wurde price in den letzten 30 Tagen tatsächlich abgefragt, und von welchen Clients? Ohne diese Daten bleibt jede Entscheidung, ein Feld endgültig zu entfernen, eine Vermutung statt einer belegten Tatsache. Apollo Server und graphql-php bieten beide Extension-Points, an denen sich pro Request die verwendeten Feldpfade extrahieren lassen, bevor die Antwort zurückgeht.

In der Praxis reicht ein einfaches Middleware-Pattern: Der Resolver für ein deprecated Feld schreibt bei jedem Aufruf einen Zähler in ein Time-Series-System wie Prometheus oder in eine einfache Log-Zeile, die anschließend aggregiert wird. Wichtig ist, den aufrufenden Client zu identifizieren, etwa über einen X-Client-Name-Header oder das persisted-query-Manifest, damit das Team gezielt auf das verantwortliche Frontend-Team zugehen kann, statt eine anonyme Zahl zu haben, die niemandem zugeordnet werden kann.


<?php
declare(strict_types=1);

namespace Mironsoft\GraphQlDeprecation\Plugin;

use GraphQL\Type\Definition\ResolveInfo;
use Psr\Log\LoggerInterface;

/**
 * Tracks usage of deprecated GraphQL fields so removal decisions are data-driven,
 * not guesswork based on assumptions about which clients still call them.
 */
final class DeprecatedFieldUsageTracker
{
    private const array TRACKED_FIELDS = ['Product.price', 'Query.legacyFilter'];

    public function __construct(private readonly LoggerInterface $logger)
    {
    }

    /**
     * Logs a structured usage event for deprecated fields, keyed by client identity.
     *
     * @param ResolveInfo $info    Resolver metadata including parent type and field name.
     * @param string      $client  Identifier of the calling client, e.g. from a header.
     * @return void
     */
    public function trackIfDeprecated(ResolveInfo $info, string $client): void
    {
        $fieldPath = $info->parentType->name . '.' . $info->fieldName;
        if (!in_array($fieldPath, self::TRACKED_FIELDS, true)) {
            return;
        }

        $this->logger->info('deprecated_field_usage', [
            'field' => $fieldPath,
            'client' => $client,
            'timestamp' => time(),
        ]);
    }
}

4. Sunset-Fristen und Kommunikationsplan

Ein Feld ohne Enddatum als @deprecated zu markieren führt fast immer dazu, dass es Jahre im Schema verbleibt, weil "irgendwann" kein Termin ist, auf den jemand hinarbeitet. Ein funktionierender Deprecation Workflow setzt von Anfang an ein konkretes Sunset-Datum, das im reason-Text steht und zusätzlich in einem Changelog dokumentiert wird, das Konsumenten abonnieren können. Üblich sind Fristen zwischen drei und sechs Monaten für interne APIs und sechs bis zwölf Monaten für öffentlich dokumentierte APIs mit externen Partnern.

Kommunikation ist der Teil des Deprecation Workflows, der am häufigsten vernachlässigt wird. Es reicht nicht, ein Feld zu markieren und zu hoffen, dass jemand die SDL-Datei liest. Wirksame Kanäle sind ein automatisiertes Release-Announcement bei jeder neuen Deprecation, eine zentrale Deprecation-Übersichtsseite, die alle aktuell markierten Felder mit Sunset-Datum auflistet, und bei kritischen Feldern eine direkte Nachricht an die Teams, die laut Nutzungs-Tracking noch aktiv darauf zugreifen. Erst wenn diese drei Ebenen greifen, ist ein Deprecation Workflow vollständig.

5. Deprecation im Codegen und in der IDE sichtbar machen

Ein oft übersehener Hebel im Deprecation Workflow ist die Entwickler-Erfahrung direkt in der IDE. GraphQL Code Generator markiert generierte TypeScript-Typen für deprecated Felder automatisch mit dem @deprecated-JSDoc-Tag, wodurch moderne Editoren wie VS Code das Feld im Autocomplete durchgestrichen anzeigen und beim Hovern den reason-Text einblenden. Das verschiebt die Warnung vom Dokumentations-Portal direkt in den Moment, in dem ein Entwickler die Query schreibt, deutlich früher als jede nachträgliche Prüfung.

ESLint-Plugins wie eslint-plugin-graphql gehen noch einen Schritt weiter und lassen den Build fehlschlagen, sobald ein neu geschriebener Query-String ein deprecated Feld referenziert. Das verhindert, dass ein Entwickler versehentlich neuen Code gegen ein bereits im Sunset befindliches Feld schreibt, weil die Deprecation-Warnung übersehen wurde. Diese Automatisierung ist besonders wichtig in großen Codebasen mit vielen Entwicklern, die nicht jede einzelne Schema-Änderung im Kopf haben können.

6. Deprecation am Gateway erzwingen und eskalieren

In Architekturen mit einem API-Gateway oder Apollo Router lässt sich der Deprecation Workflow zusätzlich technisch erzwingen: Das Gateway kann Requests, die ein bereits über die Sunset-Frist hinaus laufendes Feld verwenden, mit einem zusätzlichen Warn-Header in der Antwort versehen, sichtbar für jedes Monitoring-Tool auf Client-Seite. In einer letzten Eskalationsstufe kann das Gateway solche Requests sogar mit einem 410-Gone-Status für genau dieses Feld beantworten, während der Rest der Query normal verarbeitet wird.

Diese gestufte Eskalation, von der reinen Direktiven-Markierung über aktives Nutzungs-Tracking bis zur technischen Blockade am Gateway, gibt Teams die Kontrolle über das Tempo, mit dem ein Feld tatsächlich verschwindet. Wichtig ist, dass jede Eskalationsstufe im Voraus kommuniziert wurde, denn eine plötzliche 410-Antwort ohne vorherige Warnung ist technisch zwar sauber, praktisch aber genauso überraschend wie ein sofortiges Löschen ohne jede Deprecation.


// gateway-deprecation-plugin.js — Apollo Gateway plugin that warns, then blocks
const OVERDUE_FIELDS = { 'Product.price': '2026-11-01' };

module.exports = {
  requestDidStart() {
    return {
      willSendResponse({ request, response }) {
        const usedField = extractUsedDeprecatedField(request.query);
        if (!usedField) return;

        const sunset = OVERDUE_FIELDS[usedField];
        const isPastSunset = sunset && new Date() > new Date(sunset);

        if (isPastSunset) {
          // Escalation stage 2: block with 410, do not silently degrade
          response.http.status = 410;
          response.errors = [{ message: `Field ${usedField} was removed on ${sunset}` }];
        } else {
          // Escalation stage 1: warn but still serve the request
          response.http.headers.set('X-GraphQL-Deprecation-Warning', usedField);
        }
      },
    };
  },
};

7. Deprecation-Workflow in Magento GraphQL-Modulen

In Magento-eigenen GraphQL-Modulen wird die @deprecated-Direktive direkt in der schema.graphqls-Datei des jeweiligen Moduls gepflegt, genau wie im Core selbst, wo etwa ältere Cart-Mutation-Felder über Jahre hinweg mit klaren Reason-Texten markiert wurden, bevor sie in einer Major-Version entfernt wurden. Für eigene Erweiterungen empfiehlt sich derselbe Ansatz: Ein neues Feld ersetzt ein altes, das alte wird markiert, und die Entfernung erfolgt frühestens im nächsten Major-Release des eigenen Moduls, niemals in einem Patch-Release.

Ein Magento-spezifisches Detail betrifft db_schema.xml-gestützte Custom-Attribute, die über Resolver in GraphQL exponiert werden: Wird ein Attribut im Backend entfernt, aber der GraphQL-Resolver bleibt bestehen, kann das Feld weiterhin abgefragt werden und liefert dann konsequent null zurück. Der Deprecation Workflow muss deshalb beide Ebenen synchron behandeln, das GraphQL-Feld und das zugrunde liegende Attribut, sonst entsteht eine stille Inkonsistenz, bei der ein als aktiv markiertes Feld faktisch schon tot ist.


{
  "deprecations": [
    {
      "field": "Product.price",
      "reason": "Use priceV2: Money instead",
      "deprecatedSince": "2026-05-01",
      "sunsetDate": "2026-11-01",
      "usageLast30Days": 142,
      "activeClients": ["storefront-web", "partner-app-legacy"]
    },
    {
      "field": "Query.legacyFilter",
      "reason": "Use filter: ProductFilterInput instead",
      "deprecatedSince": "2026-03-15",
      "sunsetDate": "2026-09-15",
      "usageLast30Days": 0,
      "activeClients": []
    }
  ]
}

8. CI-Automatisierung: verwaiste Deprecations aufspüren

Ohne Automatisierung verkommt jeder Deprecation Workflow zu einer manuellen Excel-Liste, die niemand pflegt. Eine CI-Pipeline kann bei jedem Merge die SDL-Datei nach @deprecated-Vorkommen durchsuchen, das darin enthaltene Sunset-Datum extrahieren und automatisch ein Ticket erstellen, sobald das Datum überschritten ist. Das verhindert, dass ein Feld formal als deprecated markiert bleibt, aber praktisch nie tatsächlich entfernt wird, weil kein Mensch mehr aktiv daran erinnert wird.

Ein zweiter automatisierter Check vergleicht das Nutzungs-Tracking mit der Deprecation-Liste: Felder mit null Aufrufen in den letzten 30 Tagen und abgelaufener Sunset-Frist werden als sicher entfernbar markiert und können in einem automatisch generierten Pull-Request vorgeschlagen werden. Felder mit weiterhin aktiver Nutzung trotz abgelaufener Frist lösen dagegen eine Eskalation an das verantwortliche Team aus, statt stillschweigend liegen zu bleiben oder überraschend entfernt zu werden.


#!/usr/bin/env bash
# ci-deprecation-check.sh — finds fields past their sunset date and opens tickets
set -euo pipefail

SCHEMA_FILE="schema.graphqls"
TODAY="$(date +%Y-%m-%d)"

# Extract "Removal planned for YYYY-MM-DD" from every @deprecated reason
grep -oP '@deprecated\(reason: "[^"]*Removal planned for \K\d{4}-\d{2}-\d{2}' "$SCHEMA_FILE" \
  | while read -r sunset_date; do
      if [[ "$sunset_date" < "$TODAY" ]]; then
        echo "[OVERDUE] Sunset date $sunset_date has passed — filing ticket"
        gh issue create \
          --title "GraphQL field overdue for removal (sunset: $sunset_date)" \
          --label "graphql,deprecation" \
          --body "Automated deprecation check found a field past its sunset date."
      fi
    done

9. Deprecation-Strategien im Vergleich

Es gibt mehrere gängige Strategien, um Felder in GraphQL auslaufen zu lassen. Die folgende Tabelle stellt die wichtigsten gegenüber und zeigt, wofür sie sich jeweils eignen.

Strategie Wie sie funktioniert Geeignet für
Sofort löschen Feld verschwindet ohne Vorwarnung Niemals empfehlenswert, garantiert Breaking Change
@deprecated ohne Frist Markierung ohne konkretes Enddatum Erste Sichtbarkeit, aber ohne Zeitdruck bleibt Feld liegen
Deprecation mit Sunset-Datum + Tracking Frist, Nutzungsmessung, Eskalation an aktive Clients Empfohlener Standard für produktive GraphQL-APIs
Gateway-Enforcement (Warn-Header, 410) Technische Eskalation am API-Gateway Letzte Stufe bei hartnäckigen, nicht migrierten Clients

Die Kombination aus Sunset-Datum und aktivem Nutzungs-Tracking liefert in der Praxis die beste Balance zwischen Aufräumtempo und Stabilität für Konsumenten. Reine Direktiven-Markierung ohne Frist ist besser als nichts, führt aber erfahrungsgemäß zu Schemas, die Jahre alte deprecated Felder ansammeln, weil nie ein konkreter Zeitpunkt zum Handeln zwingt.

Mironsoft

GraphQL Schema-Lifecycle und API-Governance

Alte GraphQL-Felder ohne Breaking Change entfernen?

Wir richten für euer Schema einen vollständigen Deprecation Workflow ein, von der @deprecated-Direktive über Nutzungs-Tracking bis zur automatisierten CI-Prüfung überfälliger Sunset-Fristen.

Deprecation-Audit

Bestehendes Schema auf unmarkierte Altlasten und fehlende Fristen prüfen

Usage-Tracking

Feldnutzung pro Client messen und Entfernungsentscheidungen datenbasiert treffen

CI-Automatisierung

Automatische Tickets bei abgelaufenen Sunset-Fristen und aktiver Restnutzung

10. Zusammenfassung

Ein sauberer GraphQL Deprecation Workflow besteht aus vier Bausteinen, die zusammen funktionieren müssen: konsequente Markierung mit @deprecated und aussagekräftigem Reason-Text auf Feldern, Enum-Werten und Argumenten, aktives Nutzungs-Tracking, das echte Daten statt Vermutungen liefert, ein konkretes Sunset-Datum mit klarer Kommunikation über Changelogs und direkte Nachrichten an betroffene Teams, sowie CI-Automatisierung, die überfällige Deprecations aktiv aufspürt statt sie liegen zu lassen.

Wer diesen Workflow konsequent anwendet, kann ein wachsendes GraphQL-Schema über Jahre hinweg aufräumen, ohne jemals eine unangekündigte Breaking Change zu produzieren. Der Aufwand für Tracking und Automatisierung zahlt sich spätestens dann aus, wenn ein Schema Dutzende Module und externe Partner-Integrationen umfasst, bei denen niemand im Team mehr aus dem Kopf weiß, wer welches Feld noch verwendet.

GraphQL Deprecation Workflow — Das Wichtigste auf einen Blick

@deprecated-Direktive

Auf Feldern, Enum-Werten und Argumenten einsetzen, mit Ersatzfeld und Sunset-Datum im Reason-Text.

Nutzungs-Tracking

Pro Feld und Client messen, statt anzunehmen, dass ein Feld unbenutzt ist.

Sunset-Fristen

Konkretes Datum, dokumentiert im Changelog, kommuniziert an aktive Konsumenten.

CI-Automatisierung

Überfällige Deprecations automatisch erkennen und Tickets oder PRs generieren.

11. FAQ: GraphQL Deprecation Workflow

1Löschen vs. Deprecation?
Löschen bricht sofort jede Query. Deprecation markiert das Feld zunächst nur, es bleibt funktionsfähig, während Nutzung gemessen und eine Frist kommuniziert wird.
2Wo @deprecated einsetzen?
Auf Feldern, Enum-Werten und Argumenten. Alle drei konsequent markieren, sonst bleiben Teile der Deprecation unsichtbar.
3Wie lange sollte die Frist sein?
Intern drei bis sechs Monate, extern eher sechs bis zwölf Monate. Entscheidend ist ein konkretes Datum, keine vage Zukunft.
4Nutzung eines Feldes messen?
Mit Nutzungs-Tracking im Resolver, idealerweise mit Client-Identität über Header oder persisted-query-Manifest.
5Frist abgelaufen, Feld noch genutzt?
Eskalation an das verantwortliche Team, keine automatische Entfernung. Erst nach direktem Kontakt entfernen.
6Codegen und IDE-Warnung?
GraphQL Code Generator überträgt @deprecated als JSDoc-Tag, VS Code zeigt das Feld im Autocomplete durchgestrichen.
7Gateway erzwingt Deprecation?
Ja, zunächst Warn-Header, in letzter Eskalationsstufe ein 410-Status für das betroffene Feld.
8Deprecated Attribute in Magento?
Attribut und Resolver synchron behandeln. Fehlt das Attribut im Backend, liefert das Feld künftig konsequent null.
9Überfällige Deprecations automatisiert finden?
CI-Pipeline durchsucht SDL nach @deprecated, extrahiert das Sunset-Datum und erstellt automatisch ein Ticket bei Überschreitung.
10Entfernung im Patch-Release?
Nein. Die tatsächliche Entfernung ist eine Breaking Change und gehört in ein Major-Release nach abgelaufener Frist.