It's May 2022, and MegaLinter has won. If you start a new repo today and need linting, you'll see MegaLinter on Stack Overflow, in dev blogs, and in every second pull-request template. One image, a hundred linters, one YAML, done. For 80 % of repos, that's exactly right.
This article is for the other 20 %. The ones where the MegaLinter image stretches your CI by four minutes. Where 95 of the bundled linters target tools you don't have. Where a generic fail message tells you "MegaLinter found problems" and you have to open the MegaLinter docs to find out which of the hundred linters is trying to say something.
The alternative: four clean layers, each with a clear responsibility, all individually pinnable.
Layer 1 — Language Linters
Check syntactic correctness and language-specific best
practices. The mandatory layer — nothing reaches main without
it. Typical tools: hadolint for Dockerfiles,
tflint for Terraform/OpenTofu,
ShellCheck for Bash, yamllint for
generic YAML, actionlint for GitHub-Actions
workflows.
Each of these tools is small (Alpine images around 20-30 MB),
fast (seconds), and has a clear job. Pin to specific
versions — hadolint:2.10-alpine, not
:latest — or a major upgrade will topple your
pipeline.
Layer 2 — Format Linters
Check whether files are uniformly structured. The hygiene
layer. Typical tools: markdownlint for Markdown,
Prettier for JS/TS/CSS/JSON/YAML,
editorconfig-checker for cross-language
conventions.
Boundary to Layer 1: Layer 1 asks "is it correct?", Layer 2
asks "does it read consistently?". Format linters often have
a --fix option — allow it locally in pre-commit,
check only in CI.
Layer 3 — Convention Linters
Check git metadata — commits, branch names, release notes.
The workflow layer. Most important tool: commitlint
with @commitlint/config-conventional.
This layer runs before the others sequentially, because if the commit message is broken the rest doesn't matter. semantic-release and automated versioning are a house of cards without convention linting.
Layer 4 — Domain Linters
Check domain-specific structures — OpenAPI specs, feature
files, Helm charts. The contract layer. Typical tools:
Spectral for OpenAPI/AsyncAPI,
gherkin-lint for BDD features,
helm lint and kubeconform for
Kubernetes manifests.
This layer only runs when relevant files have changed (path filters). Saves CI time, because not every commit touches domain files.
What that looks like in your pipeline
Three stages, four layer jobs:
- Stage 1 — Convention. commitlint, sequential.
- Stage 2 — Language + Format + Domain. Three jobs in parallel.
Total runtime after cache warm-up: typically 90-120 s. MegaLinter equivalent: 6-8 min. Four to five times faster, with clearer error output and individually pinnable tool versions.
What you can take away today
Three rules of thumb:
- Pin every tool version. No
:latesttags. A tool upgrade is a pull request. - Local = CI. What runs locally with
just lintis exactly what runs in CI. Otherwise the pipeline becomes a feedback loop with a three-minute delay. - One layer per quarter. If your repo isn't yet layered, start with Layer 1. Adopting all four at once burns team patience.
If you want to go deeper
The short book "Linting in Layers — Four Layers Instead of One Mega-Linter" develops each layer over its own chapter with setup snippets per tool, CI and pre-commit configurations for GitHub Actions, GitLab CI, and Forgejo Actions, plus a worked-through example on a Postgres-operator repo. Three diagrams, a cheatsheet to pin on the wall. Around 80 pages, 90 minutes of reading. Published May 2022 as paperback and eBook via epubli.