The GraphQL federation decision record: why the gateway architecture you chose determines your schema evolution complexity and your resolver blast radius

GraphQL federation is adopted when the schema ownership problem gets expensive enough to justify the additional infrastructure. A team that has been maintaining a single GraphQL schema for eighteen months reaches a point where every schema change requires a coordination meeting between three teams, a PR approval from a schema council, and a deployment from a shared schema repository — because the schema has grown to 400 types across 60 services and there is no clean way to assign ownership of each type to the team that owns the underlying data. The decision to adopt Apollo Federation, or Schema Stitching, or a custom gateway approach, is made in a planning session where the focus is on solving the ownership problem. The question of how the gateway architecture affects query planning, what happens to client queries when a subgraph is unavailable, how authorization context propagates from the gateway to each subgraph, and what the process is for making breaking changes to a shared entity type — these questions are not answered in the planning session because they feel like implementation details that can be sorted out after the architecture is in place. What is not sorted out becomes the source of incidents, multi-week migrations, and production authorization failures that trace directly back to the unwritten decisions made during the federation adoption sprint.

The GraphQL federation decision record is missing from most federated architectures because federation feels like a technical choice within a bounded domain — GraphQL schema management — rather than a cross-cutting decision with consequences that compound for years. What is actually decided when a team adopts federation is the schema evolution coordination model for every type change made thereafter, the failure propagation model that determines which client queries degrade when any given service has an incident, the authorization surface that every subgraph team must understand and implement correctly, and the query performance model that determines whether adding a new cross-subgraph relationship is a two-line schema change or a two-week performance investigation. Those decisions belong in a decision record. Without one, the team that owns the federated gateway three years after adoption is operating a system whose design constraints are visible only in the code and in the accumulated tribal knowledge of engineers who were in the original planning session — or, more commonly, who have since left.

Two things that happen when the decision is not written down

The type rename that required three weeks of cross-team coordination

A 55-person SaaS company adopted Apollo Federation v1 at 18 engineers, during the quarter when the engineering team split into three product squads: one owning the core product experience, one owning the integration platform, and one owning billing and account management. The federation adoption was straightforward: the existing monolith GraphQL schema was split into three subgraphs aligned to the squad boundaries, a gateway was deployed on top of Apollo Gateway, and each squad became the owner of their subgraph's types and resolvers. The decision had been made by the platform engineering lead in a two-hour architecture session and captured in a brief Confluence page that described the subgraph structure and the Apollo Studio managed federation setup, but did not address the type ownership model beyond the initial split, the breaking change policy for shared entity types, or the process for cross-subgraph type dependencies.

The federation worked well for fourteen months. Each squad shipped schema changes independently, the gateway composed subgraph schemas at deploy time, and the Apollo Studio schema checks caught most intra-subgraph breaking changes before they reached production. The problem surfaced when the product team decided to rename the core entity from Account to Workspace — a branding change the go-to-market team had been requesting for eight months and that the engineering team had repeatedly deferred because "it touches everything." The scope of "everything" had never been quantified. When the integration platform squad began the rename, they discovered that Account was defined as a federation entity with a @key directive in all three subgraphs: the core product subgraph owned the authoritative Account type and defined id and name fields, the integration platform subgraph extended Account with integration-related fields (connectedApps, webhookEndpoints, apiKeyCount), and the billing subgraph extended Account with subscription fields (plan, billingEmail, usageSummary).

A rename of Account to Workspace required coordinated changes across all three subgraphs simultaneously. The Apollo composition model in v1 required that the entity type name be identical in every subgraph that defines or extends it — you cannot rename the type in one subgraph and leave the old name in the others because the composition will fail to link the entity extensions. The team had three options: (1) rename in all three subgraphs in a single coordinated deployment, (2) introduce a new Workspace type that extends Account with an adapter layer while deprecating Account, or (3) use an alias approach at the schema level while maintaining backward-compatible field names. Each option required design work, cross-squad alignment, and a deployment strategy that avoided a window where the gateway received an incompatible combination of subgraph schemas during the rollout.

