Templates Are Code
Last reviewed · content updated
IntermediateWhat you'll learn
~18 min- Give the template library its own CI - templates that ship broken break everyone
- Version templates with tags and compatibility branches so consumers upgrade deliberately
- Enforce template usage on protected resources with extends + required-template checks
The product needs what every product needs
Lesson 2.2 made pipelines a product with consumers. Products need three things bespoke files never did: tests (a broken template breaks forty teams at once), versioning (consumers must upgrade on their schedule, not yours), and enforcement (a product teams can silently bypass is a suggestion). This lesson is those three, each drawn from a working software factory.
1. The template repo gets its own CI
The factory’s rule, verbatim-worthy: templates are code, so they get tested. Its CI job parses every YAML file in the library, resolves every template: reference, and fails the build on anything unresolved — a renamed step template with a stale reference in some stage template is caught at template-PR time, not at forty consumers’ next run. Build on that floor:
Template-repo CI, in escalating value:1. RESOLVE every template reference across the library - fail on dangling2. COMPILE each tier-3 starter against a fixture consumer - the expanded YAML must be produced without error (compile-time bugs caught)3. CONTRACT assert each template's parameters: types, required/optional, defaults - a parameter REMOVAL or type change fails unless the version is being majored (see below)4. SMOKE run one starter end-to-end against a sandbox project nightly - the only tier that catches runtime-phase breaksTier 3 is the subtle one: it turns your parameter surface into a tested public API — which is exactly what it is. (If this rhythm feels familiar from the Zero Trust training’s detection-as-code lesson, it should: rules-in-git-with-CI is one discipline wearing many artifact types.)
2. Versioning: tags forward, compat branches back
Consumers pin ref: refs/tags/v2.4.0 — so how do changes reach them? The scheme that works (straight from the factory’s ADR on the subject):
TAGS v2.4.0, v2.4.1 ... immutable, what consumers pin. Semver semantics on the PARAMETER CONTRACT: additive+defaulted = minor, breaking (remove/retype/re-mean a parameter) = MAJOR.COMPAT release/v1 branch keeps living after v2 ships - critical fixesBRANCHES cherry-pick back, tagged v1.9.x, so v1 consumers aren't forced to absorb v2's breaks to get a security fix.UPGRADES a published migration note per major; the platform team PRs the bump INTO consumer repos (you own the product; you help ship its upgrades) - never a "please upgrade by Friday" email.Recognize the shape? It’s Release Flow from Lesson 1.2, applied to the template product — trunk carries truth, releases are tags, fixes flow by cherry-pick. The platform team eating its own workflow is not a coincidence; it’s the credibility.
3. Enforcement: extends + required-template checks
The governance mechanism that makes the product real, and the reason 2.2’s starters used extends: Azure DevOps lets you attach a required-template check to protected resources — service connections, agent pools, environments. The check says: this resource may only be used by a pipeline that extends from one of these templates, at this repo/ref. Wire it to the production service connection and the production environment, and:
a bespoke pipeline that skips the templates -> can still build whatever it wants, but CANNOT touch the prod connection - the resource refuses ita pipeline extending starters@v2 -> inherits every gate the starter composes: the scan steps, the health report, the evidence emissionEnforcement lives on the resource, exactly like Lesson 2.1’s approvals lived on the environment — protect destinations, and every path to them inherits the protection. This is also the hook everything later hangs from: Module 3’s policy gates and Module 5’s evidence steps are template-composed, so “extends required” transitively means “scanned and evidenced, required” — provided the template forbids opting those steps out for protected environments (Lesson 3.4 closes exactly that hole). One mechanism, compounding returns.
Ship the governance: (1) add the four-tier CI to our template repo, with thecontract tests generated from each template's parameters block; (2) cutv1.0.0 and a release/v1 branch; (3) attach required-template checks to theprod service connection + prod environments, naming the protected compatbranch release/v1 as the ref (the check takes ONE exact branch or tag - nowildcards - so consumers extend via that branch, which only everfast-forwards to tagged releases);(4) write the consumer-facing one-pager: how to pin, how upgrades arrive,what the check blocks and why. Then demonstrate the denial: a minimalbespoke pipeline attempting the prod connection, and its rejection message.That final demonstration is Zero Trust’s verify-by-denial habit arriving in the delivery world: the governance isn’t real until you’ve watched it say no.
The GitHub equivalents live one level up: org rulesets requiring specific workflows on protected branches (the required-workflow successor), plus environment protection rules, plus the Actions policy that can block un-pinned actions org-wide. The grain differs — GitHub enforces “this workflow must run,” Azure enforces “this resource only serves template-derived pipelines” — but the design rule transfers intact: enforce at the resource/branch, never by convention. Statuses August-2026.
A team on starters v1 hits a bug fixed in v2.1. They ask the platform team to just patch v1's tag in place ('re-tag v1.9.0 with the fix — everyone gets it instantly, no PRs needed'). What does the versioning scheme say?
Key takeaway
The template library earns its product status three ways: CI that treats references and parameter contracts as tested API surface, versioning that moves by immutable tags with compat branches and platform-delivered upgrades, and enforcement attached to protected resources so bypassing the product means losing access to production. Protect destinations; let inheritance do the rest. Next: the newest consumers of this whole apparatus — the agents.