Most teams lint their code, their configs, and their SQL migrations. Prompts, which often decide more about product behavior than any single function, usually ship with no static checks at all. The first time anyone notices a broken template is when a model returns something strange in production.

A prompt linter does not need to be clever. A few dozen lines of deterministic checks, run in CI, catch a surprising share of real mistakes before any model is called.

What a prompt linter can check without a model

Prompts that live in templates have structure you can verify mechanically:

  • Unfilled or unknown placeholders. Parse every {variable} or {{variable}} and compare against the variables the calling code actually supplies. A typo such as {user_nmae} renders as literal text, and the model will often improvise around it without complaint.
  • Unescaped user content positions. Flag templates where untrusted fields are interpolated outside a clearly delimited block, since those are your injection-prone spots.
  • Token budget. Count the rendered size of the static portion and fail the build if it exceeds a threshold. This catches the slow creep where each fix adds one more paragraph.
  • Contradictory or duplicated instructions. Simple heuristics work: the same sentence appearing twice, or both "always" and "never" attached to the same keyword, are worth a warning.
  • Format promises versus parsers. If the prompt says "respond in JSON" and no schema or parser is configured, or the reverse, flag the mismatch.

Make it a warning system, not a gate on taste

The mistake is to turn style opinions into build failures. Reserve errors for things that are objectively broken, such as undefined variables or a missing output format spec. Make everything heuristic a warning with a short explanation, and allow an inline suppression comment with a required reason so exceptions are visible in review.

Wire it into the workflow people already use

Run the linter as a pre-commit hook and in CI against changed prompt files only. Output should name the file, the line, and the rule, the same as any other linter, so it fits existing editor integrations. Keep rules as small independent functions so adding one after an incident takes ten minutes. Each production incident traced to a prompt defect is a candidate rule.

What it will not do

A linter tells you the prompt is well-formed. It does not tell you the prompt is good. You still need evals for quality, and a linter is no substitute for them. Its value is that it removes a whole class of silly failures from the eval signal, so when an eval regresses you know it is about behavior and not a misspelled variable.

The takeaway: treat prompts as source files. Cheap static checks on structure, size, and placeholders pay for themselves the first time they stop a broken template from reaching users.