The coordinated deployment approach required the three squads to freeze all other schema changes for two weeks while the rename was implemented, tested in a staging environment that replicated the federated gateway configuration, and deployed with a zero-downtime cutover strategy. The staging environment had not been designed to replicate the federation composition behavior — it used a simplified single-service schema rather than the actual three-subgraph configuration — and the team discovered two composition errors in the renamed schema that only appeared in the full federated context. Both errors required schema fixes that were caught in staging only because the team had specifically built the multi-subgraph staging environment during the rename project, not because the existing development workflow would have caught them. The total elapsed time from kickoff to production cutover was three weeks; the team's original estimate had been three days. A federation decision record that had addressed the entity renaming process — specifically, the fact that entity type names are shared across all subgraphs that extend them and therefore cannot be renamed in a single-subgraph operation — would have given the team the three-day estimate's error rate before the project was committed to the product roadmap.

The inherited Schema Stitching configuration that blocked the federation migration

A 30-person developer tools company had contracted a GraphQL consulting firm to design their API layer two years before the team attempted a federation migration. The consulting firm had implemented Schema Stitching using the @graphql-tools/stitch library — a well-regarded approach at the time, predating the team's awareness of Apollo Federation — with a hand-authored stitching configuration that defined merge types, delegate resolvers, and batching strategies for cross-service queries. The configuration was a 1,400-line JavaScript file that defined how types from six backend services were merged into a unified schema. The consulting engagement had ended after the implementation was complete, and the file had been handed to the in-house team with a brief README covering deployment and environment variable configuration but no documentation of the design decisions in the stitching configuration itself: why specific fields were delegated to specific services, what the batching strategy was for the User type's cross-service fields, or what the expected behavior was when one of the six upstream services was unavailable.

The migration to Apollo Federation was initiated when the engineering team decided to hire backend engineers with federation experience and found that the team's Schema Stitching configuration was difficult to onboard new engineers onto. The stitching configuration's delegate resolver syntax was not widely known; the batching strategy required understanding a specific DataLoader pattern that the consulting firm had implemented and that had subtle correctness properties the team had not fully understood; and the merge configuration for the User type — which was contributed to by three different services — required reading all three service schemas and the stitching configuration simultaneously to understand what the final merged type looked like. A new engineer who wanted to add a field to the User type needed to determine which service should own it, how to declare the delegation in the stitching configuration, and whether the new field would interact with the existing batching strategy.

The federation migration was scoped at six weeks. The actual elapsed time was four months, and the scope expansion traced entirely to the incompatibility between Schema Stitching's delegation model and Apollo Federation's entity resolution model. In Schema Stitching, cross-service type merging is expressed as a series of merge configurations that tell the stitching layer which fields to fetch from which service for a given type, with manual specification of the delegation query structure. In Apollo Federation, cross-service entity resolution is expressed via the @key directive and a reference resolver that the subgraph defines to hydrate entity fields given a set of key fields. These two models are not structurally equivalent — the stitching configuration's delegation logic had to be rewritten, not translated, for each cross-service type. Each rewrite required understanding the existing stitching logic well enough to produce equivalent semantics in the federation model, which required reading the stitching configuration documentation, testing the existing behavior against a query set that covered the cross-service cases, and verifying that the federation implementation produced identical results.

The team spent the first month auditing the existing stitching configuration to produce a cross-service type dependency map — a document that described every type that crossed service boundaries, which service owned the canonical definition, which services contributed extension fields, and what the expected resolver behavior was for each cross-service field. This was the document that should have been written when the stitching configuration was originally built. Without it, the migration team was reverse-engineering the design intent from a 1,400-line configuration file. Two cross-service types had subtle bugs in the stitching configuration that had been masked by client-side data fetching patterns — the stitching layer was returning incorrect data for a specific query combination that no client happened to be using, but that the federation implementation correctly rejected during composition validation. Fixing those bugs added an additional three weeks to the migration because the correct behavior had to be determined from first principles rather than from a specification. Both outcomes — the three-week type rename and the four-month stitching migration — are examples of the same underlying failure: the federation adoption decision was made without documenting the design constraints that govern all subsequent schema evolution work, so those constraints were discovered under pressure rather than incorporated into planning from the start.

Three structural properties that are set at federation architecture selection time

The schema boundary definition and the type ownership conflict rate

