Skip to content
All writing
4 min read

Governance gates belong in parallel, not in a chain

Security, cost and lint checks are independent. Running them in sequence turns one review cycle into four.

Most infrastructure pipelines are a chain: lint, then plan, then policy, then cost. It reads naturally, each step builds on the last, and it is the default in every example pipeline you will find. It is also the reason engineers batch up changes and stop reading CI output.

What a chain does to feedback

In a chain the first failure hides everything after it. Push a change with a lint error, a policy violation and a cost regression, and you learn about the lint error. Fix it, push, wait, learn about the policy violation. Fix it, push, wait, learn about the cost.

Three round trips to discover three problems that were all knowable on the first run. If a pipeline takes five minutes, that is a fifteen-minute review loop with two context switches in it. The rational engineer response is to make fewer, larger changes — which is the opposite of what the gates were introduced to encourage.

They are not actually sequential

The chain implies a dependency that mostly is not there. TFLint reads the configuration. Conftest reads the plan. Infracost reads the plan. Only the plan itself is a genuine prerequisite, and it is a prerequisite for two of the checks, not a step between them.

change
→
plan
TFLintstatic syntax
Conftestsecurity policy
Infracostcost delta
→
merge
one round trip · every class of failure reported in parallel

Fan out after the plan, join before the merge. One run surfaces every category of problem, and the reviewer sees the full picture of what a change costs before deciding anything.

Evaluate policy against the plan, not the source

A detail that matters more than the ordering: policy should read the plan output, not the HCL. Static analysis of source sees what was written. The plan sees what will be created, with module defaults resolved, variables substituted and computed values filled in.

A rule like "no public S3 buckets" is trivially evadable at the source level, because the value that makes the bucket public may arrive from a variable, a default three modules deep, or a workspace-specific override. Against the plan there is nowhere for it to hide.

Cost belongs in the same row

Cost is usually treated as a monthly review rather than a gate, which means it surfaces weeks after the change that caused it, to someone who did not make it. Putting Infracost alongside the security checks moves that conversation to the point where the change is still one revert away — and quietly says something about the platform: money is a correctness property, not an afterthought.