Schema Proposals and Approval Workflows
A schema proposal is a reviewable, versioned draft of a schema change that must move through draft, review, and approval before it is implemented via publish — the governance layer that sits in front of the registry in Schema Registry and Managed Federation. Checks prove a change is safe to compose; proposals prove it is the agreed change, with the right reviewers signing off before any SDL reaches production.
When to use this pattern
- Several teams contribute fields to shared entities and the owning team needs a veto before a change lands.
- You need an auditable record of who approved each schema change and why, not just a git diff.
- You want to enforce that production schema changes cannot be published without prior review.
Prerequisites
The proposal lifecycle
A proposal is a state machine. Each state is an explicit gate, and changes cannot skip ahead.
Draft. An author opens a proposal against a variant and edits the proposed subgraph SDL — adding a field, deprecating one, evolving an entity. The draft is a working copy of the schema; nothing is published and no client is affected. The author iterates here until the shape is ready for review.
Under review. The author submits the proposal. Reviewers — typically the owners of the types the proposal touches — examine the diff, leave comments, and request changes. This is where cross-team negotiation happens: the products team proposing a new field on a shared User entity must get the accounts team, which owns User, to agree. Review can loop back to draft any number of times.
Approved. Once the required reviewers approve, the proposal is marked approved. Approval is a recorded, attributable decision — it is the authorization to implement, not the implementation itself. The schema is still not live.
Implemented. The approved SDL is published with rover subgraph publish, which recomposes the supergraph and launches it (see Publishing Subgraph Schemas with the Rover CLI). The registry can track that the publish matches an approved proposal, closing the loop between intent and reality.
┌────────┐ submit ┌──────────────┐ approve ┌──────────┐ publish ┌──────────────┐
│ Draft │ ────────▶ │ Under review │ ────────▶ │ Approved │ ────────▶ │ Implemented │
└────────┘ └──────────────┘ └──────────┘ └──────────────┘
▲ request changes │
└────────────────────┘
The value of the explicit Approved state is that it separates deciding from doing. A change can be agreed today and implemented during a release window next week, and the approval still stands as the record of why the schema looks the way it does.
Governance for shared types
Shared types are where proposals earn their keep. In a federated graph, an entity like User may be defined in one subgraph and extended by several others. Without a review gate, any team can add or change fields on the shared type and discover the conflict only at composition — or worse, ship a semantically wrong field that other teams now have to live with.
A proposal makes the owning team the gatekeeper. Designate one subgraph as the canonical owner of each shared entity (the team that holds the @key and the core fields), and require that any proposal touching that type be approved by that owner. This turns “whoever publishes last wins” into “the owner decides”, which is the only sustainable model once more than two teams share a type. Encode the ownership map explicitly so reviewers are unambiguous, the same map that drives your Type Ownership and Shared Schema Contracts and the use of the @shareable directive for overlapping types.
Reviewer assignment
Reviewers should be assigned by what the proposal touches, not by who happened to open it. The reliable pattern is type-ownership-driven assignment: derive the set of changed types from the proposal diff, map each type to its owning team, and require an approval from each affected owner.
# .schema-governance.yml — type ownership drives reviewer assignment.
# CI parses the proposal diff, maps changed types to owners, requests review.
ownership:
User: accounts-team # canonical owner of the User entity
Product: catalog-team
Order: checkout-team
Money: platform-team # shared scalar/value type
policy:
# A proposal touching a type requires approval from that type's owner.
require_owner_approval: true
# Cross-cutting changes (e.g. shared scalars) need platform sign-off too.
global_reviewers:
- platform-team
The principle: the reviewer set is a function of the diff. A proposal that only adds a field to a type your own team owns can be self-approved under a light policy; a proposal that modifies a shared entity must collect the owning team’s approval. This keeps review friction proportional to blast radius.
Linking proposals to checks
Proposals and checks are complementary, and the strongest workflows wire them together. A proposal carries the intended SDL; a check tells you whether that SDL is safe. Run rover subgraph check against the proposal’s SDL so reviewers see the composition and real-traffic verdict alongside the diff they are approving — nobody should approve a change that fails composition or breaks a live operation.
# Run a check against the proposal's SDL so the verdict is attached to the review.
# Reviewers approve a change they can SEE is composition-safe and traffic-safe.
rover subgraph check my-graph@production \
--name products \
--schema ./products/proposed-schema.graphql
# FAILURE -> the proposal should not be approved until the SDL is fixed
# PASS -> reviewers approve the agreed, verified change
This gives the review two independent signals: the check answers “is it safe?” and the human reviewers answer “is it right?”. See Apollo Studio Schema Checks for Managed Federation for configuring those checks and Schema Validation in CI/CD Pipelines for the surrounding CI.
Enforcing proposal-backed changes
A governance workflow only holds if it cannot be bypassed. The enforcement goal is simple: a publish to a protected variant must correspond to an approved proposal. Implement this at the CI boundary — the publish step verifies an approval exists before it runs, and direct publishes that skip review are rejected.
name: Publish (proposal-gated)
on:
push:
branches: [main]
paths: ['products/schema.graphql']
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rover
run: |
curl -sSL https://rover.apollo.dev/nix/latest | sh
echo "$HOME/.rover/bin" >> $GITHUB_PATH
- name: Verify an approved proposal backs this change
run: ./scripts/assert-approved-proposal.sh products ./products/schema.graphql
# Fails the job if no approved proposal matches the SDL being published.
- name: Publish only after proposal check passes
run: |
rover subgraph publish my-graph@production \
--name products \
--schema ./products/schema.graphql
env:
APOLLO_KEY: ${{ secrets.APOLLO_KEY }}
Combine this CI gate with branch protection and a Studio policy that flags any publish to a protected variant without a corresponding approved proposal. The result is that the only path to production schema change runs through review — exactly the discipline managed federation needs given how fast a publish propagates.
Verification steps
- Open a proposal, request changes, and confirm it cannot be approved while changes are outstanding.
- Approve with the required owner and confirm the approval is recorded with reviewer and timestamp.
- Attempt a direct
rover subgraph publishwith no approved proposal and confirm the CI gate rejects it. - Publish the approved SDL and confirm the registry associates the resulting launch with the proposal.
Common mistakes & gotchas
Approving without a check. A proposal that reads well can still break composition or a live operation. Always attach a rover subgraph check result to the review so approval is informed.
Ownership that does not match reality. If the ownership map names a team that no longer owns the type, reviews route to the wrong people and rubber-stamp. Keep the ownership map current as services move between teams.
A bypassable gate. If publishing to production is possible without an approved proposal, the workflow is theater. Enforce the proposal check in CI and back it with variant protection so there is no side door.
Frequently Asked Questions
What does a schema proposal add on top of schema checks?
Checks are automated and answer whether a change is safe to compose and safe against live traffic. A proposal adds human governance — a draft, review, and recorded approval — answering whether the change is the agreed one and authorized by the owners of the affected types. The strongest workflow runs a check against the proposal’s SDL so reviewers approve a change that is both verified and agreed.
Who should review a schema proposal?
The owners of the types the proposal touches. Derive the changed types from the diff, map each to its owning team via an ownership file, and require approval from each affected owner. Add a global reviewer (such as a platform team) for cross-cutting changes like shared scalars so blast radius drives the reviewer set.
How do I stop someone from publishing a schema change without review?
Enforce it at the CI boundary: the publish step verifies an approved proposal matches the SDL before running rover subgraph publish, and you back this with branch protection and a Studio policy on protected variants. If a publish to production is possible without an approved proposal, the gate is bypassable and the governance is ineffective.