Every federated schema has a boundary definition model: the rules that determine which team owns which types and which fields, and what happens when two teams need to add fields to the same type. Apollo Federation's entity model makes one team the authoritative owner of each entity's key fields and reference resolver; other teams can extend the entity with additional fields, but the extension must not conflict with the base definition. This model is clean when the ownership boundaries align with the organizational boundaries — when the team that owns the User service also owns the User type and all its core fields — but creates conflict when a type is inherently cross-functional. A Document type might be owned by the content team for its core metadata fields, but the permissions team needs to add ACL fields, the billing team needs to add usage attribution fields, and the search team needs to add index-state fields. In federation, all three teams extend the Document entity, and a conflict in their extensions — two teams defining a field with the same name but different types or semantics — fails composition at deploy time and blocks all three teams' deployments until resolved.

The type ownership conflict rate — the frequency with which two teams attempt to add conflicting changes to the same entity — is determined by the schema boundary definition and the organization's team structure. A schema boundary definition that aligns type ownership cleanly with organizational ownership produces a low conflict rate. A schema boundary definition that cuts across organizational boundaries, or that assigns ownership of shared platform types (users, accounts, permissions, audit records) to a single team that becomes a bottleneck for all other teams' schema changes, produces a high conflict rate. Document the boundary definition model in the federation decision record: which team owns each entity type, what the process is for requesting an extension from a team that owns a type, and how conflicts between extension proposals from different teams are resolved. This documentation is the governance model for the schema — without it, conflict resolution happens ad-hoc and the outcomes depend on whoever argues most persuasively in the PR comment thread.

The boundary definition also determines the schema evolution cost for the microservices-vs-monolith decision — when a team refactors service ownership boundaries, moving functionality from one service to another, the schema boundary must move with it. If the schema boundary is not documented, the ownership of each type must be reconstructed from the code, and the refactoring scope expands to include a schema audit alongside the service migration.

The resolver blast radius and query plan failure modes

The resolver blast radius is the set of client queries that fail or degrade when a single subgraph is unavailable. In a federated schema, most queries require fetching data from multiple subgraphs — a query for a user's documents with their subscription status touches the user subgraph, the document subgraph, and the billing subgraph. If the billing subgraph returns a 503, the gateway has three options depending on its configuration: fail the entire query and return an error to the client, return partial data with the billing fields as null and an error in the errors array, or return partial data with the billing fields omitted entirely if they are optional. The chosen behavior is not a default — it requires explicit configuration and a field nullability contract that the client teams must understand and handle.

The blast radius is minimized by three mechanisms that must each be explicitly chosen and documented. The first is field nullability: making all cross-subgraph fields nullable in the schema ensures that a subgraph failure can be handled as a partial result rather than a query failure. The trade-off is that every nullable field is a potential null pointer in every client implementation — mobile clients, web clients, and third-party integrations must all handle the null case correctly. A field that was non-nullable for eighteen months and is made nullable during a blast-radius reduction project requires changes in every client that currently treats the field as guaranteed present. The second is query plan parallelism: the gateway's query planner can be configured to fetch from independent subgraphs in parallel rather than sequentially, which reduces latency for queries that span multiple subgraphs but requires the gateway to have sufficient connection capacity to all subgraphs simultaneously. The third is entity resolution tolerance: when the gateway is fetching entity extensions from a failed subgraph, the failure mode — skip the extension fields vs. fail the entity resolution — must match the client's expectation of what a partial entity looks like.

Document the chosen blast radius policy in the federation decision record. Include the field nullability conventions (which field categories are nullable and why), the query plan parallelism configuration, and the expected client behavior when a subgraph is unavailable. This documentation gives client teams the contract they need to build resilient consumers and gives the on-call engineer responding to a subgraph incident the information they need to understand which client features are degraded rather than discovering the blast radius empirically under pressure. The blast radius policy also connects to the observability platform decision record: the alerts and dashboards needed to detect subgraph failures and measure their impact on query success rates are different from the alerts needed for a monolith GraphQL API, and they need to be designed with the blast radius model in mind.

The authentication and authorization propagation model and its security surface

