A quality gate is an automated checkpoint in a CI/CD pipeline that blocks a build or release from moving forward unless it clears defined criteria - test coverage, static-analysis scores, security-scan findings, or performance thresholds. Only changes that clear the bar reach the next stage; anything below it fails the pipeline.
How does a quality gate work?
A quality gate is a policy-based checkpoint that a CI/CD pipeline evaluates against the outputs of a build, and that either lets the pipeline continue or fails it. The name comes from manufacturing quality control - the point at which a batch is inspected against a specification and either moves down the line or gets pulled - and the mechanic in software is almost identical. The pipeline runs its tests, its analysers and its scanners, collects the numbers, and compares them to a set of thresholds defined ahead of time. Meet the thresholds, the gate is green and the next stage runs. Miss any of them, the gate is red and the pipeline stops.
A useful quality gate has three moving parts:
- The metrics. Concrete, measurable outputs that the build produces on every run. Line and branch coverage from the test runner, findings and severities from a static analyser (SonarQube, Semgrep, CodeQL), vulnerability counts and severities from an SCA scanner (Snyk, Dependabot, Trivy, Grype), duplication percentage, cyclomatic complexity, bundle size, container image size, cold-start latency from a benchmark step. The rule of thumb: if a human has to interpret it, it is not a gate metric; it belongs in a review.
- The thresholds. The pass/fail rules applied to those metrics. Most teams start with a mix of absolute limits ("no
CRITICALCVEs in production dependencies", "container image ≤ 400 MB") and diff-based limits against the target branch ("coverage on new code ≥ 80%", "no newBLOCKERstatic-analysis issues"). Diff-based rules matter enormously in older codebases: they stop the change getting worse without demanding an unrealistic overnight cleanup of legacy code. - The enforcement point. The place in the pipeline that reads those numbers and either continues or fails. This is usually a step immediately after test / scan / analyse - the earlier the better, because a red gate should stop the pipeline before any expensive downstream work runs.
In practice the gate is one line of pipeline configuration - fail if coverage < 80%, or fail if sonar.projectStatus != OK, or required-status-checks: [ci/quality] on the branch-protection rule. What makes it work is not the syntax but the discipline behind it: the criteria are agreed by the team, measured against real historical data, versioned in the repo alongside the code, and enforced automatically so no one has to remember to check.
Why does it matter?
The reason quality gates exist is that without them, "quality" is whatever the on-call engineer has time to eyeball at 5pm on a Friday. Every team says it cares about test coverage, security posture and code health; almost no team consistently acts on those numbers unless the pipeline enforces them. A quality gate is the mechanism that turns "we should keep coverage high" into "the pipeline will not merge a PR that drops coverage below the line" - and that change, from intent to enforcement, is what actually keeps the codebase from decaying release by release.
The economic argument matters even more than the quality one. The shift-left principle says a defect caught on the developer's laptop costs orders of magnitude less than the same defect caught in production; a quality gate is the concrete artefact that implements shift-left at the branch level. Every problem the gate catches - a new CRITICAL CVE, a coverage regression, a security lint finding, a bundle size that just doubled - is a problem that was found before merge, before deploy, before a customer touched it, and before an incident channel had to be opened. The alternative is not "no cost"; the alternative is "same cost, paid later, with an audit trail no one wants to read".
Quality gates also matter for the humans in the review loop. Code review is expensive and the reviewer's attention is finite; the more the pipeline can decide objectively before a human is asked, the more that reviewer's time goes to the judgement calls only a human can make - "does this design make sense", "does this match the product spec", "is now the right time to ship". A team that runs its coverage, security and static-analysis rules through automated gates ends up doing better code review than a team that does not, because the reviewer is not reading 500 lines of diff wondering whether tests were added; the gate already answered that. That, more than any specific metric, is why quality gates are worth the setup: they buy back reviewer attention.
Quality gate vs manual approval vs branch protection
The three mechanisms overlap in outcome - all of them can block a merge or a deploy - but they answer different questions and pair best when used together.
- Quality gate - the pipeline evaluates measurable criteria against the build's outputs, and passes or fails deterministically. Same input, same result, every time. No human in the loop.
- Manual approval - a named human clicks "approve" (or refuses to) at a specific pipeline step. Used for questions the machine cannot answer: change-management approval, business timing, coordinated release windows.
- Branch protection - a repository-level rule that says "cannot merge to this branch unless X and Y are green", where X and Y are usually a mix of quality-gate results and approvals. It is the plumbing that makes the other two enforceable at the source-control layer, not a separate policy in itself.
A production-grade setup uses all three: quality gates run on every PR and produce the required status checks; branch protection wires those status checks to the merge button so red gates cannot be bypassed; manual approval sits on the deploy step for the tiny number of releases that need a human's timing call. The mistake is trying to use one of the three for the job of another - relying on branch protection alone (nothing measures anything), or using a manual approval to catch coverage regressions (humans forget to check).
How do popular CI/CD tools handle quality gates?
Quality gates are old enough that every serious CI/CD stack has an answer for them, but the depth of that answer varies a lot. What differentiates the tools is how much of the gate you get out of the box vs how much you assemble from primitives.
- SonarQube / SonarCloud is the reference implementation of the concept - the term "quality gate" as it is used today largely comes from Sonar's UI, where a gate is a first-class object with conditions like "coverage on new code < 80%", "duplicated lines > 3%", "security rating worse than A", and a pass/fail verdict the CI job can read. Every other tool below integrates with it. If your goal is deep, language-aware code quality with maintainability, reliability and security ratings on new-code windows, SonarQube is the specialist and is the better fit here - nothing else models the gate itself as richly.
- GitHub Actions + branch protection is the most common shape for open-source and startup teams. The workflow runs tests, coverage, CodeQL and any scanner, publishes required status checks, and the branch-protection rule on
maingates the merge on those checks being green. It is very flexible, free for public repos, and pairs perfectly with SonarCloud or CodeQL. The trade-off is that "the gate" is spread across a workflow file, the branch-protection settings and possibly a third-party dashboard - three places to change if the policy changes. - GitLab CI/CD ships an integrated Code Quality report, a License Compliance report, a SAST report and a Container Scanning report that surface diffs directly on the merge request, with
allow_failure: falseacting as the hard gate. For teams that want the whole "did quality regress on this MR?" story in the MR UI without a second product, this integration is best-in-class. - Jenkins does not have a native quality-gate object, but its plugin ecosystem is enormous: the SonarQube plugin's
waitForQualityGatestep, the JaCoCo plugin for coverage thresholds, the Warnings Next Generation plugin for compiler and lint findings, and thepost { unstable { ... } }block for turning any of those into a hard failure. Powerful and endlessly customisable, at the usual Jenkins cost of operating the controller and keeping the plugins current. - Azure DevOps has literal "release gates" as a built-in Pipelines feature - checkpoints that can query work-item state, invoke a REST endpoint, wait for Azure Monitor alerts to clear, or poll a Sonar quality gate - and it holds a release at the stage boundary until they return green. For teams already inside the Microsoft toolchain this is the most integrated experience of any of the options here.
- Buddy is one of the options we would recommend when the goal is to keep the gate, the test step and the failure branch in a single pipeline file rather than assembled across a workflow, a branch-protection screen and a plugin. Buddy ships a first-class
SONAR_SCANNERaction (with an inlinefail_on_quality_gate: truefield),SNYK_CLI/TRIVYscan actions with configurable severity thresholds, native coverage assertions in the test actions, and per-actiontrigger_conditionsyou can point at any variable the build produced - so a red gate stops the pipeline immediately and there is exactly one file to review when the policy changes. It is worth a look for teams that value that single-file model; it is not the right choice if you specifically want SonarQube's new-code quality-gate object as the source of truth - in that case, run Sonar as the specialist and wire whichever CI tool you prefer towaitForQualityGate.
The honest summary: SonarQube owns the gate itself, GitLab owns the MR-level integration, Azure DevOps owns the release-stage gate for Microsoft shops, and every general-purpose CI (GitHub Actions, Jenkins, CircleCI, Buddy) can wire those specialists into a pipeline. Pick the specialist for the metric that matters most to you; pick the CI tool for how you want the gate wired to the rest of the delivery flow.
Example
The pipeline below shows a realistic quality gate on a Node.js service: run the tests, publish coverage and SAST findings, hold the build on a SonarQube quality-gate verdict, block on any new HIGH or CRITICAL vulnerability, and only then let the deploy step run. If any check comes back red, the pipeline stops before the artifact is ever published.
# .buddy/buddy.yml - quality gate before deploy
- pipeline: "quality-gate-then-deploy"
events:
- type: "PUSH"
refs:
- "refs/heads/main"
- "refs/pull/*"
actions:
- action: "Install & test"
type: "BUILD"
docker_image_name: "node"
docker_image_tag: "20"
commands: |-
npm ci
npm run lint
# fail the pipeline if line coverage drops below 80%
npm test -- --coverage --reporters=default --coverageReporters=lcov
- action: "SonarQube quality gate"
type: "BUILD"
docker_image_name: "sonarsource/sonar-scanner-cli"
docker_image_tag: "latest"
variables:
- key: "SONAR_HOST_URL"
value: "https://sonar.example.com"
commands: |-
# sonar.qualitygate.wait hard-fails the action unless the gate returns PASSED
sonar-scanner \
-Dsonar.projectKey=web-app \
-Dsonar.host.url=$SONAR_HOST_URL \
-Dsonar.token=$SONAR_TOKEN \
-Dsonar.qualitygate.wait=true
- action: "Dependency vulnerability scan"
type: "SNYK_CLI"
integration: "snyk"
commands: |-
# exits non-zero on any HIGH/CRITICAL finding = pipeline fails
snyk test --severity-threshold=high --fail-on=upgradable
- action: "Container image scan"
type: "TRIVY_CLI"
commands: |-
# exit 1 on any HIGH/CRITICAL vulnerability; skip unfixed findings
trivy image --severity HIGH,CRITICAL --exit-code 1 --ignore-unfixed registry.example.com/web-app:$BUDDY_RUN_ID
# only runs if every gate above was green
- action: "Publish artifact"
type: "BUILD"
docker_image_name: "ubuntu"
docker_image_tag: "22.04"
commands: |-
bdy artifact publish web-app:$BUDDY_RUN_ID ./dist --create
- action: "Deploy to production"
type: "BUILD"
docker_image_name: "ubuntu"
docker_image_tag: "22.04"
commands: |-
bdy distro route update prod-distro \
--domain=example.com \
--target=artifact=web-app:$BUDDY_RUN_ID
# runs on the first failed action above - never on success
- action: "Notify on quality-gate failure"
type: "SLACK"
integration: "slack"
channel: "#deploys"
content: "🚫 Quality gate failed for $BUDDY_EXECUTION_URL - deploy stopped."
run_only_on_first_failure: true
Two properties make this a real quality gate, not just a sequence of test steps. First, every check is a hard failure - fail_on_quality_gate: true, --severity-threshold=high --fail-on=upgradable, exit_code: 1 - so a red result stops the pipeline instead of being logged and ignored. Second, the deploy actions come after the gates and only run if every gate passed; there is no way to publish an artifact whose coverage regressed, whose Sonar rating dropped, or whose image carries a new CRITICAL CVE. That is what turns a set of independent scanners into a single decision the pipeline makes on your behalf. See the Buddy SonarQube action reference for the full set of fields available on the gate action.
Frequently asked questions
What is the difference between a quality gate and a smoke test?
They sit at different points in the pipeline and answer different questions. A **quality gate** runs *before* a deploy, against the code and its build outputs, and asks "does this change meet the standards we agreed on?" - it looks at coverage numbers, static-analysis reports, SCA vulnerability counts, bundle size, license findings, and so on. A **[smoke test](/smoke-test/)** runs *after* a build or deploy, against a running instance of the system, and asks "does this thing actually come up and work?" - it hits real endpoints on a real process. A change that clears every quality gate can still fail its smoke test (the artifact builds cleanly but the container will not boot); a change that passes smoke tests can still be blocked by a quality gate (the app runs fine but coverage dropped below the threshold). A healthy pipeline runs both, in that order.
Who decides the criteria for a quality gate?
Whoever owns the code, calibrated against real data - not "industry best practice" numbers pulled off a blog. The pattern that works is to measure the current state on `main` for a couple of weeks (coverage, mean scan findings, p95 build time, bundle size) and set the gate at *slightly better than today's median* rather than at some aspirational figure. That way the gate stops regressions without failing every PR on day one. Ratcheting the threshold up over time - a "coverage must be within 2 points of `main`" rule rather than a fixed "coverage ≥ 80%" - keeps the bar honest as the codebase evolves and avoids the classic failure mode where the gate is either so loose it catches nothing or so strict everyone learns to bypass it.
Should a failing quality gate block a merge or just warn?
Block, if you actually mean it. A gate that only warns is not a gate - it is a dashboard, and every team eventually learns to ignore dashboards. The point of the mechanism is that "cannot merge / cannot deploy" is a bright line the pipeline enforces without human negotiation. That said, blocking gates only work if the criteria are calibrated and the *override path* is explicit: a documented "break-glass" flow (a specific approver, a labelled PR, an audit-logged event) so genuine emergencies are not stalled by a coverage rule. If your team is bypassing the gate more than once a week, the threshold is wrong; fix the threshold, do not demote the gate to a warning.
How is a quality gate different from a manual approval?
A quality gate is a machine judgement against measurable criteria; a manual approval is a human judgement against everything else. The two are complementary, not alternatives - the gate handles the questions a computer can answer objectively (did coverage drop, is there a critical CVE, did the build size regress by 30%?), and the approval handles the questions a computer cannot (is now a good time to ship, does this feature match what the customer agreed to, is the on-call awake?). A well-designed pipeline uses gates to *eliminate* the approvals that should be automated, so the ones that remain are the ones that genuinely need a human.
Suggest a new word or an edit to an existing one. Every submission is reviewed before it goes live.