Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Set up a small, repeatable quality gate: run formatting, linting and tests with commands defined by the project; execute those same commands on pull requests and important branch pushes; then configure the repository to require the checks before merging. A workflow that runs is not automatically a merge gate, and a required check that never runs can block contributors.

What automated code quality checks do

Automated checks run tools against code and report whether it meets defined rules. They can catch common mistakes and make changes more consistent, but each check answers a different question; none proves that a program is bug-free or that it meets every requirement.

Choose checks for the risks in your project

Formatting

A formatter enforces a consistent layout. It does not establish correctness. For example, prettier . --check checks files without rewriting them and exits with code 1 when formatting changes are needed. Keep the formatter configuration and version with the project so local runs and CI agree. Prettier CLI documentation and Prettier installation guidance explain check mode and project-local installation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Linting

A linter flags patterns covered by its configured rules, including some likely bugs and inconsistent conventions. It cannot detect problems outside those rules, and overly broad or noisy rules can make a gate frustrating. ESLint’s current getting-started guide uses the flat eslint.config.js format and specifies supported Node.js prerequisites: ESLint getting started.

Tests

Tests check specified behaviors when they run. Passing tests do not prove all behavior is correct; their value depends on what they exercise. Start with the test command developers already use, make CI run it, and expand tests around important behavior and regressions.

Type checks

A type checker can find certain mismatches in typed code. Its strictness is a project policy, not a universal setting. For example, mypy’s --strict enables a defined group of checks, and that group may change between releases; consult the mypy command-line reference before relying on a particular behavior.

Security and dependency checks

Security scanners and dependency vulnerability checks add a separate layer. State which tool and vulnerability data source the project uses, and review what its findings mean. These checks do not replace code review, tests or careful dependency updates.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Coverage

Coverage reports indicate which code tests exercised, not whether the tests are effective. Set a threshold only if it reflects a deliberate team policy; there is no universal percentage that guarantees quality.

Standardize the commands contributors and CI will run

  1. Inventory the repository. Identify its languages, supported runtime versions, package or build manager, test command, generated and vendor directories, and existing CI host.
  2. Choose a small first gate. Start with deterministic formatting and lint checks plus the test command already used by developers. Add type or security checks when they address a real project need.
  3. Commit configuration and reproducibility inputs. Keep rules in version control, commit the dependency lockfile, and pin tools and CI actions according to a review and update policy. Using “latest” can change results without a code change.
  4. Expose shared project commands. Add scripts or task-runner targets such as format:check, lint, typecheck and test, using the repository’s own package manager and naming conventions. Have CI call these commands rather than maintain a separate implementation of the checks.
  5. Account for non-source files. Configure tools to exclude generated, vendored, build and cache directories where appropriate. Check that exclusions do not hide source code.
  6. Run the commands locally. Fix existing findings or choose a visible baseline or gradual-adoption plan before making a check mandatory. Do not silently suppress all existing findings.

Add the checks to pull-request CI

Run checks on proposed changes and on pushes to the important branch or branches. Put fast checks early and run independent checks in parallel if the CI host supports it. A slow integration suite may be worth retaining in the gate if omitting it could allow important regressions.

The following GitHub Actions workflow illustrates a Python project using Ruff and pytest. It is a pattern, not a drop-in configuration: change the Python version, dependency installation and test command to match the repository. The example uses action tags for readability, not as the strongest security option.

name: quality

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  quality:
    name: quality
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: python -m pip install --upgrade pip
      - run: pip install -e ".[dev]"
      - run: ruff check .
      - run: ruff format --check .
      - run: pytest

GitHub’s Python build-and-test tutorial shows the general workflow pattern, including Ruff. Ruff can be configured in pyproject.toml, ruff.toml or .ruff.toml; ruff check and ruff format --check perform different jobs. See Ruff configuration and the Ruff formatter documentation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For GitHub Actions, workflow files live under .github/workflows. Events, branch filters and path filters determine when workflows run; review the workflow syntax reference when adapting triggers. For a monorepo, path filters can save time, but validate them against source changes and shared files such as configuration, build scripts and lockfiles. An accidentally skipped gate is not useful protection.