Authentication and authorization propagation is the most security-sensitive structural decision in a federated GraphQL architecture. In a monolith GraphQL schema, authentication is validated once per request at the gateway layer and the authorization context is available to all resolvers in the same process. In a federation, each subgraph is a separate service that receives HTTP requests from the gateway, and each subgraph must decide independently how to validate the caller's identity and authorization context. The propagation model — how the gateway passes the user's identity and permissions to each subgraph — determines the security surface of the entire federated API.

The most common propagation pattern is header forwarding: the gateway validates the incoming request's Authorization header (JWT, session token, or API key), and forwards the same header to each subgraph. Each subgraph independently validates the credential and enforces its own authorization rules. This pattern is straightforward and gives each subgraph independent control over its authorization enforcement, but it requires each subgraph to implement credential validation — or to call a shared authentication service — and creates a latency cost proportional to the number of subgraphs that make auth service calls per request. More critically, it creates a security dependency: if a subgraph is misconfigured to skip credential validation, it will accept requests from the gateway without verifying the caller's identity, and an attacker who can send requests directly to the subgraph's HTTP endpoint (bypassing the gateway) will be able to access the subgraph's data without authentication.

The header enrichment pattern — where the gateway validates the credential, extracts claims (user ID, roles, organization membership), and forwards the enriched claims as custom headers to subgraphs — shifts the validation responsibility to the gateway and simplifies subgraph implementation. But it creates a different security risk: if the gateway is misconfigured or bypassed, and if subgraphs trust the custom headers without verifying their source, those subgraphs can be called with arbitrary claim headers by any caller that can reach them directly. The service-to-service authentication pattern — where the gateway authenticates to each subgraph using a service credential (mTLS certificate, internal JWT signed with a shared secret), and the end-user context is passed as a structured payload that subgraphs verify was produced by the gateway — addresses the bypass risk but requires managing service credentials and their rotation across all subgraphs. Document the chosen propagation model in the federation decision record, including the security assumptions it depends on (that subgraphs are not reachable directly from untrusted callers, or that service credentials are rotated on a documented schedule) and the mitigation if those assumptions are violated. The propagation model is the authorization surface that every new subgraph team must implement correctly — a team that adds a subgraph without understanding the propagation model will implement a different pattern and create an authorization inconsistency across the federated API. This directly relates to the authentication strategy decision record and the authorization model decision record — the federation propagation model must be consistent with both.

Five sections the GraphQL federation decision record should contain

1. Federation approach selection and gateway implementation

Document the chosen federation approach — Apollo Federation v1, Apollo Federation v2, Schema Stitching, GraphQL Mesh, a custom gateway, or a no-federation monolith schema with service-level data fetching — and the rationale for the choice. For an Apollo Federation selection, document the specific version (v1 or v2) and the router implementation (Apollo Gateway vs. Apollo Router), because the migration path between versions is non-trivial and the router choice affects the plugin model available for custom middleware. Include the alternatives that were evaluated and why they were rejected: Schema Stitching vs. federation (Schema Stitching is more flexible but less standardized and harder to onboard new engineers onto), federation v1 vs. v2 (v2's shared types model is more permissive but requires explicit @shareable declarations), managed federation via Apollo Studio vs. a self-managed supergraph SDL file (Studio adds schema change notifications and composition validation in CI but adds a dependency on Apollo's managed service).

Document the migration path from the current approach to the next one if the current approach's limitations are reached. If the team adopts Schema Stitching today and anticipates migrating to federation in two years, the migration cost should be estimated at decision time, not when the migration is already committed to the roadmap. Include the trigger criteria for re-evaluating the federation approach: a specific scale threshold at which the query planning overhead becomes unacceptable, a schema complexity level at which the current composition tooling produces composition errors that are difficult to diagnose, or an organizational change that shifts the schema ownership model in a way the current federation approach cannot accommodate. The GraphQL vs. REST decision record is a prerequisite — if the team has not documented why they chose GraphQL over REST, the federation decision record's rationale for federation-over-monolith-schema is incomplete.

2. Subgraph ownership model and type boundary definition policy

