Skip to content

Implementation guide

CI/CD quality gates that teams keep

Adding a quality gate to a pipeline takes an afternoon. Choosing a threshold the team will still respect in three months is the hard part, and it is where most gates quietly die.

Why most quality gates get switched off

The usual sequence: someone enables a gate at a strict threshold, the next morning every open pull request is red, and by the end of the week the gate is either disabled or routinely overridden. Nothing about the code changed. The gate simply asserted a standard the codebase had never met.

A gate is a promise about what you will not merge. If it fires on work nobody intends to fix today, the team learns to ignore it, and you have added friction without adding signal.

Pick the threshold from your current state

DebtDrone gates on severity. --fail-on takes critical, high, medium, low, or none, and fails the command when it finds an issue at or above that level.

  • Start by scanning without a gate — run the scan in CI with --fail-on none for a week and look at what it reports. You are measuring your baseline, not enforcing anything yet.
  • Set the gate just above your baseline — if the repository already carries high-severity findings, gating on high fails every build on day one. Gate on critical, and let high be reported.
  • Tighten on a schedule, not on a whim — move the threshold down one step only once the level above it is reliably clean. A gate that ratchets is a gate people trust.
  • Put the threshold in the workflow file, not in configuration — persistent DebtDrone configuration is per-user, so a fresh CI runner has none of it and would scan with no gate at all. Passing --fail-on explicitly in the pipeline keeps the gate under review like any other change, and makes it impossible to lose by accident.

A pipeline step that does this

The scan runs against a checkout, so a gate is one step. The command exits non-zero when the gate fails, which is all a CI runner needs.

GitHub Actions
- name: Check out the repository
  uses: actions/checkout@v4

- name: Scan for technical debt
  run: debtdrone scan . --fail-on critical --format json

Findings go to stdout, analyzer warnings to stderr, and the gate decides the exit code. The text format is one row per finding, so a failing build shows you what failed it without opening an artifact. --max-complexity defaults to 15, and critical starts above twice that — which is the threshold the message below reports:

Example output — findings table
SEVERITY   FILE:LINE                       RULE   MESSAGE
--------   ---------                       ----   -------
CRITICAL   /workspace/src/handler.go:112   N/A    Function 'ProcessRequest' has critical cyclomatic complexity of 32 (threshold: 30)

--format json gives you the same findings as data if you would rather publish them as an artifact or feed them into another step. Dependency and secret scanning run by default; coverage parsing is opt-in and reads existing coverage artifacts rather than running your test suite for you.

When the gate trips, the command exits non-zero and says which threshold it hit — the message names the severity you configured, so a red build is self-explaining:

Example output — gate failure
quality gate failed: found issues matching or exceeding severity 'critical'

The tradeoffs worth deciding deliberately

  • Blocking versus reporting — a blocking gate changes behaviour but costs goodwill when it is wrong. A reporting-only run costs nothing and gets ignored. Most teams want blocking at critical and reporting below it.
  • Whole repository versus changed files — scanning everything gives a stable number that does not depend on the size of the diff, but it means an unrelated pre-existing finding can fail your pull request. Scanning only what changed avoids that and lets old debt sit undisturbed.
  • Speed versus coverage — dependency scanning adds time proportional to the size of your lockfile. If pipeline duration is the binding constraint, run the full scan on a schedule and the gate on pull requests.
  • A failed scan is not a clean scan — if part of a scan fails, treat that as a build problem rather than a pass. DebtDrone reports partial results and the gate outcome together rather than silently returning success.

If you would rather not own the pipeline step

The hosted product runs the same scanner without a pipeline change. On Pro and Enterprise plans, supported pull-request events update one summary comment on the pull request and publish a commit status, which you can require in a branch protection rule — the same blocking behaviour, configured in GitHub rather than in your workflow file.

The tradeoff is where the decision lives. A CLI gate is versioned with your code and works on any runner; a commit status is easier to set up and keeps history alongside your scan reports. Plan limits are on the pricing page, and the blog covers how the analysis pipeline fits together.

Try it on your own code

Add the gate to a branch and watch one pipeline run before you enforce it anywhere else.