In software engineering, no pull request merges without passing automated linters like ESLint, Ruff, or Go Vet. Linters catch syntax errors, enforce formatting consistency, and guarantee architectural patterns without requiring human engineers to spend review cycles debating semicolon usage.
For years, editorial review in technical documentation lacked this automated safety net. Technical editors spent hours manually flagging passive voice, catching uncapitalized brand names, reminding contributors not to use jargon like "easy" or "simply", and correcting inconsistent heading capitalization across hundreds of documentation pull requests.
Vale is an open-source, extensible linter for prose designed specifically for technical writers and Docs-as-Code workflows. By executing syntax-aware checks directly against Markdown, AsciiDoc, and HTML Abstract Syntax Trees, Vale brings deterministic software testing discipline to English prose. In this comprehensive guide, we examine how to configure Vale, integrate industry-standard style guides, and author custom editorial rules.
###CODEBLOCKPLACEHOLDER0###
1. Why Vale Stands Out for Technical Documentation
Unlike generic spellcheckers or consumer grammar tools, Vale was designed from the ground up for technical documentation:
- Syntax-Aware Parsing: Vale parses Markdown, AsciiDoc, reStructuredText, and HTML by building an Abstract Syntax Tree (AST). It understands code blocks, inline backticks, frontmatter metadata, and HTML comments, so it never flags valid programming syntax as spelling errors!
- Completely Local & Private: Vale runs natively on your machine or inside private CI runners. No proprietary documentation drafts are ever sent to third-party cloud servers.
- Pluggable Style Packages: You can import established open-source style guides (Google Developer Documentation Style Guide, Microsoft Writing Style Guide, Red Hat Style Guide) with a single line of configuration.
- Custom Extensibility in YAML: Anyone can write custom rules using regex patterns, word substitution dictionaries, or sentence complexity thresholds without writing compiled code.
2. Setting Up .vale.ini Configuration
Create a .vale.ini file in the root of your documentation repository:
###CODEBLOCKPLACEHOLDER1###
Installing Configured Packages
Once.vale.ini is created, run the Vale package manager command:
###CODEBLOCKPLACEHOLDER2###
Vale downloads the specified Google and Microsoft style packages directly into your .vale/styles folder.
3. Writing Custom Editorial Rules in YAML
Every company has unique branding guidelines, prohibited terminology, and preferred phrasing. With Vale, you can encode these requirements into clean YAML rule files.
Example 1: Prohibiting Condescending Words (Vale/CondescendingWords.yml)
Technical writing should be objective and empathetic. Words like "obviously", "simply", and "just" alienate beginners when they encounter unexpected friction:
###CODEBLOCKPLACEHOLDER3###
Example 2: Enforcing Standard Brand Spelling (Vale/BrandNames.yml)
Enforce correct capitalization for internal products and technology trademarks:
###CODEBLOCKPLACEHOLDER4###
Example 3: Enforcing Sentence Case in Headings (Vale/HeadingCase.yml)
Google Developer Style Guide recommends sentence case for all headings (capitalizing only the first word and proper nouns):
###CODEBLOCKPLACEHOLDER5###
Example 4: Enforcing Oxford Commas (Vale/OxfordComma.yml)
Enforce serial commas in lists of three or more items:
###CODEBLOCKPLACEHOLDER6###
Example 5: Requiring Language Identifiers on Code Fences (Vale/CodeFenceLanguage.yml)
Fenced code blocks without language tags cause syntax highlight renderers to fail:
###CODEBLOCKPLACEHOLDER7###python' instead of '```')."
level: error
scope: raw
tokens:
- '```\s*\n'
- '!\[\s*\]\('
- '!\[(?:image|screenshot|photo|diagram)\]\('
- repo: https://github.com/errata-ai/vale
- id: vale
- name: Checkout Code
- name: Run Vale Action
###CODEBLOCKPLACEHOLDER8###yaml
extends: existence
message: "Image alt text must not be empty or generic like 'image' or 'screenshot'."
level: error
scope: raw
tokens:
###CODEBLOCKPLACEHOLDER9###yaml
.vale/styles/Metrics/FleschKincaid.yml
extends: metric
message: "Grade level (%s) exceeds maximum threshold of 10. Simplify sentence structure."
level: warning
formula: |
0.39 (words / sentences) + 11.8 (syllables / words) - 15.59
condition: "> 10"
###CODEBLOCKPLACEHOLDER10###yaml
.pre-commit-config.yaml
repos:
rev: v3.4.0
hooks:
files: '\.md$'
args: ['--minAlertLevel=warning']
###CODEBLOCKPLACEHOLDER11###bash
Output lint results in structured JSON format
vale docs/ --output=JSON > vale_report.json
###CODEBLOCKPLACEHOLDER12###python
import json
with open("vale_report.json") as f:
report = json.load(f)
total_warnings = sum(
1 for alerts in report.values() for a in alerts if a["Severity"] == "warning"
)
total_errors = sum(
1 for alerts in report.values() for a in alerts if a["Severity"] == "error"
)
print(f"Documentation Health Score: {totalerrors} errors, {totalwarnings} warnings.")
###CODEBLOCKPLACEHOLDER13###
.vale/
└── styles/
└── config/
└── vocabularies/
└── TechWriter/
├── accept.txt # Whitelisted domain terms (case-sensitive)
└── reject.txt # Explicitly forbidden terms
###CODEBLOCKPLACEHOLDER14###
Kubernetes
etcd
Kubelet
gRPC
Protobuf
OAuth2
JWT
OpenAPI
PostgreSQL
Webhook
###CODEBLOCKPLACEHOLDER15###bash
Lint an entire documentation directory
vale docs/
Lint a specific file and filter for errors only
Lint only markdown files modified in the current branch
vale --glob='docs//*.md' $(git diff --name-only origin/main...HEAD)Filter for specific style errors
vale docs/ --filter='Action.Severity == "error"' ###CODEBLOCKPLACEHOLDER16### docs/api/authentication.md 14:5 warning Avoid condescending word 'simply'. State facts plainly. Vale.CondescendingWords 28:19 error Use standard capitalization 'GitHub' instead of 'Github'. Vale.BrandNames 45:12 warning 'In order to' is wordy. Consider using 'To'. Google.Wordiness ###CODEBLOCKPLACEHOLDER17###yaml name: Vale Review Bot on: [pull_request]jobs:
vale-lint:
runs-on: ubuntu-latest
steps:
uses: actions/checkout@v4
with:
files: 'docs'
vale_flags: '--minAlertLevel=suggestion'
env:
GITHUBTOKEN: ${{ secrets.GITHUBTOKEN }}
```
10. Prose Linter Architectural Comparison
When evaluating prose linters for technical documentation teams, understand how Vale compares to alternative open-source tools:
| Linter | Engine & Speed | Extensibility | Syntax Awareness | Best Fit |
|---|---|---|---|---|
| Vale | Go (Extremely fast, compiled binary) | High (YAML rules, regex, AST tokens) | Markdown, AsciiDoc, RST, HTML, code block exclusion | Enterprise Docs-as-Code pipelines, multi-repo CI/CD |
| proselint | Python (Moderate speed) | Low (Fixed python heuristics) | Plain text, basic markdown | Casual essay writing, blog drafts |
| write-good | Node.js (Moderate speed) | Low (Naive regex pattern matching) | Flags code blocks accidentally | Quick CLI sanity check on plain text |
| textlint** | Node.js (Moderate speed) | High (JavaScript/TypeScript plugins) | Markdown, HTML | Teams already deeply invested in the Node ecosystem |
Automating prose linting with Vale removes subjective friction from editorial reviews, guarantees consistency across dozens of distributed contributors, and elevates the professional quality of your documentation.