Document which team owns each subgraph, what types each subgraph is responsible for, and what the process is for cross-subgraph schema changes. Include the entity type ownership model: for each entity type in the schema, document which subgraph defines the authoritative @key fields and reference resolver, and which subgraphs are permitted to extend the entity with additional fields. This list does not need to be maintained as a separate document from the federation decision record — a simple table of entity types and their owning subgraphs is sufficient as a starting point, with the understanding that it will be updated as the schema evolves.

Document the extension request process: when a team that does not own an entity type needs to add an extension field, what is the process for requesting the extension? Is it sufficient to open a PR to the owning subgraph's repository with the extension? Does the owning team need to approve the extension before it ships? Is there a composition validation step in CI that catches conflicts before the PR is merged? The absence of a documented process means that extension requests happen informally, and the first time two teams simultaneously submit conflicting extensions to the same entity, the resolution is ad-hoc. Include the conflict resolution policy: when two extension proposals conflict at composition time, how is the conflict resolved? By convention (the owning team arbitrates), by schema design review (a designated schema council reviews conflicts), or by negotiation (the two teams resolve the conflict directly)? The policy determines how long schema conflicts block deployments and how much coordination overhead each schema change carries. This section also connects to the API schema design decision record — the response shape and field naming conventions documented there should apply consistently across all subgraphs.

3. Breaking change policy and schema versioning strategy

Document what constitutes a breaking change in the federated schema, the detection mechanism, and the deprecation process. In a federated API, breaking changes have three distinct audiences: clients (consumers of the public API surface), other subgraphs (teams that extend entities defined by this subgraph), and the gateway (the composition validator that must successfully compose all subgraph schemas into a supergraph). A change that is non-breaking for clients may be breaking for subgraph composition: removing a field from an entity type is breaking for composition if another subgraph's extension references that field in a @requires directive, even if no client query accesses the field directly.

Document the breaking change detection tooling: Apollo Studio schema checks, Rover CLI's subgraph check command, or a custom schema validation step in CI. Include the schema check configuration — which client operations are included in the check, how the check handles fields that are defined in the schema but never queried (safe to remove without a breaking change warning), and how long the operation history window is for determining whether a field is in active use. Document the deprecation process: when a field is deprecated with the @deprecated directive, what is the expected timeline between deprecation and removal, how are consumers of the deprecated field notified, and what is the authorization process for removing a deprecated field if consumers have not yet migrated? The deprecation timeline connects directly to the API versioning decision record — the same deprecation window and notification process that applies to REST API versions should apply consistently to GraphQL field deprecations. Without a documented deprecation policy, fields accumulate @deprecated markers and are never removed because no team has the authority to remove a deprecated field without knowing whether any consumer still depends on it.

4. Authentication and authorization propagation model

Document the chosen propagation pattern (header forwarding, claim enrichment, or service-to-service credential with user context payload) and the implementation contract that every subgraph must satisfy. Include the specific headers that the gateway sends to each subgraph, the expected format of each header, and what the subgraph should do when a required header is absent or malformed. This implementation contract should be part of the subgraph development checklist — when a new subgraph is added, the team that builds it should read the propagation model section of the federation decision record to understand what authentication validation is expected before the subgraph is deployed to production.

Document the network boundary policy: which services are permitted to call subgraphs directly (bypassing the gateway), and what authentication is required for direct subgraph calls. In most federation deployments, subgraphs should only accept calls from the gateway and from other authorized internal services, and should reject calls from any caller that cannot present a valid service credential. If this network boundary is enforced by a service mesh, a network security group, or an API gateway rule rather than by the subgraph's own authentication logic, document the enforcement mechanism so that the team responsible for deploying new subgraphs knows what infrastructure policy applies. The propagation model and network boundary policy together determine the authorization security properties of the federated API — a gap in either can produce an authorization bypass that is difficult to detect because it only manifests when a caller reaches a subgraph directly rather than through the gateway's request path. The API gateway decision record should document whether the gateway is the sole entry point to the federated API or whether subgraphs are reachable through other paths.

5. Query plan caching strategy and performance model

