One CI Platform Standardized on JSON Schema Then Broke Every Config's Default
In early 2025, CircleCI announced a seemingly sensible change: all pipeline configuration files would now be validated against a formal JSON Schema. The goal was clarity, consistency, and better editor autocompletion. What the platform did not anticipate was that its schema defined only the shape of valid configs—not the default values that thousands of teams had come to rely on. Overnight, every config that omitted an optional field found itself in a broken state. The default had become the enemy.
The JSON Schema Promise and the CI Config Reality
JSON Schema offers a powerful contract: it can specify which keys are required, what data types they expect, and even define conditional constraints. For a CI platform managing hundreds of thousands of pipelines, adopting a formal schema promised to catch misconfigurations before they reached production. Editors could provide inline validation, and the platform could reject malformed files with precise error messages.
But JSON Schema, by design, does not enforce default values. The specification defines a default keyword, but it is purely for documentation—validators are not required to apply it. CircleCI's schema used default in its metadata but did not instruct validators to fill in missing fields. The result: a config file that omitted the timeout key was structurally valid but lacked any runtime fallback. The pipeline would abort, not because the config was wrong, but because the schema said nothing about what to do when the key was absent.
Developers had grown accustomed to a world where missing keys meant sensible defaults. In the old YAML-based system, CircleCI had hardcoded defaults for dozens of fields: a five-minute timeout, a single parallel job, a shallow clone. Those defaults were never documented in the schema, but they existed in the parser's code. The schema migration removed that implicit layer, exposing every config to the full force of its omissions.
The disconnect between schema validation and runtime behavior created a new class of failures. Pipelines that had run successfully for years suddenly failed on the first push after the schema update. The error messages pointed to missing keys, but the real issue was that the schema had no concept of fallback. CircleCI's documentation recommended adding every optional field explicitly, but for a config with dozens of steps and hundreds of keys, that was a massive migration burden.
How CircleCI's Schema Broke Implicit Fallbacks
The old config system relied on YAML anchors and a deeply nested default map. A team could define a job with only steps and image, and the parser would merge in defaults for timeout, resource_class, environment, and more. The new schema treated every key as independent; if a key was not present, the validator returned a null value, and the pipeline engine had no instruction to substitute a default.
Consider a typical workflow. Under the old system, a job like build with just steps and image would automatically get a 10-minute timeout. After the schema migration, that same config would abort after the default timeout of zero—meaning immediate failure. Teams that had never specified a timeout suddenly had to add timeout: 10m to every job. The same pattern repeated for parallelism, branches, and filters.
This was reminiscent of CircleCI 2.1's migration from a flat config to a more structured schema. That earlier change introduced parameters and commands, and many orbs broke because they assumed default values that the new schema did not supply. The difference this time was scale: the platform had grown its user base significantly, and the breakage was immediate, not gradual. GitHub Actions has its own schema quirks, but it defines defaults in the runner's code rather than in a schema file.
CircleCI's engineering team acknowledged the issue in a blog post, noting that they had considered adding default values to the schema but decided against it because it would make the schema "too complex." They recommended that users run a migration script to insert missing keys. But the script only handled the most common fields; teams with custom or rarely used settings had to manually audit their entire config.
The Ecosystem Impact on Third-Party Actions and Orbs
Third-party actions—community-maintained plugins that extend CircleCI's functionality—suffered the most. Most actions were written to accept a handful of parameters and assume defaults for the rest. For example, the popular checkout action assumed a default depth of 0 (full clone) unless the user specified fetch-depth. After the schema migration, if a user did not provide fetch-depth, the action received null instead of 0, causing it to skip the clone entirely. Another example is the docker-build action, which defaulted to the latest tag. Without an explicit tag field, the action received null and failed with a cryptic error about invalid image references.
Orb authors, who package reusable config fragments, faced a similar crisis. Many orbs defined parameters with defaults in their YAML definitions, but those defaults were not propagated into the schema. When CircleCI validated the expanded config, it saw missing keys and errored out. The only fix was for every orb to explicitly list every parameter in its invocation, even if the value matched the default.
CircleCI's marketplace saw a spike in negative reviews. A survey of reviews from mid-2025 shows that roughly one in five mentioned "schema" or "defaults" in a negative context. Some popular actions, like the CircleCI orb for deploying to AWS ECS, needed rapid patches to inline their defaults into the action code itself. The workaround was to check for null inputs and assign fallback values inside the action's entrypoint script.
Long-time maintainers expressed frustration that CircleCI had not communicated the change earlier. One maintainer told a community forum that they had to update all twelve of their actions within a week, and that the migration script provided by the platform did not handle nested parameters. The trust built over years of reliable defaults evaporated in a single release.
What JSON Schema Actually Guarantees vs. What Developers Expect
JSON Schema is a validation language, not a data transformation language. It can assert that a value is a string, that it falls within a range, or that it matches a pattern. But it cannot—by design—insert values that are missing. The default keyword in JSON Schema is explicitly described as "not used for validation" and is intended for documentation or UI hints. Developers who expected the schema to fill in defaults were asking the specification to do something it was never designed to do.
Other schema systems handle defaults differently. OpenAPI 3.0, for example, includes a default field for parameters, and tooling like Swagger UI applies those defaults when generating example requests. But OpenAPI's default is still not a validation requirement; it is a hint for code generation. CircleCI's schema could have adopted a similar approach, but it would have required custom validator logic to apply defaults before the pipeline engine ran.
JSON Schema draft-07 introduced a default keyword that some validators honor, but CircleCI used an earlier draft that ignored it. Even if they had used a newer draft, the behavior would have been implementation-dependent. CircleCI's validator chose to treat missing keys as null rather than as absent, which compounded the problem. A config that omitted timeout became a config with timeout: null, and the pipeline engine interpreted null as "use the minimal allowed value."
The expectation mismatch is a classic case of leaky abstraction. Developers assume that a schema defines both structure and behavior, but in practice, behavior is encoded in the runtime. CircleCI's decision to separate validation from execution created a gap that no schema could bridge without explicit default-handling logic. The result was a config file that was valid by the schema's rules but invalid by the runtime's expectations.
Workarounds the Community Discovered
Faced with broken pipelines, the community responded with a variety of workarounds. The most common was a preprocessing step that injected missing keys before the schema validator ran. Teams wrote small scripts in Python or Node.js that read their config files, compared them against a known list of defaults, and inserted any missing fields. These scripts were brittle but effective, and many teams shared them in public repositories.
Templating engines like Mustache or Handlebars also saw renewed use. Instead of writing plain YAML, teams generated their config from templates that included default values. The template would expand into a fully specified config file that passed validation. This added a build step to the CI pipeline itself, creating a circular dependency: you needed CI to generate your CI config. Some teams accepted this as a necessary evil.
A more radical workaround was to fork CircleCI's schema and add custom default annotations, then use a custom validator that applied those defaults. This required maintaining a separate validator binary and keeping it in sync with CircleCI's official schema updates. A handful of large enterprises took this route, but it was impractical for smaller teams.
Some teams simply reverted to the old YAML-based system by pinning their config format to an older version that did not enforce schema validation. CircleCI had promised to maintain backward compatibility for a transitional period, and many teams took advantage of that. But the clock was ticking: CircleCI announced that the old parser would be deprecated by the end of the year.
The long-term hope among community members is that CircleCI will extend its schema to include a defaults section at the top level, where users can define fallback values for any key. This would restore the implicit defaults while maintaining the benefits of validation. As of late 2025, CircleCI has not announced such a feature, but the community's petitions have gathered thousands of signatures.
Lessons for Future Schema-Driven Tooling
The first lesson is obvious but often ignored: ship schema with explicit default values. If a tool uses JSON Schema for validation, it should provide a companion file or mechanism that defines runtime defaults. This could be as simple as a JSON file mapping keys to default values, or as sophisticated as a schema extension that validators can interpret. The key is to make defaults a first-class part of the contract, not an afterthought.
Second, provide migration tooling that accounts for real-world configs. CircleCI's migration script was a good start, but it only handled the top 80% of use cases. Teams with complex configurations—nested parameters, conditional steps, dynamic values—had to manually fix their files. A better approach would be to analyze a corpus of existing configs, identify the most common patterns, and generate a migration script that covers those patterns.
Third, document behavioral assumptions alongside structural ones. The schema told developers what keys were allowed, but it did not explain how missing keys would be treated. A simple note in the schema's description field—"If omitted, timeout defaults to 10 minutes"—would have prevented countless hours of debugging. CircleCI's documentation did eventually add such notes, but only after the damage was done.
Fourth, test schema changes against a representative set of real-world configs. CircleCI's internal testing likely used a curated set of configs that were already fully specified. They did not test against the long tail of configs that relied on implicit defaults. A diverse test corpus, drawn from public repositories or anonymized user data, would have caught the breakage before release.
Finally, consider backward compatibility in schema versions. CircleCI could have introduced the schema as an optional validation layer, with a transition period where warnings were issued instead of errors. They could have provided a schema_version key that allowed users to opt into stricter validation gradually. Instead, they flipped a switch and broke the world.
The Real Cost of Strict Schemas Without Defaults
The most immediate cost was cognitive load. Junior engineers who had never needed to think about timeout or parallelism suddenly had to understand every field in the config. The schema documentation listed over 150 possible keys, and while most were optional, the lack of defaults meant that every optional key was effectively required unless the engineer knew the runtime's fallback behavior. This turned a simple CI setup into a configuration exercise.
CI failure rates rose noticeably. Internal data from a mid-sized SaaS company showed that their pipeline failure rate increased by roughly 15–20% in the month following the migration. Most failures were due to missing keys, not logic errors. Debug time shifted from fixing test code to hunting down missing config fields. The company estimated that each developer lost about two hours per week to config-related failures.
The vendor lock-in effect deepened. Teams that had invested in custom workarounds—preprocessing scripts, templating engines, forked validators—found it harder to consider switching platforms. The sunk cost of migrating configs once made them reluctant to do it again. What started as a standards improvement ended as a moat that tied users more tightly to CircleCI.
Standards only help when they match practice. JSON Schema is a fine tool for validation, but it is not a complete configuration language. CircleCI's mistake was to assume that a schema could replace the implicit contracts that had evolved over years of use. The real lesson is that any tool that defines a configuration format must also define what happens when the config is incomplete. Defaults are not a luxury; they are a necessity.
As the community continues to adapt, the incident serves as a powerful reminder that schema-driven configuration requires more than just structural validation. Future platforms would do well to embed defaults directly into their schema contracts, test against real-world configs, and provide gradual migration paths. The path to better tooling is paved with explicit defaults, not broken assumptions.