For another runtime version, use a matrix only when the project actually supports those versions and each result is meaningful. Specify the versions deliberately instead of assuming a single runner environment covers all users. The label ubuntu-latest can move as the hosted runner image changes, so it does not guarantee an identical environment over time.

On GitLab, pipeline and job configuration goes in .gitlab-ci.yml. To run a merge-request pipeline, configure rules that match CI_PIPELINE_SOURCE == "merge_request_event" or the corresponding workflow rules. See GitLab pipelines and GitLab merge-request pipelines. On any host, verify that the pipeline runs for the actual proposed-change event and that repository merge settings require its result.

Require successful checks before merging

After confirming the workflow runs, configure the target branch’s protection or equivalent merge policy to require its check results. On GitHub, required status checks are configured through branch protection. Choose the actual check name shown by the workflow and keep job names unique: duplicate names across workflows can make a required check ambiguous. See GitHub protected branches.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

GitHub’s strict up-to-date behavior requires a change branch to include the current base branch before merging, which can cause checks to run again. Loose behavior avoids that requirement, but a passing result may predate changes to the base branch. A merge queue is another option where supported. Availability of branch-protection features can depend on repository visibility and plan, so check the host’s current settings for your repository.

Test the policy in both directions: confirm a passing change shows the expected result, then make a controlled failing change and verify it blocks merging. Restore the code afterward. A green badge alone does not establish that merging is blocked.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Use local hooks as an optional early warning

Local hooks can run quick checks before a commit, reducing avoidable CI failures. They are a convenience, not the shared enforcement point: contributors may not install them or may bypass them. CI should remain required for the protected branch. For repositories configured with pre-commit, pre-commit install installs hooks and pre-commit run --all-files runs configured hooks across the repository; see pre-commit usage.

Introduce checks in a legacy repository

When existing code already violates proposed rules, choose a transition the team can see and maintain:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Fix findings first: gives the repository a clean starting point, but can create a large initial cleanup.
  • Baseline existing findings: lets the team fail on new problems while tracking old ones for later work. Make the baseline visible and give it an owner or review plan so it does not become permanent invisible debt.
  • Check changed files or lines first: enables gradual adoption with smaller pull requests, but leaves old issues in untouched code. Expand coverage deliberately over time.

Decide whether warnings fail the build. A tool that only reports findings but always exits successfully provides visibility, not an enforced merge gate. Increase strictness when the team can act on findings without creating disproportionate noise or blocking unrelated work.

Troubleshoot missing or confusing results

  • The workflow does not appear on a pull request: check that its event and branch rules match the proposed change. GitLab merge-request pipelines need matching merge-request rules; GitHub workflow event and path filters also affect whether a workflow runs.
  • A required check remains pending or cannot be selected: check that the workflow runs for that change and that the required name matches the actual job or status check. Give jobs unique names. On GitHub, path and branch filters can prevent a required result from being produced.
  • A job is green but the expected work did not run: inspect conditional jobs and path filters. GitHub documents that a skipped job reports success, so confirm the intended checks actually executed rather than treating a skip as evidence that code passed. See GitHub status checks.
  • A check passes locally but fails in CI: compare runtime and tool versions, dependency installation, lockfile use, environment variables and runner assumptions. Pin versions where repeatability matters and update them intentionally.
  • Tests fail intermittently: distinguish infrastructure failures from deterministic regressions. Track and fix flaky tests instead of retrying indefinitely, which can obscure real failures.
  • Checks take too long or generate noise: review whether rules fit the project, move fast checks earlier, and parallelize independent work where practical. Use path filtering only after validating what it excludes.

Document the standard local commands, what a failure means and how to request a rerun when the failure is infrastructure-related. Any temporary waiver should be explicit, limited in scope and revisited rather than becoming a standing exception.

Protect CI when changes come from outside the team

Treat pull-request code from forks as untrusted. GitHub’s pull_request event restricts token permissions for fork pull requests and withholds secrets by default. By contrast, pull_request_target runs with elevated trust; checking out and executing fork code under that event can expose secrets or write access. Avoid that pattern. See GitHub’s guidance on secure use of pull_request_target.

Grant workflows only the permissions they need, review third-party action code and pin actions to full-length commit SHAs when using the immutable option. Tags are convenient but can move; audit updates rather than assuming a tag is fixed. GitHub explains these risks in its Actions secure-use guidance.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.