Document the query plan caching strategy: how the gateway caches compiled query plans, what the cache invalidation trigger is (typically a schema composition update), and what the expected query plan compilation latency is for novel queries not present in the cache. In Apollo Router, query plan compilation is computationally expensive for complex queries that span multiple subgraphs — a query that joins five entity types from four subgraphs may require 50–100 milliseconds of planning time on the first execution, which is unacceptable if the cache miss rate is high (for example, if clients send highly parameterized queries that produce unique query shapes). Document the expected query complexity distribution: if clients are expected to send a bounded set of query shapes (common in mobile clients with fixed query documents), query plan caching is highly effective. If clients send dynamic queries composed at runtime (common in GraphQL explorer tools and developer integrations), the cache hit rate will be lower and the planning overhead higher.

Include the N+1 query prevention strategy for cross-subgraph entity resolution. When the gateway resolves a list of entities and needs to fetch extension fields for each entity from a second subgraph, the naive implementation sends one reference resolution request per entity — an N+1 pattern at the federation layer. Apollo Federation's @requires and entity batching model is designed to batch these resolution calls, but the batching behavior depends on the subgraph implementing a batch-capable reference resolver (the __resolveReference function that accepts an array of entity references rather than a single reference). Document whether subgraph reference resolvers are required to support batching, and how the requirement is validated in the subgraph development process. An unbatched reference resolver deployed to a subgraph that serves frequently-listed entity types will produce N+1 queries in production — N proportional to the list length — that are invisible in development but produce latency spikes in production when list sizes grow. The caching strategy decision record should be consulted for the query result caching model: a federated API's cache invalidation is more complex than a monolith API's because a cache entry that joins data from three subgraphs must be invalidated when any of the three subgraphs' underlying data changes.

What ties these five sections together is the observation that the API schema design decision record makes for a single schema owner and the federation decision record makes for a distributed schema: federation distributes schema ownership across teams and services, and the governance model that keeps a distributed schema coherent — the type ownership model, the breaking change policy, the authorization propagation contract, the query plan performance model — must be designed and documented before it is needed. The alternative is a schema that drifts toward incoherence as each team makes locally reasonable decisions without a shared model. By the time the drift is visible — composition errors that arrive unexpectedly, authorization behaviors that differ across subgraphs, query plans that degrade as the schema grows more complex — the accumulated technical debt is proportional to the time the governance model has been absent. A federation decision record written at adoption time costs two days of engineering effort. The three-week type rename and the four-month stitching migration both cost more than that, and both were preventable with documentation.

What to do with the GraphQL federation decision record

The federation decision record's primary audience is the team that builds the second, third, and fourth subgraph — not the team that built the first one. The first subgraph team made the architecture choices; every subsequent team inherits them. A new team that adds a subgraph without reading the federation decision record will implement their authentication model according to their own intuition, define their entity types without checking the ownership model, and write their reference resolver without knowing whether batching is required. Each of these gaps is individually small and individually correctable, but collectively they produce a federated schema where the authorization model, entity ownership, and reference resolver behavior are inconsistent across subgraphs — inconsistencies that manifest as security bugs, composition failures, and performance problems that require auditing all subgraphs to diagnose.

If the federation decision record was not written at adoption time, the most useful reconstruction is not a comprehensive history of every schema change since federation was introduced. The most useful starting point is to answer four questions today: what federation approach and version is the gateway running, and what is the documented migration path if that approach's limitations are reached? What is the type ownership model for the most-extended entity in the schema, and is there a documented process for resolving extension conflicts? What is the authorization propagation model, and does every deployed subgraph implement it consistently? What is the expected behavior when any given subgraph becomes unavailable, and do client teams know what to expect? Those four answers, documented concisely and stored where the gateway configuration lives, are the federation decision record that the next subgraph team will find most valuable.

The ADR template provides a starting structure for the record itself. The WhyChose extractor can surface federation-related decisions that were made in ChatGPT or Claude conversations during the adoption planning — the architecture discussion where the team chose Apollo Federation over Schema Stitching, or the security review where the authorization propagation model was debated, may contain reasoning that was never written into a document and that is now recoverable from the chat export. The decisions never written down essay covers why distributed architecture decisions like federation governance are among the most valuable to reconstruct: they are the decisions whose absence is least visible day-to-day and most expensive to discover when a schema change, a security review, or a new subgraph team finally asks the question the record would have answered.