QA Engineer Skills 2026QA-2026Quality Gates

Quality Gates

What Is a Quality Gate?

A quality gate is a checkpoint in your pipeline that blocks progression if criteria are not met. It is the automated equivalent of a human approver, but faster, more consistent, and impossible to forget.

A gate that can be bypassed is not a gate. Configure gates as required checks on your main branch protection rules.


Common Quality Gates

All Tests Pass

The most basic and most important gate. A single test failure blocks the merge or deploy.

# GitHub branch protection: Require status checks to pass
# Settings > Branches > Branch protection rules > Require status checks
# Select: "unit-tests", "integration-tests", "browser-tests"

Why this gate matters: If you allow merging with failing tests, developers quickly learn that test failures are ignorable. Within weeks, nobody trusts the test suite, and the pipeline becomes decoration.

Code Coverage Threshold

Require that coverage does not drop below a baseline.

# Using a coverage check action
- uses: codecov/codecov-action@v4
  with:
    fail_ci_if_error: true
    flags: unittests

# Or enforce in the test command itself
- run: npx jest --coverage --coverageThreshold='{"global":{"branches":75,"functions":80,"lines":80}}'

Better approach: Rather than enforcing an absolute threshold (e.g., "80% overall"), require that new code is covered. A legacy codebase at 60% coverage should not block PRs that add well-tested new features.

# Codecov configuration (codecov.yml)
coverage:
  status:
    patch:
      default:
        target: 90%  # New code must be 90% covered
    project:
      default:
        target: auto  # Overall coverage must not decrease

Security Scanning

Integrate SAST (Static Application Security Testing) and DAST (Dynamic Application Security Testing) scanners and fail on findings above a threshold.

# Example: Snyk security scan
- uses: snyk/actions/node@master
  env:
    SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
  with:
    args: --severity-threshold=high  # Fail on high/critical vulnerabilities

Gate levels:

  • Critical/High vulnerabilities: Block the merge. These must be fixed before shipping.
  • Medium vulnerabilities: Warn but allow merge. Create a follow-up ticket.
  • Low vulnerabilities: Informational only. Track but do not block.

Performance Budgets

Enforce performance standards to prevent gradual degradation.

# Lighthouse CI
- run: npx lhci autorun
  env:
    LHCI_BUILD_CONTEXT__CURRENT_HASH: ${{ github.sha }}

# lighthouse-ci configuration
# lighthouserc.js
module.exports = {
  ci: {
    assert: {
      assertions: {
        'categories:performance': ['error', { minScore: 0.9 }],
        'categories:accessibility': ['error', { minScore: 0.95 }],
        'first-contentful-paint': ['warn', { maxNumericValue: 2000 }],
        'largest-contentful-paint': ['error', { maxNumericValue: 2500 }],
      },
    },
  },
};

Other performance gates:

  • Bundle size limits: Fail if the JavaScript bundle exceeds a threshold
  • API response time p95: Fail if the 95th percentile response time exceeds the budget
  • Image optimization: Fail if unoptimized images are added

Lint and Format Checks

Enforce code style so reviews focus on logic, not formatting.

lint:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - run: npm ci
    - run: npm run lint          # ESLint
    - run: npm run format:check  # Prettier --check
    - run: npm run typecheck     # tsc --noEmit

Why gate on formatting: Without automated enforcement, code reviews devolve into arguments about semicolons and indentation. Automate the trivial decisions so humans focus on architecture and logic.


Configuring Gates in GitHub

Branch Protection Rules

  1. Go to Settings > Branches > Branch protection rules
  2. Add a rule for main
  3. Enable "Require status checks to pass before merging"
  4. Select the specific checks that must pass (e.g., unit-tests, lint, security-scan)
  5. Enable "Require branches to be up to date before merging" (ensures the PR is tested against the latest main)
  6. Enable "Require pull request reviews before merging" (human gate)

Required vs Optional Checks

Not every check needs to be required. Use required checks for gates that must never be bypassed, and optional checks for informational feedback.

Check Required? Rationale
Unit tests Yes Broken logic must not reach main
Lint / format Yes Consistent code style is non-negotiable
Integration tests Yes API and database issues must be caught
Browser tests Yes (on main) UI regressions must be caught before deploy
Coverage (patch) Yes New code must be tested
Coverage (overall) No Legacy code coverage should improve over time, not block PRs
Security scan Yes (high/critical) Critical vulnerabilities must not ship
Performance budget No (initially) Start as informational, promote to required once budgets are stable

Common Pipeline Anti-Patterns

Anti-Pattern Problem Fix
Tests only run on main Bugs discovered after merge, harder to fix Run tests on every PR
No artifact collection Failures require re-running the entire pipeline to debug Always upload reports, screenshots, logs
Hardcoded secrets Credentials in YAML files visible in repo history Use platform secret stores
Monolithic pipeline One job runs everything sequentially for 40 minutes Split into parallel jobs with dependencies
Ignoring flaky tests Tests are allow_failure: true and nobody investigates Quarantine flaky tests, track and fix them
No caching Every run installs dependencies from scratch Cache based on lockfile hash
Bypassable gates "Admin override" used routinely instead of exceptionally Require admin approval for overrides; track bypass frequency
Too many gates Every PR triggers 45 minutes of checks Tier gates by trigger: fast on push, thorough on PR, full on merge

Pipeline Configuration Tips for QA

Environment Variables for Test Configuration

env:
  TEST_TIMEOUT: 30000       # Per-test timeout in milliseconds
  RETRY_COUNT: 2            # Retries for infrastructure flakes
  HEADLESS: true            # Headless browser mode
  SLOW_MO: 0                # No artificial delay
  WORKERS: 4                # Parallel test workers
  CI: true                  # Signal to test framework that we are in CI

Retry Logic for Flaky Infrastructure

Retries should handle infrastructure flakes (network timeouts, container startup delays), not mask test bugs.

- run: npx playwright test --retries=2
  timeout-minutes: 30
  continue-on-error: false

If a test consistently needs retries, quarantine it and fix the root cause.

Notifications

Send Slack/Teams messages on pipeline failure so the team responds quickly:

notify:
  needs: [unit-tests, browser-tests]
  if: failure()
  runs-on: ubuntu-latest
  steps:
    - uses: slackapi/slack-github-action@v1
      with:
        payload: |
          {
            "text": "Pipeline failed on ${{ github.ref_name }}: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
          }
      env:
        SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

Hands-On Exercise

  1. List all the quality gates currently in your pipeline. Are any missing from the common gates above?
  2. Check your branch protection rules. Are all critical checks marked as required?
  3. Intentionally try to merge a PR with a failing required check. Verify it is blocked.
  4. Add a coverage gate that requires new code to be at least 80% covered
  5. Add a security scan and configure it to fail on high-severity findings
  6. Set up failure notifications to your team's Slack channel

Interview Talking Point: "I treat the CI/CD pipeline as part of the test infrastructure, not just a deployment tool. I structure pipelines with unit tests as a fast gate on every push, integration tests on PRs, and full browser suites before deploy -- each stage providing progressively deeper confidence. I optimize with dependency caching, test sharding across parallel runners, and selective execution based on changed paths. When a pipeline takes more than 15 minutes for the PR feedback loop, I treat that as a bug to fix. I configure quality gates as required checks on branch protection rules -- all tests pass, coverage does not decrease on new code, no critical security vulnerabilities -- so the pipeline is a reliable safety net, not just decoration."