DEV Community

Cover image for Choosing a Markdown linter for your docs pipeline: what each tool actually covers
Kazu
Kazu

Posted on • Edited on

Choosing a Markdown linter for your docs pipeline: what each tool actually covers

If you're setting up a documentation quality gate — for a Go service, a platform team's runbooks, or any repo where Markdown is the source of truth — you've probably hit the same decision point: four or five tools come up in search results, they all call themselves "Markdown linters," and it's genuinely unclear what each one actually catches versus what it quietly ignores.

This is a practical breakdown of the major options, what each one covers, where each one stops, and which use case each one fits best.


The tools and what they actually do

Tool Language Stars Rules Nature
markdownlint (DavidAnson) JavaScript ~5,900 60 built-in Structural linter
remark-lint JavaScript ~1,000 ~80 packages Structural linter (AST-based)
markdownlint (Ruby) Ruby ~2,009 ~50 Structural linter
mdformat Python ~726 N/A Auto-formatter
gomarklint Go 8 Structural linter + link checker

The distinction that matters most isn't language — it's whether the tool reports violations or silently rewrites files. mdformat is an auto-formatter: it won't tell you a heading level is wrong; it will just fix it. If you want visibility into what's broken (for PR review, for CI blocking), you want one of the linters, not a formatter.


If you need the broadest rule coverage: markdownlint

For teams that want thorough enforcement of documentation conventions — heading order, blank lines around elements, consistent list markers, trailing spaces, bare URLs — markdownlint (DavidAnson) is the mature choice. 60 built-in rules, a VS Code extension with wide adoption, and a CLI that integrates into any CI runner.

The cost: it requires Node.js. In a Go or Rust project where you've deliberately kept the toolchain lean, adding a JS runtime just for linting is a real friction point.

Pick this if: your team already runs Node.js in CI, or you need fine-grained rule configuration across many rule categories.


If you need AST-level extensibility: remark-lint

remark-lint operates on the parsed AST rather than raw text, which makes it uniquely suited to writing custom rules that understand document structure — not just surface patterns. Around 80 rules available across packages. The pluggable architecture is its strength; the configuration surface area is also its steepest learning curve.

Pick this if: you need to write project-specific rules, or you're already in the unified/remark ecosystem.


If you need format + dead-link validation in one binary, no runtime: gomarklint

gomarklint is a Go binary with no runtime dependencies. Download it, run it. It covers structural linting and two checks the JS tools don't touch at all:

Live external link validation — most linters ignore whether [see the RFC](https://...) actually resolves. gomarklint makes real HTTP requests, concurrently, and reports 404s and timeouts. ~2,000 links in under 10 seconds.

gomarklint . --enable-link-check
Enter fullscreen mode Exit fullscreen mode

Unclosed code block detection — tolerant AST parsers silently repair a missing closing fence. gomarklint uses a text-based pass that catches the raw break — the one that causes everything below it to render as code.

The tradeoff is honest: 8 rules versus markdownlint's 60. If you need blanks-around-headings, no-bare-urls, or trailing-space enforcement today, gomarklint doesn't have them yet.

Pick this if: your repo is Go or polyglot and you want zero runtime overhead, or dead external links and structural breaks (unclosed fences, heading skips) are your highest-priority catches.


How the rules map across tools

For teams evaluating overlap before switching or combining tools:

gomarklint Rule markdownlint remark-lint
final-blank-line MD047 final-newline
unclosed-code-block — (unique) — (unique)
empty-alt-text MD045
heading-level MD001 + MD041 heading-increment
duplicate-heading MD024 no-duplicate-headings
no-multiple-blank-lines MD012 no-consecutive-blank-lines
external-link — (unique) — (unique)
no-setext-headings MD003 heading-style

unclosed-code-block and external-link have no equivalent in the major JS tools. Everything else in gomarklint's current set has a direct counterpart — so if you're already on markdownlint, you're not losing coverage by keeping it for the rules gomarklint doesn't yet implement.


Quick-reference decision guide

  • Broadest rule set, VS Code integration → markdownlint (DavidAnson)
  • Custom rules, AST access → remark-lint
  • Auto-fix without review → mdformat
  • No runtime, dead-link detection, CI binary → gomarklint
  • Maximum coverage today → markdownlint + gomarklint's --enable-link-check as a second pass

Try gomarklint

go install github.com/shinagawa-web/gomarklint@latest
gomarklint init
gomarklint .
Enter fullscreen mode Exit fullscreen mode

Full documentation: https://shinagawa-web.github.io/gomarklint/


Which check has caught the most real issues in your docs pipeline — structural violations like heading order, or dead links that slipped through unnoticed? The answer tends to tell you which tool to reach for first.

Top comments (0)