Defect Shift-Left
Pipeline stages have a strict order. Every defect has an earliest stage at
which it can be caught. Catching it later is always a regression.
Core Directives
- Prevent over detect. Make invalid states unrepresentable before adding
a check.
- Earliest possible stage is mandatory. If a check can run at stage N,
running it at N+1 is a regression.
- Replace same-scope duplicates. When shifting a check earlier, remove
any later check that covers the same scope. Keep a later backstop only
when it covers a broader or less-bypassable scope.
- Fail loud at the origin. Errors must surface where they originated.
Improvement Trio
defect-shift-left: move defect detection earlier.
push-out: move recurring operational work outward.
bring-down: move bespoke code down into reusable capability.
1. The Ladder
| Stage |
Rank |
Phase |
What runs here |
| 0 |
0 |
Language |
Type system, syntax, language semantics |
| 1 |
1 |
Design |
Spec, ADR, threat model, schema |
| 2 |
2 |
Authoring |
LSP, in-editor lint, formatter |
| 3 |
3 |
Pre-commit |
Format, fast lint, secret scan, commit-msg hook |
| 4 |
4 |
Compile |
Compiler, type-checker, codegen |
| 5 |
5 |
Build / Static analysis |
Full lint, depcheck, SAST, license, CVE, bundle, IaC, fitness functions |
| 6 |
6 |
Unit test |
Local test runner, property tests |
| 7 |
7 |
Integration / Contract |
CI suite, contract tests, container builds |
| 8a |
8 |
Pre-deploy static |
Migration dry-run, config-vs-env, capacity, IAM diff (deploy abortable) |
| 8b |
9 |
Deploy execution |
Smoke, health probes, slot readiness (rollback on failure) |
| 9 |
10 |
Canary / Staging |
Partial traffic, real env, perf regression |
| 10 |
11 |
Production runtime |
Live traffic, monitoring |
| 11 |
12 |
Post-incident |
Forensics, RCA |
Cost grows roughly geometrically with rank. The ladder is monotonic — later
detection is never neutral. Use Rank for distance math; stage labels like
8a and 8b are names, not numbers.
Stages 8a and 8b are split because some defects only become detectable
when target-environment state is available; pre-deploy can abort cheaply, deploy
execution requires rollback.
2. Stage 0 — Make Invalid States Unrepresentable
Before adding any check at Stage ≥1, ask: can a type or schema make this defect
unrepresentable? If yes, the check belongs at Stage 0.
| Technique |
Eliminates |
| Strong / branded types |
Type confusion, semantic mixing |
| Sum types + exhaustive matching |
Missing case, silent fallthrough |
| Option / Result types |
Null deref, silent failure |
| Refinement types |
Range, off-by-one |
| Linear / affine types |
Use-after-free, double-close |
| Schema-as-code |
Config drift, contract mismatch |
| Const / immutable default |
Accidental mutation, race |
Strict compiler flags (strict, noUncheckedIndexedAccess, strictNullChecks, --strict) |
Whole defect classes without writing new types — flip a flag, the compiler enumerates the gaps |
3. Defect Taxonomy → Earliest Stage
Stage vs rank. The Stage column is the label from §1; for distance
math use the rank. Labels 0–7 equal their rank, then 8a→8, 8b→9,
9→10, 10→11, 11→12 — never subtract stage labels.
| Defect class |
Stage |
Mechanism (fallback) |
| Type mismatch, null deref, semantic-type mixing |
0 |
Type system |
| Missing case handling |
0 |
Exhaustive sum types |
| Off-by-one / range |
0 |
Refinement types (else 6: property test) |
| Use-after-free, race |
0 |
Linear / borrow types (else 5: static analysis) |
| Generated code drift from schema |
0 |
Codegen types (else 5: codegen drift check) |
| Contract / schema absent or ambiguous |
1 |
Shared schema / spec |
| Authorization model gap |
1 |
Threat model (else 7: security test) |
| Style, formatting, unused code, API misuse |
2 |
LSP / editor (else 5: lint) |
| Banned API / unsafe pattern |
2 |
LSP rule (else 5: lint) |
| Forbidden architectural dependency |
2 |
Editor import rule (else 5: depcheck / lint) |
| Committed config violates schema |
2 |
Editor schema hint (else 5: schema validation) |
| Secret in source |
3 |
Pre-commit scanner (else 5: SAST) |
| Symbol resolution / missing import |
4 |
Compiler |
| CVE in dependency |
5 |
SCA audit |
| License incompatibility |
5 |
License audit |
| Bundle / artifact regression |
5 |
Bundle validator |
| Logic error in pure function |
6 |
Unit test |
| Property violation across input space |
6 |
Property test |
| Integration boundary mismatch |
7 |
Contract test |
| Container / build reproducibility |
7 |
CI image build |
| Performance regression (micro) |
7 |
Benchmark (else 9: load test) |
| Migration vs current schema |
8a |
Dry-run against prod DB |
| Irreversible migration |
8a |
Reversibility check |
| Cross-service version skew |
8a |
Version-matrix gate |
| Backwards-incompatible API change |
8a |
Contract diff vs deployed |
| Missing / expired secret in target env |
8a |
Secret-store presence check |
| Undefined feature flag in target |
8a |
Flag-store consistency |
| Target-env config violates schema |
8a |
Pre-deploy config / env validation |
| Capacity / quota exceeded |
8a |
Resource projection |
| IAM permission expansion |
8a |
IAM diff |
| Cost / budget breach |
8a |
Cost projection |
| Missing rollback artifact |
8a |
Registry probe |
| Compliance approval missing |
8a |
Policy gate |
| Artifact crashes on boot |
8b |
Startup smoke |
| Health probe never passes |
8b |
Orchestrator readiness gate |
| Target env unreachable dependency |
8b |
Boot connectivity check |
| Resource exhaustion under load |
9 |
Load test |
| Real-world latency / SLO breach |
10 |
Production monitoring |
4. Audit Protocol
- Inventory every check and the stage it runs at, including manual reviews,
advisory warnings, and runtime asserts.
- Classify each by defect class (§3).
- Look up the earliest possible stage and its rank (§1).
- Compute rank distance = current rank − earliest rank.
- Prioritize by rank distance × frequency × blast radius.
- Move the check to the earliest feasible stage.
- Gate it. A correct-stage check that does not block is still a detection
gap.
- Remove later same-scope duplicates once the earlier gate is proven. Keep
only broader or less-bypassable backstops.
- Audit every escaped defect: find its earliest possible stage and place a
gate there.
| Situation |
Action |
| Proposed = earliest possible |
Proceed |
| Proposed > earliest, earlier feasible now |
Reject — implement at the earlier stage |
| Proposed > earliest, earlier requires effort |
Document gap as technical debt; schedule shift |
| No check; defects only found in production |
Critical — work backward from Stage 10 |
| Check requires target-env state |
Stage 8a is earliest — do not push to Stage 10 |
| Check exists but does not block |
Promote to blocking gate or remove as theatre |
| Later check covers same scope as earlier check |
Remove later duplicate after proof |
| Later check covers broader / unbypassable scope |
Keep as backstop; record distinct scope |
Emit one coder-facing row per gap:
| Defect class |
Current stage (rank) |
Earliest stage (rank) |
Rank distance |
Mechanism |
Decision |
Owner/check |
Verification |
Next action |
If a gap remains, state: "Detection Gap: defect class catchable at Stage [X] (rank [Xr]),
currently at Stage [Y] (rank [Yr]). Mechanism: [...]."
5. Anti-Patterns
| Pattern |
Actual / earliest |
| Runtime check for type errors |
Stage 10 / Stage 0 |
| CI formatting check with no editor support |
Stage 5 / Stage 2 |
| Linter only in CI |
Stage 5 / Stage 2 + Stage 5 |
| Code review as primary defect filter |
Manual / Stage 2–5 |
| Production monitor for known-bad input |
Stage 10 / Stage 0 |
| Compile errors hidden behind dynamic types |
Stage 6+ / Stage 0 |
| Manual deployment checklist |
Manual / Stage 5 or 8a |
| Documentation as the contract |
Stage 7+ / Stage 1 |
| Deploy-and-pray monitoring |
Stage 10 / Stage 8a |
| Migration applied without dry-run |
Stage 8b–10 / Stage 8a |
| Secrets / config validated only at runtime |
Stage 10 / Stage 8a |
| Manual rollback on deploy failure |
Stage 10 / Stage 8b |
| No canary, full traffic on new artifact |
Stage 10 / Stage 9 |
These three do not detect late — they suppress a defect rather than move it
earlier, so they have no "earliest stage":
- Retry as error handling — masks a Stage 10 failure indefinitely instead of surfacing it.
- Catch-and-log silent failure — swallows the error, violating "fail loud at the origin" (Directive 4).
- Warnings nobody reads — detection with no gate; see §6.4.
6. Common Shift Patterns
Recurring moves that shift a defect class from a later stage to an earlier one.
Recognise them; apply them deliberately.
6.1 Untyped → strict-typed source
|
|
| Shifts |
Type errors, null deref, registry-shape drift, silent undefined from bracket access |
| From |
Stage 6+ (unit test) or Stage 10 (production) |
| To |
Stage 0 (type system) |
Convert source to a language with a checking compiler (JS → TS, Python → typed
Python under mypy/pyright, Ruby → RBS/Sorbet). Then progressively enable the
strictest flags — strict, noUncheckedIndexedAccess, strictNullChecks — and
retire every @ts-nocheck / # type: ignore escape hatch. Each flag flip is
its own shift: the compiler enumerates the defects, you fix them in batches.
The shift completes only when the strict typecheck is a blocking gate at
both pre-commit (fast feedback on staged files) and CI (full-repo backstop). A
typecheck nobody runs is theatre — see §6.4.
6.2 ADR → executable architectural rule
|
|
| Shifts |
Forbidden imports, layering violations, banned API usage, accidental cross-module coupling |
| From |
Stage 1 (design doc) or Stage 7+ (code review) |
| To |
Stage 2 (editor rule) + Stage 5 (blocking static analysis) |
Architectural rules expressed in prose are advice; rules expressed in lint
config are enforcement. eslint-plugin-boundaries,
import/no-restricted-paths, dependency-cruiser, ArchUnit (JVM), and
import-linter (Python) — all turn an ADR sentence into editor feedback and a
build failure.
The recipe: encode each architectural decision as a rule that fails the build
when violated. The ADR document remains as rationale; the lint config is the
enforcement.
For the encoding pattern see architecture-as-code, with concrete
implementations in architecture-as-code-javascript or
architecture-as-code-python. For first principles see
architecture-guidelines; for the topology rationale this enforces, see
morphogenetic-architecture.
6.3 Hand-validated boundary → schema-as-code
|
|
| Shifts |
Config drift, API contract mismatch, malformed input, doc-vs-reality skew |
| From |
Stage 6+ (hand-rolled if-chain validators) or Stage 10 (runtime parse errors, prose-as-contract) |
| To |
Stage 0 (codegen), Stage 2 (editor), Stage 5 (build), Stage 8a (deploy) — one artifact, multiple rungs |
Schemas are the highest-leverage shift in this catalogue because the same
artifact powers checks at every stage that can read it:
- Stage 0 — codegen produces static types: JSON Schema →
json-schema-to-typescript; OpenAPI → server / client stubs; Protobuf → typed
clients; XSD → C# / Java classes.
- Stage 2 — editor schemas drive autocomplete and inline validation for
hand-edited files (
$schema in JSON, xsi:schemaLocation in XML, YAML
language server hints).
- Stage 5 — CI validates committed files against the schema (
ajv,
xmllint, spectral for OpenAPI, buf lint for Protobuf).
- Stage 8a — pre-deploy gate rejects config that does not match the schema
before it reaches a running service.
- Boundary runtime — schema-bridged TS libraries (
zod, typebox, io-ts,
valibot) make the schema the single source: static type plus runtime
validator generated from one declaration. Use at every external input
boundary (HTTP body, env vars, message payload).
Catalogue: JSON Schema (configs, package.json), OpenAPI (HTTP), gRPC /
Protobuf (service-to-service), GraphQL SDL, AsyncAPI (events), Avro (streaming),
XSD (XML / SOAP).
The win is not "we validate" — it is "validation comes from a single artifact
that fans out to every appropriate stage." Two hand-written sources checking
the same shape are the same-scope duplication §Directive 3 forbids; one schema
is the antidote.
6.4 Optional check → blocking gate
|
|
| Shifts |
The check itself, from advisory to enforced |
| From |
Stage where the check exists but does not block |
| To |
Same stage, now a gate |
The most common shift-left failure is having the right check at the right stage
and not making it block. A typecheck run as a manual npm run command has zero
shift-left value relative to no typecheck at all. Audit:
- Pre-commit hook fails → does it block the commit, or just print?
- CI job fails → does branch protection require it before merge?
- Lint warning → is the rule severity
error or warn?
- Coverage drop → does it fail the build, or land in a report nobody opens?
6.5 Scope-justified backstops
§Directive 3 allows later backstops when two layers run the same check on
different scopes:
- Pre-commit — staged files only, fast, narrow, bypassable with
--no-verify.
- CI — full repo, slow, complete, un-bypassable behind branch protection.
Both are warranted: different blast radii (single commit vs. branch), different
bypass costs. Layering pays when the earlier layer is faster and bypassable —
the later layer is the un-bypassable backstop, not a duplicate.
7. Stack-Aware Tooling Survey
Use this only when the user asks for tooling recommendations or implementation
options. A plain shift-left audit stops at the missing category.
- Detect the stack: manifests, lockfiles, scripts, test runners, CI/CD
files, IaC/deploy config, hook runners, and editor config. Record present and
absent signals.
- Map gaps to categories: name the stage, defect class, and missing tool
category. Do not jump straight to products.
| Stage |
Tool category to look for |
| 0 |
Type system / compiler strictness flags / schema-as-code library |
| 1 |
ADR template, schema registry, threat-model artifact |
| 2 |
LSP, editor lint integration, formatter-on-save |
| 3 |
Hook runner, secret scanner, commit-message linter |
| 4 |
Compiler / type-checker invoked in build |
| 5 |
Linter, dependency auditor, SAST, license checker, IaC scanner |
| 6 |
Unit test runner, property-test library, coverage gate |
| 7 |
Integration / contract test harness, container build verifier |
| 8a |
Migration dry-run, config validator, IAM diff, cost projector |
| 8b |
Smoke-test runner, health-probe spec, orchestrator readiness gate |
| 9 |
Canary controller, load generator, perf-regression gate |
| 10 |
Runtime monitoring, error tracker, SLO alerting |
| 11 |
Incident-record system, RCA template |
- Find specific options only on request: search the detected ecosystem,
filter for stack compatibility, prefer tools already present in the stack,
and cite each option with a source URL and release/currency signal.
Produce one row per gap:
| Stage |
Defect class at risk |
Detected stack signal |
Candidate tool category |
Specific options (cited) |
Effort |
Do not propose a tool without naming the stage it staffs and the defect class it
catches. A tool that does not map to a rung on §1 has no place in the output.
8. See also
architecture-as-code — the codified-architecture pattern this skill names in §6.2.
architecture-guidelines — first-principles rules whose violations this skill places on the ladder.
ci-cd-reliability-architecture — pipeline rules that staff Stages 5–10.
push-out — move recurring operational work out of human/manual execution into durable systems.
bring-down — move bespoke or duplicated code down into reusable capability.
continuous-improvement — how to promote a recurring escaped-defect into a permanent gate (Directive 1).
1---2name: defect-shift-left3description: Places every error detection at the earliest stage of the pipeline that is technically capable of catching it. Use when designing or auditing a CI/CD pipeline, choosing tooling, deciding where a check belongs, or asking "could this have been caught earlier?"4---56# Defect Shift-Left78> Pipeline stages have a strict order. Every defect has an earliest stage at9> which it can be caught. Catching it later is always a regression.1011> **Core Directives**12>13> 1. **Prevent over detect.** Make invalid states unrepresentable before adding14> a check.15> 2. **Earliest possible stage is mandatory.** If a check _can_ run at stage N,16> running it at N+1 is a regression.17> 3. **Replace same-scope duplicates.** When shifting a check earlier, remove18> any later check that covers the same scope. Keep a later backstop only19> when it covers a broader or less-bypassable scope.20> 4. **Fail loud at the origin.** Errors must surface where they originated.2122> **Improvement Trio**23>24> - `defect-shift-left`: move defect detection earlier.25> - `push-out`: move recurring operational work outward.26> - `bring-down`: move bespoke code down into reusable capability.2728---2930## 1. The Ladder3132| Stage | Rank | Phase | What runs here |33| ------ | ---- | ----------------------- | ------------------------------------------------------------------------- |34| **0** | 0 | Language | Type system, syntax, language semantics |35| **1** | 1 | Design | Spec, ADR, threat model, schema |36| **2** | 2 | Authoring | LSP, in-editor lint, formatter |37| **3** | 3 | Pre-commit | Format, fast lint, secret scan, commit-msg hook |38| **4** | 4 | Compile | Compiler, type-checker, codegen |39| **5** | 5 | Build / Static analysis | Full lint, depcheck, SAST, license, CVE, bundle, IaC, fitness functions |40| **6** | 6 | Unit test | Local test runner, property tests |41| **7** | 7 | Integration / Contract | CI suite, contract tests, container builds |42| **8a** | 8 | Pre-deploy static | Migration dry-run, config-vs-env, capacity, IAM diff _(deploy abortable)_ |43| **8b** | 9 | Deploy execution | Smoke, health probes, slot readiness _(rollback on failure)_ |44| **9** | 10 | Canary / Staging | Partial traffic, real env, perf regression |45| **10** | 11 | Production runtime | Live traffic, monitoring |46| **11** | 12 | Post-incident | Forensics, RCA |4748Cost grows roughly geometrically with rank. The ladder is monotonic — later49detection is never neutral. Use `Rank` for distance math; stage labels like50`8a` and `8b` are names, not numbers.5152Stages **8a** and **8b** are split because some defects only become detectable53when target-environment state is available; pre-deploy can abort cheaply, deploy54execution requires rollback.5556---5758## 2. Stage 0 — Make Invalid States Unrepresentable5960Before adding any check at Stage ≥1, ask: _can a type or schema make this defect61unrepresentable?_ If yes, the check belongs at Stage 0.6263| Technique | Eliminates |64| -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |65| Strong / branded types | Type confusion, semantic mixing |66| Sum types + exhaustive matching | Missing case, silent fallthrough |67| Option / Result types | Null deref, silent failure |68| Refinement types | Range, off-by-one |69| Linear / affine types | Use-after-free, double-close |70| Schema-as-code | Config drift, contract mismatch |71| Const / immutable default | Accidental mutation, race |72| Strict compiler flags (`strict`, `noUncheckedIndexedAccess`, `strictNullChecks`, `--strict`) | Whole defect classes without writing new types — flip a flag, the compiler enumerates the gaps |7374---7576## 3. Defect Taxonomy → Earliest Stage7778> **Stage vs rank.** The `Stage` column is the label from §1; for distance79> math use the **rank**. Labels `0`–`7` equal their rank, then `8a`→8, `8b`→9,80> `9`→10, `10`→11, `11`→12 — never subtract stage labels.8182| Defect class | Stage | Mechanism (fallback) |83| ----------------------------------------------- | ----- | ------------------------------------------------ |84| Type mismatch, null deref, semantic-type mixing | 0 | Type system |85| Missing case handling | 0 | Exhaustive sum types |86| Off-by-one / range | 0 | Refinement types (else 6: property test) |87| Use-after-free, race | 0 | Linear / borrow types (else 5: static analysis) |88| Generated code drift from schema | 0 | Codegen types (else 5: codegen drift check) |89| Contract / schema absent or ambiguous | 1 | Shared schema / spec |90| Authorization model gap | 1 | Threat model (else 7: security test) |91| Style, formatting, unused code, API misuse | 2 | LSP / editor (else 5: lint) |92| Banned API / unsafe pattern | 2 | LSP rule (else 5: lint) |93| Forbidden architectural dependency | 2 | Editor import rule (else 5: depcheck / lint) |94| Committed config violates schema | 2 | Editor schema hint (else 5: schema validation) |95| Secret in source | 3 | Pre-commit scanner (else 5: SAST) |96| Symbol resolution / missing import | 4 | Compiler |97| CVE in dependency | 5 | SCA audit |98| License incompatibility | 5 | License audit |99| Bundle / artifact regression | 5 | Bundle validator |100| Logic error in pure function | 6 | Unit test |101| Property violation across input space | 6 | Property test |102| Integration boundary mismatch | 7 | Contract test |103| Container / build reproducibility | 7 | CI image build |104| Performance regression (micro) | 7 | Benchmark (else 9: load test) |105| Migration vs current schema | 8a | Dry-run against prod DB |106| Irreversible migration | 8a | Reversibility check |107| Cross-service version skew | 8a | Version-matrix gate |108| Backwards-incompatible API change | 8a | Contract diff vs deployed |109| Missing / expired secret in target env | 8a | Secret-store presence check |110| Undefined feature flag in target | 8a | Flag-store consistency |111| Target-env config violates schema | 8a | Pre-deploy config / env validation |112| Capacity / quota exceeded | 8a | Resource projection |113| IAM permission expansion | 8a | IAM diff |114| Cost / budget breach | 8a | Cost projection |115| Missing rollback artifact | 8a | Registry probe |116| Compliance approval missing | 8a | Policy gate |117| Artifact crashes on boot | 8b | Startup smoke |118| Health probe never passes | 8b | Orchestrator readiness gate |119| Target env unreachable dependency | 8b | Boot connectivity check |120| Resource exhaustion under load | 9 | Load test |121| Real-world latency / SLO breach | 10 | Production monitoring |122123---124125## 4. Audit Protocol1261271. **Inventory** every check and the stage it runs at, including manual reviews,128 advisory warnings, and runtime asserts.1292. **Classify** each by defect class (§3).1303. **Look up** the earliest possible stage and its rank (§1).1314. **Compute rank distance** = current rank − earliest rank.1325. **Prioritize** by rank distance × frequency × blast radius.1336. **Move the check** to the earliest feasible stage.1347. **Gate it.** A correct-stage check that does not block is still a detection135 gap.1368. **Remove later same-scope duplicates** once the earlier gate is proven. Keep137 only broader or less-bypassable backstops.1389. **Audit every escaped defect:** find its earliest possible stage and place a139 gate there.140141| Situation | Action |142| ---------------------------------------------- | ---------------------------------------------- |143| Proposed = earliest possible | Proceed |144| Proposed > earliest, earlier feasible now | Reject — implement at the earlier stage |145| Proposed > earliest, earlier requires effort | Document gap as technical debt; schedule shift |146| No check; defects only found in production | Critical — work backward from Stage 10 |147| Check requires target-env state | Stage 8a is earliest — do not push to Stage 10 |148| Check exists but does not block | Promote to blocking gate or remove as theatre |149| Later check covers same scope as earlier check | Remove later duplicate after proof |150| Later check covers broader / unbypassable scope | Keep as backstop; record distinct scope |151152Emit one coder-facing row per gap:153154| Defect class | Current stage (rank) | Earliest stage (rank) | Rank distance | Mechanism | Decision | Owner/check | Verification | Next action |155| ------------ | ------------- | -------------- | -------------- | --------- | -------- | ----------- | ------------ | ----------- |156157If a gap remains, state: _"Detection Gap: defect class catchable at Stage [X] (rank [Xr]),158currently at Stage [Y] (rank [Yr]). Mechanism: [...]."_159160---161162## 5. Anti-Patterns163164| Pattern | Actual / earliest |165| ------------------------------------------ | ----------------------------- |166| Runtime check for type errors | Stage 10 / Stage 0 |167| CI formatting check with no editor support | Stage 5 / Stage 2 |168| Linter only in CI | Stage 5 / Stage 2 + Stage 5 |169| Code review as primary defect filter | Manual / Stage 2–5 |170| Production monitor for known-bad input | Stage 10 / Stage 0 |171| Compile errors hidden behind dynamic types | Stage 6+ / Stage 0 |172| Manual deployment checklist | Manual / Stage 5 or 8a |173| Documentation as the contract | Stage 7+ / Stage 1 |174| Deploy-and-pray monitoring | Stage 10 / Stage 8a |175| Migration applied without dry-run | Stage 8b–10 / Stage 8a |176| Secrets / config validated only at runtime | Stage 10 / Stage 8a |177| Manual rollback on deploy failure | Stage 10 / Stage 8b |178| No canary, full traffic on new artifact | Stage 10 / Stage 9 |179180These three do not detect late — they **suppress** a defect rather than move it181earlier, so they have no "earliest stage":182183- **Retry as error handling** — masks a Stage 10 failure indefinitely instead of surfacing it.184- **Catch-and-log silent failure** — swallows the error, violating "fail loud at the origin" (Directive 4).185- **Warnings nobody reads** — detection with no gate; see §6.4.186187---188189## 6. Common Shift Patterns190191Recurring moves that shift a defect class from a later stage to an earlier one.192Recognise them; apply them deliberately.193194### 6.1 Untyped → strict-typed source195196| | |197| ---------- | ------------------------------------------------------------------------------------- |198| **Shifts** | Type errors, null deref, registry-shape drift, silent `undefined` from bracket access |199| **From** | Stage 6+ (unit test) or Stage 10 (production) |200| **To** | Stage 0 (type system) |201202Convert source to a language with a checking compiler (JS → TS, Python → typed203Python under `mypy`/`pyright`, Ruby → RBS/Sorbet). Then progressively enable the204strictest flags — `strict`, `noUncheckedIndexedAccess`, `strictNullChecks` — and205retire every `@ts-nocheck` / `# type: ignore` escape hatch. Each flag flip is206its own shift: the compiler enumerates the defects, you fix them in batches.207208The shift completes only when the strict typecheck is a **blocking gate** at209both pre-commit (fast feedback on staged files) and CI (full-repo backstop). A210typecheck nobody runs is theatre — see §6.4.211212### 6.2 ADR → executable architectural rule213214| | |215| ---------- | ------------------------------------------------------------------------------------------ |216| **Shifts** | Forbidden imports, layering violations, banned API usage, accidental cross-module coupling |217| **From** | Stage 1 (design doc) or Stage 7+ (code review) |218| **To** | Stage 2 (editor rule) + Stage 5 (blocking static analysis) |219220Architectural rules expressed in prose are advice; rules expressed in lint221config are enforcement. `eslint-plugin-boundaries`,222`import/no-restricted-paths`, `dependency-cruiser`, ArchUnit (JVM), and223`import-linter` (Python) — all turn an ADR sentence into editor feedback and a224build failure.225226The recipe: encode each architectural decision as a rule that fails the build227when violated. The ADR document remains as rationale; the lint config is the228enforcement.229230For the encoding pattern see `architecture-as-code`, with concrete231implementations in `architecture-as-code-javascript` or232`architecture-as-code-python`. For first principles see233`architecture-guidelines`; for the topology rationale this enforces, see234`morphogenetic-architecture`.235236### 6.3 Hand-validated boundary → schema-as-code237238| | |239| ---------- | ---------------------------------------------------------------------------------------------------------- |240| **Shifts** | Config drift, API contract mismatch, malformed input, doc-vs-reality skew |241| **From** | Stage 6+ (hand-rolled `if`-chain validators) or Stage 10 (runtime parse errors, prose-as-contract) |242| **To** | Stage 0 (codegen), Stage 2 (editor), Stage 5 (build), Stage 8a (deploy) — **one artifact, multiple rungs** |243244Schemas are the highest-leverage shift in this catalogue because the same245artifact powers checks at every stage that can read it:246247- **Stage 0** — codegen produces static types: JSON Schema →248 `json-schema-to-typescript`; OpenAPI → server / client stubs; Protobuf → typed249 clients; XSD → C# / Java classes.250- **Stage 2** — editor schemas drive autocomplete and inline validation for251 hand-edited files (`$schema` in JSON, `xsi:schemaLocation` in XML, YAML252 language server hints).253- **Stage 5** — CI validates committed files against the schema (`ajv`,254 `xmllint`, `spectral` for OpenAPI, `buf lint` for Protobuf).255- **Stage 8a** — pre-deploy gate rejects config that does not match the schema256 before it reaches a running service.257- **Boundary runtime** — schema-bridged TS libraries (`zod`, `typebox`, `io-ts`,258 `valibot`) make the schema the single source: static type plus runtime259 validator generated from one declaration. Use at every external input260 boundary (HTTP body, env vars, message payload).261262Catalogue: JSON Schema (configs, `package.json`), OpenAPI (HTTP), gRPC /263Protobuf (service-to-service), GraphQL SDL, AsyncAPI (events), Avro (streaming),264XSD (XML / SOAP).265266The win is not _"we validate"_ — it is _"validation comes from a single artifact267that fans out to every appropriate stage."_ Two hand-written sources checking268the same shape are the same-scope duplication §Directive 3 forbids; one schema269is the antidote.270271### 6.4 Optional check → blocking gate272273| | |274| ---------- | ----------------------------------------------- |275| **Shifts** | The check itself, from advisory to enforced |276| **From** | Stage where the check exists but does not block |277| **To** | Same stage, now a gate |278279The most common shift-left failure is having the right check at the right stage280and not making it block. A typecheck run as a manual `npm run` command has zero281shift-left value relative to no typecheck at all. Audit:282283- Pre-commit hook fails → does it block the commit, or just print?284- CI job fails → does branch protection require it before merge?285- Lint warning → is the rule severity `error` or `warn`?286- Coverage drop → does it fail the build, or land in a report nobody opens?287288### 6.5 Scope-justified backstops289290§Directive 3 allows later backstops when two layers run the same check on291different scopes:292293- **Pre-commit** — staged files only, fast, narrow, bypassable with294 `--no-verify`.295- **CI** — full repo, slow, complete, un-bypassable behind branch protection.296297Both are warranted: different blast radii (single commit vs. branch), different298bypass costs. Layering pays when the earlier layer is faster _and_ bypassable —299the later layer is the un-bypassable backstop, not a duplicate.300301---302303## 7. Stack-Aware Tooling Survey304305Use this only when the user asks for tooling recommendations or implementation306options. A plain shift-left audit stops at the missing category.3073081. **Detect the stack:** manifests, lockfiles, scripts, test runners, CI/CD309 files, IaC/deploy config, hook runners, and editor config. Record present and310 absent signals.3112. **Map gaps to categories:** name the stage, defect class, and missing tool312 category. Do not jump straight to products.313314| Stage | Tool category to look for |315| ------ | ----------------------------------------------------------------- |316| **0** | Type system / compiler strictness flags / schema-as-code library |317| **1** | ADR template, schema registry, threat-model artifact |318| **2** | LSP, editor lint integration, formatter-on-save |319| **3** | Hook runner, secret scanner, commit-message linter |320| **4** | Compiler / type-checker invoked in build |321| **5** | Linter, dependency auditor, SAST, license checker, IaC scanner |322| **6** | Unit test runner, property-test library, coverage gate |323| **7** | Integration / contract test harness, container build verifier |324| **8a** | Migration dry-run, config validator, IAM diff, cost projector |325| **8b** | Smoke-test runner, health-probe spec, orchestrator readiness gate |326| **9** | Canary controller, load generator, perf-regression gate |327| **10** | Runtime monitoring, error tracker, SLO alerting |328| **11** | Incident-record system, RCA template |3293303. **Find specific options only on request:** search the detected ecosystem,331 filter for stack compatibility, prefer tools already present in the stack,332 and cite each option with a source URL and release/currency signal.333334Produce one row per gap:335336| Stage | Defect class at risk | Detected stack signal | Candidate tool category | Specific options (cited) | Effort |337| ----- | -------------------- | --------------------- | ----------------------- | ------------------------ | ------ |338339Do not propose a tool without naming the stage it staffs and the defect class it340catches. A tool that does not map to a rung on §1 has no place in the output.341342## 8. See also343344- **`architecture-as-code`** — the codified-architecture pattern this skill names in §6.2.345- **`architecture-guidelines`** — first-principles rules whose violations this skill places on the ladder.346- **`ci-cd-reliability-architecture`** — pipeline rules that staff Stages 5–10.347- **`push-out`** — move recurring operational work out of human/manual execution into durable systems.348- **`bring-down`** — move bespoke or duplicated code down into reusable capability.349- **`continuous-improvement`** — how to promote a recurring escaped-defect into a permanent gate (Directive 1).