Skip to content

CONCEPT Cited by 1 source

Schema linter enforcement

Definition

Schema linter enforcement is the discipline of running a build-time static-analysis tool over an API schema and failing the build when the schema violates the organisation's governance rules. The linter is a CI gate: rules live as executable checks in the build pipeline, and the PR cannot merge until the schema passes.

The mechanism is the enforcement arm that turns declarative schema annotations (directives, naming conventions, structural conventions) from documentation of intent into gates on merge. Without a linter, a policy like "fields with a sensitive-keyword name must carry @sensitive" is an aspiration that new team members forget; with a linter, the rule is a property of the repository that cannot be bypassed without explicit opt-out.

Contrast with runtime enforcement

A policy can be enforced at three points along the development lifecycle, each with different cost / coverage / blast-radius trade-offs:

Enforcement point Cost Coverage Failure mode
Documentation ("developers should add @sensitive") Zero engineering Depends on developer recall Silent leak when forgotten
Linter (build-time) Moderate engineering (write rules, wire CI) 100% of merged schema changes Build fails before merge
Runtime check (e.g. deny at execute) Higher engineering (runtime code path) 100% of runtime traffic Request fails; customer impact

Schema linter enforcement sits in the sweet spot for schema-shaped policy: schemas change infrequently enough that build-time check is a fast feedback loop, and the linter catches problems before customer-facing failure modes can happen.

The Zalando UBFF worked example

Zalando uses multiple linter rules that interact with schema directives:

  1. PII keyword detection → @sensitive forcing function. The linter fails any PR whose schema contains a field or argument name matching a bootstrapped keyword list — password, email, phone, bank, bic, account, owner, order, token, voucher, customer — unless the field has @sensitive applied. This prevents the policy-by-discipline failure mode where a new engineer adds a sensitive field without realising redaction is a requirement.

  2. @final enum value protection. The post explicitly notes: "It is only used in our GraphQL linter that executes during the build time and prevents additions of new values to enums which are marked as final." The linter compares the current PR schema against the base branch; any new value on a @final-marked enum fails the build. To make a dangerous change, the engineer must first open a PR that removes @final, and only then open a second PR adding the value (concepts/final-enum).

  3. Persisted-query compatibility (implicit). The persist-time validator is a linter-shaped gate on the client side: a client that references a @draft-marked field or an unauthorised @allowedFor- scoped field fails persist registration, preventing the query from reaching production (patterns/directive-based-field-lifecycle).

(Source: sources/2023-10-18-zalando-understanding-graphql-directives-practical-use-cases-at-zalando).

Why linters succeed where documentation fails

  • Documentation decays. A convention written in a wiki loses fidelity as the team grows; new members don't read every wiki page before adding a field.
  • Policy without enforcement biases toward forgetting. If the policy is "please add @sensitive to fields containing PII", the default in any PR is no directive, and forgetting is silent.
  • Linters invert the default. The default in any PR is the linter passes; violating the policy requires an explicit opt-out (lint-disable comment, justification in PR description, or reviewer sign-off).
  • Audit trail. Every lint failure and every exception-documented PR is visible in the build history, giving the policy team a feedback loop on which rules need tuning.

Keyword-based linting is a floor, not a proof

The keyword approach has a known false-negative profile: oddly-named fields escape detection (contactInfo.primary for an email doesn't match email). Zalando's post is aware of this — the directive must still be applied manually in blind spots; the keyword linter catches the obvious cases and backstops "I forgot" failures without attempting to be a complete PII classifier. The keyword list is bootstrapped from the organisation's own field- naming patterns and extended as new blind spots surface.

For higher-precision PII detection, organisations can layer semantic classification (ML-based PII detectors, regex on sample values, or the @sensitive directive at FIELD_DEFINITION with a central taxonomy) on top — the linter remains the first-line enforcement mechanism.

Other schema-linter instances on the wiki

Non-directive-shaped schema linting has broader precedent:

Seen in

Last updated · 501 distilled / 1,218 read