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
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-checkas a second pass
Try gomarklint
go install github.com/shinagawa-web/gomarklint@latest
gomarklint init
gomarklint .
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)