skip to content

Where does a GraphQL schema check get its baseline, and why keep it in a registry?

level: middleimportance: should knowfreq 43%

answer

  1. diff against what is running
  2. main is often ahead of production
  3. the environment is a parameter
  4. publish after deploy, or it rots
  5. introspection has no history

basics

~20 s

The baseline is the schema currently published for the environment being deployed to. A registry stores that per environment, with a history of who published what and when, so CI can fetch a pinned, auditable answer instead of guessing from a branch.

solid answer

~50 s

The baseline must be **what is actually running where this change is going**, not the SDL on your branch and not the SDL on main — main routinely carries edits that are merged but not yet deployed, so diffing against it hides exactly the change you are shipping. A schema registry is the service that holds this: for each graph and each deployment environment it stores the schema currently published, plus the history of previous publishes and who made them. CI fetches the baseline for the target environment, runs the diff, and after a successful deploy the new schema is published back, becoming the next baseline. You can approximate a registry by introspecting a running server, but that has real drawbacks: introspection is often switched off in production, replicas mid-rollout answer differently, and standard introspection does not report which directives are applied to a field. A registry also gives you the record — the audit trail a removal decision has to point at.

code

pseudocode · 13 lines
pseudocode
# check, at pull-request and again at deploy time
result = schema_check(
    proposed = print_schema(build_schema(branch)),
    baseline = registry.published(graph = "parcel-locker",
                                  environment = target_env)
)

# the half that is easy to forget: close the loop
on deploy_succeeded(target_env):
    registry.publish(graph = "parcel-locker",
                     environment = target_env,
                     schema = print_schema(deployed_schema),
                     commit = git_sha)

go deeper

for a junior

Remember the one rule: compare against what is deployed where you are deploying, not against your branch or against main. Know that a registry is simply the service that stores that answer per environment.

for a middle

Explain the full cycle — fetch the baseline, check, deploy, publish — and what breaks at each step if it is skipped. Be ready to say why a per-environment baseline is not optional once staging exists.

for a senior

Show that you have seen the quiet failures: a job pointed at the wrong environment reports no changes, and a missing publish step turns the output into stale noise the team learns to override. Say how you would detect both.

for a principal

Own the registry as the system of record rather than a lint input. Decide who may publish, how the history is used to justify a removal after the fact, and how other build steps consume a pinned schema artefact instead of a live endpoint.

## The baseline is the whole question A diff is only as good as what it is diffed against. Once you accept that, most of the interesting content of "schema checks" turns out to be about the **baseline**, not the algorithm. The rule is short: *the baseline is the schema currently published to the environment this change will reach.* Everything else is a convenient lie. * Diffing against **the file you started from** tells you what you typed, not what you are about to do to callers. * Diffing against **main** looks respectable and is wrong whenever main is ahead of production. Merge a removal on Tuesday and deploy on Friday; a Wednesday branch diffed against main sees the field already gone and reports no change. * Diffing against **the last release tag** breaks whenever a hotfix, a rollback or a partial deploy has moved production away from that tag. ## What a registry is A schema registry is a service that stores, per graph and per deployment environment, the schema that is currently published there — plus a history: every previous publish, when it happened, and which commit or person produced it. Three things follow from that. **CI has one place to ask.** The check step fetches `published(graph, environment)` and does not have to reason about branches or tags. **Publishing is an explicit event.** A schema becomes the baseline when it is published, which is normally wired to a successful deploy — not to a merge. That ordering is what keeps the baseline honest. **The history is the record.** When someone asks in six months why a field disappeared, the registry says which publish removed it, when, and what evidence was attached to the decision. A checked-in SDL file gives you the same information only if nobody ever rebased or force-pushed. ## Per environment, and why it bites A parcel-locker graph running in staging and production has two baselines, and they are routinely different — staging is where a change lands first. A pipeline that checks every change against staging is checking against a schema that already contains the edit under test. That is the real-world failure, and it is quiet. A team deleted `Compartment.sizeCode`, deployed to staging, then re-ran the pipeline for the production deploy. The production job also read the staging baseline. Result: **no change detected**, green gate, straight into production, and the kiosk firmware still selecting `sizeCode` began failing whole requests — a validation error on the document, not a null field. The check was working perfectly; it was pointed at the wrong side. The defensive shape is the same everywhere: the environment is a parameter of the check, and the job that deploys to an environment must resolve the baseline for *that* environment at the moment it runs. ## Registry versus introspecting the running server You can build a baseline by querying a running server through the introspection meta-fields, and for a small single-service graph that is a reasonable start. Know the four drawbacks: 1. **Introspection is commonly disabled in production**, which is precisely the environment whose baseline matters most. 2. **Mid-rollout, replicas disagree.** Ask during a deploy and you get whichever pod answered — old schema or new, non-deterministically. 3. **Applied directives are largely invisible.** Standard introspection describes the type system and the *definitions* of directives, but does not report which directives are applied where; deprecation state and a custom scalar's specification URL have dedicated fields, and that is roughly the extent of it. If your compatibility story depends on an applied directive, an introspected baseline cannot see it. 4. **There is no history.** Introspection tells you the present. It cannot tell you when a field arrived, or that it was already gone before your change. ## The publish half of the loop A check is one half; the publish is the other, and a check-only setup rots. If nothing publishes the new schema after a deploy, the baseline drifts behind reality and every subsequent check accumulates the same findings until the output is noise nobody reads. The pipeline is a cycle: fetch baseline → check proposed → deploy → publish as the new baseline. Skipping the last step is the most common way a working setup stops working, and it fails silently — the check stays green while comparing against a schema from three sprints ago. ## What a registry is not It is not a runtime dependency. The server does not ask the registry anything to serve a request; if it did, you would have made a schema store a hard dependency of your hot path. It is a build-and-deploy-time record. It is also not defined anywhere in the GraphQL specification — registries, publishes and baselines are entirely an ecosystem practice.

  • Your check runs on every pull request. Why run it again at deploy time?
    Because the baseline moves. A branch checked on Monday was compared against Monday's published schema; if another team deploys on Tuesday, the schema your change lands on top of is no longer the one you were cleared against. Re-running at deploy time, against the baseline resolved at that moment, is the only check whose result is still true when the schema is published.
  • Nobody wired up the publish step after deploys. What does the check output look like six weeks later?
    Stale and loud. The baseline is frozen at the last publish, so every accumulated edit since then is re-reported on every run — the same dozen findings, none of them about the change under review. Teams stop reading it and start overriding by reflex, which is worse than having no gate, because the organisation believes it is protected.
  • Can you use the registry's stored schema as the input to other build steps?
    Yes, and that is a good reason to have one. A published schema artefact is a pinned, named version that other build steps can consume rather than each of them pointing at a live endpoint and getting whatever answered. It also means you can state which schema version a given client build was produced against, which is impossible when the input was an unpinned URL.

The baseline is the signed copy of the contract the other party is holding, not the draft on your desk. Diffing against your own draft always shows agreement.

saying these in an interview costs you the question

  • Diffs the branch against main instead of what is deployed
  • Uses one baseline for staging and production alike
  • Introspects a production server mid-rollout for the baseline
  • Never publishes the new schema after a successful deploy
  • Assumes the running server consults the registry per request
  • Calls the registry part of the GraphQL specification

context