Automated Documentation Linting: Implementing Vale with Google and Microsoft Style Guides

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:

  1. 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!

  2. 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.

  3. 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.

  4. 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'

  • ###CODEBLOCKPLACEHOLDER8###yaml
    extends: existence
    message: "Image alt text must not be empty or generic like 'image' or 'screenshot'."
    level: error
    scope: raw
    tokens:
  • '!\[\s*\]\('

  • '!\[(?:image|screenshot|photo|diagram)\]\('

  • ###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:
  • repo: https://github.com/errata-ai/vale

  • rev: v3.4.0
    hooks:
  • id: vale

  • 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:

  • name: Checkout Code

  • uses: actions/checkout@v4
  • name: Run Vale Action

uses: errata-ai/vale-action@v2.1.1
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:

LinterEngine & SpeedExtensibilitySyntax AwarenessBest Fit
ValeGo (Extremely fast, compiled binary)High (YAML rules, regex, AST tokens)Markdown, AsciiDoc, RST, HTML, code block exclusionEnterprise Docs-as-Code pipelines, multi-repo CI/CD
proselintPython (Moderate speed)Low (Fixed python heuristics)Plain text, basic markdownCasual essay writing, blog drafts
write-goodNode.js (Moderate speed)Low (Naive regex pattern matching)Flags code blocks accidentallyQuick CLI sanity check on plain text
textlint**Node.js (Moderate speed)High (JavaScript/TypeScript plugins)Markdown, HTMLTeams already deeply invested in the Node ecosystem
Vale's ability to parse the Abstract Syntax Tree (AST) rather than treating documents as flat strings makes it the uncontested standard for technical documentation engineering.

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.

NA

Written by Nuhman Areekode

Technical Writer & Cloud Documentation Specialist. Focused on documenting distributed systems, OpenAPI specifications, and Docs-as-Code workflows.

Related Technical Guides