Skip to content

Linting Documentation with Vale and markdownlint

DodaTech Updated 2026-06-28 4 min read

Documentation linting automates style and formatting checks. Learn how to configure Vale for prose style enforcement and markdownlint for Markdown formatting rules in your docs pipeline.

What You'll Learn

You will learn how to lint documentation for prose style and Markdown formatting, how to configure Vale with style guides, and how to integrate both tools into your CI pipeline.

Why It Matters

Manual proofreading is slow and inconsistent. Automated linting catches style violations, formatting errors, and terminology issues on every commit. It enforces consistency across all contributors without a human reviewer checking every detail.

Real-World Use

DodaTech uses Vale with the Google Developer Documentation Style Guide and markdownlint with project-specific rules. Every PR that fails linting is blocked from merging until issues are resolved.

flowchart LR
  A[Markdown File] --> B[Vale: Prose Linter]
  A --> C[markdownlint: Formatting Linter]
  B --> D[Style Violations]
  C --> E[Formatting Errors]
  D --> F[CI Blocks PR]
  E --> F
  F --> G[Writer Fixes Issues]
  G --> A
  A:::current
  classDef current fill:#f90,color:#fff,stroke:#333,stroke-width:2px

Configuring Vale

Vale is a prose linter that enforces style rules. Create a .vale.ini file in your project root:

# .vale.ini
StylesPath = .vale/styles

[formats]
markdown = md

[*]
BasedOnStyles = Vale, Google, write-good

[*.md]
Vale.Spelling = YES
Google.WordList = YES
write-good.E-Prime = NO

Install Vale and a style guide:

# Install Vale
brew install vale  # macOS
# or: sudo snap install vale  # Linux

# Download the Google style guide
mkdir -p .vale/styles
git clone https://github.com/errata-ai/google.git .vale/styles/Google

Running Vale

vale content/api/authentication.md

Expected output:

 content/api/authentication.md
 1:1  warning  'login' is informal. Use 'sign in'.  Google.WordList
 45:12  warning  Use 'can' instead of 'may'  Google.CanMay
 89:5  error  'Utilize' is unnecessarily complex. Use 'use'.  write-good.ComplexWords

Configuring markdownlint

markdownlint enforces Markdown formatting rules. Create a .markdownlint.json file:

{
  "default": true,
  "MD013": false,
  "MD024": false,
  "MD033": false,
  "MD041": false,
  "MD046": false
}

Running markdownlint

markdownlint content/

Expected output:

content/getting-started.md: 1:1 MD041/first-line-heading
First line in a file should be a top-level heading
content/installation.md: 25 MD009/no-trailing-spaces
Trailing spaces (line 25)

Creating Custom Vale Rules

# .vale/styles/DodaTech/Terminology.yml
extends: substitution
message: "Use '%s' instead of '%s'"
level: error
swap:
  "click on": "select"
  "click here": "visit"
  "login": "sign in"
  "utilize": "use"
  "please": (remove)
  "login": "sign in"
  "setup" (noun): "set up" (verb)

Integrating Linters into CI

- name: Lint documentation
  run: |
    markdownlint-cli2 content/
    vale content/

Both tools return non-zero exit codes when they find violations, causing the CI pipeline to fail.

Common Mistakes

1. Ignoring Linting Errors

Linting errors that are always bypassed become noise. Either fix the rule or disable it. Never leave errors in the output that no one acts on.

2. Using Too Many Rules

Starting with 200+ rules overwhelms writers. Begin with 10-20 essential rules and add more as the team adapts.

3. Not Customizing Rules

Default rule sets may conflict with your style guide. Customize Vale rules to match your terminology and tone.

4. Running Linters Only Locally

Linters run locally catch nothing when writers forget to run them. Always enforce linting in CI.

5. Not Documenting the Rules

Writers need to know what rules exist and why. Add a section to your contributing guide explaining the linting configuration.

Practice Questions

1. What is the difference between Vale and markdownlint?

Vale lints prose style (word choice, tone, terminology). markdownlint lints Markdown formatting (heading structure, spacing, list syntax).

2. How does Vale know which rules to apply?

Vale reads a .vale.ini configuration file that specifies which style packages to use and which file formats to lint.

3. What happens when a linter finds a violation in CI?

The linter returns a non-zero exit code, which causes the CI pipeline to fail and blocks the Pull Request from merging.

4. Can you disable a linting rule for a specific line?

Yes. Vale supports inline comments: <!-- vale off --> and <!-- vale on -->. markdownlint supports <!-- markdownlint-disable -->.

5. Challenge: Create a custom Vale style for your project with five terminology substitutions. Configure markdownlint with a custom rule set. Create a GitHub Actions workflow that runs both linters on every PR.

FAQ

Can Vale enforce brand-specific terminology?

Yes. Create a custom substitution rule that flags incorrect terms and suggests the correct brand language.

How do I add a new word to Vale's dictionary?

Add it to a custom vocabulary file in your styles directory. Vale checks spelling against configured dictionaries.

Does markdownlint fix issues automatically?

Yes, with the --fix flag. Use this carefully — automated fixes may not always produce the desired result.

Can I run linters on non-Markdown files?

Yes. Vale supports AsciiDoc, reStructuredText, HTML, and plain text. markdownlint supports Markdown formats.

How do I lint documentation written in AsciiDoc?

Vale supports AsciiDoc natively. markdownlint does not apply to AsciiDoc, but you can use AsciiDoc-specific linters.

Mini Project

Set up a documentation Repository with a .vale.ini configuration using the Google style guide, a .markdownlint.json with 10 custom rules, and a CI workflow that runs both linters on every pull request.

What's Next

Linting covers style and formatting. Next, learn about Spell Check to catch typos and misspellings. Then explore Link Checking to prevent broken references.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro