Linting Your Prose, Not Just Your Markdown: A Practical Vale Setup
A Markdown file can pass every syntax check — clean headings, valid links, consistent list indentation — and still read like a rough draft. That's because markdown linting and prose linting check different things: one verifies structure, the other verifies the words themselves. Here's a practical setup for the second kind, using Vale.
Quick answer
Vale is a prose linter. It checks writing style and usage against a style guide, not Markdown syntax. A working setup has three pieces: a .vale.ini file at the repo root, an installed style package instead of a blank rule set, and a way to run it beyond "whenever someone remembers." Start loose. Tighten later.
Step-by-step
1. Know what you're actually adding
A Markdown linter checks that headings nest correctly, links resolve, and lists are indented consistently. It has nothing to say about word choice or tone. Vale fills that gap. In one real-world example, it flags "Our goal is..." for first-person-plural style violations, "let's assume" for preferring a contraction, and a custom rule catches "web-site" where the house style wants "website" — three things a structural linter would pass without comment. Running Vale doesn't make your Markdown linter redundant, and the reverse is also true.
2. Write a minimal .vale.ini
Four keys do most of the work in a `.vale.ini`: StylesPath points to the folder holding style rules, MinAlertLevel sets the severity threshold Vale reports (suggestion, warning, or error), Packages lists what gets installed, and BasedOnStyles names which rule sets actually run. File-glob sections like [*.md] scope rules to specific extensions or directories. A minimal file looks like this:
StylesPath = styles
MinAlertLevel = suggestion
Packages = Microsoft
[*.md]
BasedOnStyles = Vale, MicrosoftOne gotcha worth knowing before you add a second section: a later matching [glob] section replaces `BasedOnStyles` instead of adding to it. Scope a style override to *.fr.md and forget to repeat the full style list there, and you'll silently lose rules you meant to keep.
3. Install a style package instead of starting from scratch
A blank StylesPath produces zero signal, so don't write rules from nothing. `vale sync` downloads whatever a package names — Google, Microsoft, and Red Hat's developer style guides are common starting points, and the Vale Package Explorer lets you preview them before committing to one. Install a package, run it unmodified for a week or two, and see what actually fires before touching a single custom rule.
4. Write one custom rule
Once the stock package has settled, a custom rule is just a plain YAML file with a scope, a severity level, and an extension point. The simplest kind bans a term outright:
extends: existence
message: "Use 'website' instead of '%s'."
level: warningThat `extends: existence` pattern is the same shape Datadog uses in its public style package. A sentence-length rule works the same way — count the words in a sentence and warn past a threshold — but start that threshold loose. One team began at 45 words per sentence and brought it down to 40 only after confirming the rule wasn't flagging normal writing.
5. Run it in CI, not just an editor
An editor extension that lints on save catches issues while you're writing, but it only helps the person who remembered to install it. The sturdier version runs Vale in GitHub Actions on every pull request, the same way a Git-based publishing workflow already gates other checks before merge. When a flagged line is a deliberate exception rather than a mistake, an inline comment — <!-- vale SomeRule = NO --> … <!-- vale SomeRule = YES --> — disables it for that one block, instead of turning Vale off for the whole file.
Common problems and fixes
Every pull request gets blocked on day one. Usually MinAlertLevel or BasedOnStyles was left too strict from the start. Treat Vale's first output as something to review, not a gate to satisfy — loosen the threshold, note which rules are firing on legitimate writing, and tighten only the ones that hold up.
Rules you expected to run silently don't. Check whether a later [glob] section in .vale.ini replaced BasedOnStyles instead of extending it. This is the single most common reason a rule "disappears" for a subset of files.
Markdown linting already passes, so why is Vale still flagging things? That's expected, not a bug — they check different layers. A file can have perfect heading structure and list formatting and still read inconsistently if nobody's enforced terminology or tone. Keep both checks; neither replaces the other. The same logic applies to a documented house style guide or a project's CONTRIBUTING.md — Vale turns that style guide into something a machine checks, instead of something only a reviewer remembers to enforce.
Doing This with Carets
None of this setup lives in Carets — Vale runs against a repo in CI, not inside a notes app. But the drafting that happens before a pull request does. Carets keeps your Markdown as plain, portable text, organized into files, projects, and tags, so a style-guide draft, a .vale.ini sketch, and a custom-rule YAML snippet can sit side by side without leaving plain text or switching apps. It's native and fast on iPhone, iPad, and Mac, with syntax highlighting for the YAML and config files this kind of setup involves — handy for drafting a rule on an iPad before testing it against a real repo later. Carets is free on the App Store for iPhone, iPad, and Mac.
Frequently Asked Questions
Does Vale replace a Markdown linter like markdownlint?
No — they check different things. A Markdown linter verifies structure: heading order, link syntax, list formatting. Vale checks the words themselves: passive voice, banned terms, sentence length, terminology consistency. Most documentation setups run both, since neither one catches what the other is built for.
What goes in a .vale.ini file?
Four keys do most of the work: StylesPath points to where style rules live, MinAlertLevel sets the severity threshold Vale reports (suggestion, warning, or error), Packages lists what vale sync downloads, and BasedOnStyles names which style rule sets actually run against matched files. File-glob sections like [*.md] scope rules to specific extensions or directories.
Do I need to write my own Vale rules to get started?
No. Start by installing an existing style package — Google, Microsoft, or Red Hat's developer style guides are common defaults — and running it as-is for a week or two. Writing custom rules, like banned terms or a sentence-length cap, makes more sense once you know which of the stock rules are actually useful for your docs.
Will Vale block every pull request with false positives?
Only if MinAlertLevel and BasedOnStyles are left too strict from day one. Teams that roll Vale out successfully start with a loose threshold, treat its output as a first-pass suggestion rather than a hard gate, and tighten specific rules — like a sentence-length limit — only after confirming they're not just noise.