CI 通過不足以判定軟件可發佈,作者提出六項獨立關卡
A Release Gate Isn't a Checklist
Micky Irons 指出,軟件發佈不能只憑 CI 測試通過,並提出以六項由具名負責人獨立把關的檢查決定能否部署。六項檢查涵蓋工程準備、用戶體驗、安全與數據主權、知識產權及授權來源、治理簽批,以及控制措施與審計;每項須附證據,未通過或欠缺證據即阻止發佈。作者亦建議按變更影響範圍調整檢查要求,並為關卡負責人設置後備人選及升級流程。
A few months back I had a release candidate sitting in front of me with a green CI pipeline, coverage numbers that would make most teams happy, and a changelog nobody could argue with. By every metric my pipeline tracked, it was ready to ship. It also bundled a dependency whose licence terms didn't actually permit the deployment model we were planning for that release. Nothing in the test suite caught that, because the test suite was never built to catch it.
That's the gap I want to talk about. Not 'write more tests', but what 'ready to release' actually has to mean once you're shipping into a regulated or high-stakes environment, where a mistake isn't a bug ticket, it's a breach, a legal exposure, or a regulator asking why nobody signed off on something. 'Tests pass' is necessary. It is nowhere near sufficient, and treating it as the whole answer is how teams end up with incidents that have nothing to do with code quality.
Why one green checkmark isn't a release decision
CI answers one question: does the code do what I told it to do. That's a different question from is this safe to run on someone else's infrastructure, can a new user actually operate it without causing damage, did we actually have the right to ship this dependency, and is there a named human who is accountable if this goes wrong. Those are four separate questions, answered by four different kinds of expertise, and almost none of them compress into an automated assertion.
The usual failure mode is picking one of two bad options. Option one: try to encode everything into the pipeline, which mostly doesn't work because licence provenance and governance sign off aren't the kind of thing a unit test can evaluate. Option two: let engineering green light stand in for the whole decision, and quietly hope the security review, the UX pass and the legal check happened somewhere upstream. On a small team under deadline pressure, 'somewhere upstream' is usually nowhere at all.
A gate per concern, not a meeting per concern
The pattern that's worked for me is to treat a release as gated on several independent, named checks, each with its own owner and its own falsifiable criteria, rather than one holistic 'is this good' judgement call. On the systems I've built at Mickai, that's six gates: engineering readiness (does it work, is it reversible, what's the rollback plan), UX readiness (can the intended user actually use this without a support ticket), security and sovereignty review (what's the attack surface, and for anything running on someone else's premises, does data actually stay where the contract says it stays), IP and licensing provenance (do I actually have the rights to ship every component in this build, including anything pulled in transitively), governance sign off (has an accountable person actually looked at this and said yes, with their name attached), and a controls and audit gate (can I reconstruct, after the fact, exactly what shipped, who approved it, and why).
None of those six gates is new as a concept. What matters is making them structurally equal and independently blocking, so that a green pipeline literally cannot ship a release on its own. The simplest way I've found to do that is a gate manifest that sits alongside the release candidate, checked by the release job before anything is allowed to deploy.
# release-gate.yaml, checked by CI before the deploy job is allowed to run
release: "v2.14.0"
gates:
engineering:
owner: "lead-eng"
status: pass
evidence: "ci-run-8841"
ux:
owner: "design-lead"
status: pass
evidence: "usability-notes-v214.md"
security_sovereignty:
owner: "security-lead"
status: pass
evidence: "threat-model-v214.pdf"
ip_provenance:
owner: "legal-eng-liaison"
status: pass
evidence: "dependency-licence-scan-8841.json"
governance:
owner: "release-board"
status: pass
evidence: "governance-minutes-2026-10-14.md"
audit_controls:
owner: "compliance"
status: pass
evidence: "control-mapping-v214.xlsx"
def can_release(manifest):
for name, gate in manifest["gates"].items():
if gate["status"] != "pass":
return False, f"blocked on {name} (owner: {gate['owner']})"
if not gate.get("evidence"):
return False, f"{name} has no evidence attached, treat as fail"
return True, "all gates green"
The three ways this turns into theatre
The first time I set something like this up, it immediately started failing in predictable ways, and they're worth naming because they're what makes a gate framework fake rather than real.
Rubber stamping is the big one. A gate that's just a boolean someone clicks true on becomes a formality within about three releases. The fix is requiring an evidence reference for every gate, not just a status, so 'pass' means 'here is the artefact that justifies this', not 'someone with the right job title didn't object'. The pseudocode above fails closed if evidence is missing, which sounds pedantic until you notice how often that's exactly where the shortcut happens.
Gate sprawl is the opposite failure. If every release needs all six gates fully re-run regardless of size, releasing becomes a quarterly event and people start batching changes to avoid the overhead, which defeats the entire point of shipping safely and often. The fix is proportionality: classify the change by blast radius first, and let a genuinely low risk patch skip the governance and audit gates by policy, not by someone deciding on the day that it probably doesn't matter.
The third is the single point of failure. One named owner per gate is good for accountability and bad for availability, the gate owner goes on leave and the release sits blocked for two weeks. A named deputy and a time boxed response window, with an explicit escalation path rather than a silent bypass, is what keeps the gate meaningful instead of becoming the reason nothing ships.
Where this got concrete for me
Building a sovereign, on-premise AI platform makes two of these gates stop being abstract fairly quickly. The security and sovereignty gate is the one that actually caught a real problem: an assumption baked into how a service handled data residency broke the moment it ran air gapped rather than connected, something no amount of unit testing in my normal CI environment was ever going to surface, because the test environment itself had network access the production environment deliberately didn't. The IP and provenance gate caught a dependency whose redistribution terms were fine for a hosted SaaS model and not fine for something I was licensing for a customer to run entirely inside their own infrastructure. Neither of those is a code quality problem. Both would have shipped past any pipeline built only to check that the code does what it claims to do.
I'd genuinely like to know how other teams handle this at smaller scale, where six distinct named owners isn't realistic and you've maybe got two or three people wearing all these hats between them. Do you collapse several gates into one reviewer with a structured checklist, run them as sequential annotations on a single document instead of separate sign offs, or something else entirely? I suspect the six concerns stay the same regardless of team size, it's the ownership structure around them that has to flex, and I haven't settled on a version of that for a three person team that I'd fully trust yet.
來源:DEV Community · dev.to