Documents

A markdown linter checks the promises the structure makes.

Generated documentation usually parses. What it fails at is shape: two level-one headings, a section repeated under a synonym, a link that points at a hash, an image with no description.

Why structure is the first thing to lint

Headings are navigation. A reader arrives, scans the headings and decides whether the page answers their question. Generated docs often produce a heading set that looks thorough and delivers nothing, because each heading restates the title instead of adding a section.

The mechanical failures underneath are easy to catch: no headings at all in a long document, no level-one heading, several level-one headings, levels that skip from two to four, and duplicate section titles.

Each of those breaks the exported table of contents, the anchor links other pages use, and the reader's sense of where they are.

The checks worth running

Linting a markdown document is a small set of structural rules applied without judgement.

  • Heading presence and hierarchy: at least one level-one heading, exactly one, and no skipped ranks.
  • Duplicate headings, including near-duplicates that produce the same anchor slug.
  • Block length: a paragraph past 220 words, which reads as a wall on a phone.
  • Placeholder links: href values of #, or bare example domains, which ship as broken.
  • Images without alt text, which fail both readers and accessibility audits.
  • Mixed date formats inside one document, which confuse readers and search engines.

Linting is not house style

A linter enforces correctness and accessibility, not preference. Whether you use sentence case, whether lists end with periods, whether emphasis is bold or italic, those belong to a style guide.

Keeping the two separate matters because style rules generate noise. A report full of preference findings trains people to skim, and the accessibility finding next to them gets missed.

Where a house rule is genuinely important, express it as a check with a reason, not as a preference somebody holds.

The generated-doc defects a linter cannot see

Whether the steps work. A procedure can have perfect heading structure and a missing prerequisite, and only someone following it will find that out.

Whether the code samples run. Fenced blocks are text to a linter; a sample with an invented import passes every structural rule.

Whether a section should exist. Generated docs often pad with a concept section that explains the heading above it, which is length rather than help.

Run it before the pull request

Paste the file into the console and check before opening the pull request, when fixing a heading rank is a keystroke rather than a review comment.

Run it again after review changes, since restructuring during review is where duplicate headings and broken anchors appear.

For documentation in a repository, pair it with a CI markdown check. The console catches the paste-in-editor case; the pipeline guard catches everything that arrives another way.

Where the doc needs a reader

Technical documentation published for customers should be read once by someone who can follow it, and signed when it covers setup, security or billing.

That reader starts from the lint report, which means their attention goes to the procedure rather than to the heading ranks.

For anything in a regulated context, the sign-off scope should name what was checked: reviewed the installation steps and the security note, for example.

Code fences: tags, truncation and truth

A fence without a language tag renders flat and tells a reader nothing about what they are looking at. Tag every block, even the ones where the language seems obvious to you today.

Truncation is the generated defect worth watching: a snippet that starts well and stops mid-expression, or one that omits the import it depends on. A linter will not notice, and the reader who pastes it will.

Tables in markdown are fragile

Markdown tables break on content that contains a pipe, on cells that need a paragraph, and on narrow screens where four columns become a horizontal scroll.

Check that every row has the same number of cells as the header, that the alignment row is present, and that nothing in the content contains an unescaped pipe. Where the content is not genuinely comparative, a list is more robust and easier to maintain.

Lists: parallel wording, and the three-item habit

Items in a list should share a grammatical shape, because a reader is scanning for the pattern rather than reading each line. When the shape changes halfway down, the scanning stops working.

Generated docs also default to three items, which is not a structure but a rhythm. If the fourth point matters, include it; if the third is filler, cut it.

Relative paths and the directory that moved

Relative links are what break in a documentation migration, and they break silently: the page renders, the link resolves to a 404 nobody notices until a reader complains.

Prefer root-relative or absolute paths for anything that crosses a directory, and use relative paths only for siblings in the same folder. When a section moves, grep for the old path rather than trusting the build.

Keep the source readable for the next editor

Markdown is read twice: once rendered, once as source in a pull request. A file with paragraphs on single enormous lines produces a diff nobody can review.

Keep one paragraph per line block, use blank lines between blocks, and break long lists into their own sections. It costs nothing and it is the difference between a reviewable change and a wall of red.

What this covers

  • Heading hierarchy

    One level-one heading, no skipped ranks, no missing levels.

  • Duplicate sections

    Identical and slug-colliding headings flagged before export.

  • Block length

    Paragraphs past 220 words surfaced for splitting.

  • Placeholder links

    Hash hrefs and example domains caught before publishing.

  • Images and dates

    Alt text present, date formats consistent across the document.

Questions this page usually raises

Is a markdown linter the same as a style checker?
No. It checks structure, accessibility and link integrity. House style is a separate rule set, and mixing the two produces reports nobody reads.
Why does heading order matter for docs?
Because exports, tables of contents and inbound anchors all depend on it. A skipped rank produces a document that renders and cannot be navigated.
Can it check that code samples work?
No. Samples are text to a structural check. Running them, or reviewing them, is a separate step that generated documentation needs more than most.
Does linting a file upload it?
No. Parsing happens in the browser, which matters for pre-release API documentation and client material.
Does it check HTML mixed into markdown?
Structural rules apply to the markdown; inline HTML is checked by the markup rules, including alt text and placeholder links.

Markdown linter

Run the checks, then decide who has to sign it.

The automated pass is free, needs no account and uploads nothing. Escalate to a named reviewer only where being wrong is expensive.