Drift is when your documentation says one thing but your code does another. Drift diffs JSDoc, examples, markdown, and option tables against the real API spec (TypeScript via Openpkg, or OpenAPI / Clarity via adapters).
drift failed.Signature and type mismatches between JSDoc and code.
| Drift Type | Description |
|---|---|
param-mismatch | @param name doesn't match any parameter in the signature |
param-type-mismatch | @param type doesn't match actual parameter type |
return-type-mismatch | @returns type doesn't match actual return type |
optionality-mismatch | @param marks a required param as optional or vice versa |
generic-constraint-mismatch | @template constraint doesn't match actual generic constraint |
property-type-drift | Documented property type doesn't match actual type |
async-mismatch | JSDoc says sync but function is async, or vice versa |
Metadata and visibility mismatches.
| Drift Type | Description |
|---|---|
deprecated-mismatch | @deprecated tag present but export isn't deprecated, or vice versa |
visibility-mismatch | JSDoc visibility (@internal, @private, etc.) conflicts with code visibility |
broken-link | {@link SomeExport} in JSDoc references a non-existent export |
Issues with @example code blocks.
| Drift Type | Description |
|---|---|
example-drift | Example imports or references non-existent exports |
example-syntax-error | Example has syntax errors |
Broken references in markdown documentation.
| Drift Type | Description |
|---|---|
prose-broken-reference | Markdown code block imports a name that doesn't exist in package exports |
prose-unresolved-member | Markdown code block calls a method that doesn't exist on any exported type |
prose-deprecated-reference | Markdown references a deprecated export/member with no deprecation note nearby |
driftdrift runs drift detection and reports issues:
drift
Output includes file path and line number for each issue:
3 issues found
parseConfig @param 'options' type mismatch: documented as 'object', actual 'ParseOptions'
src/config.ts:42
createClient @returns type mismatch: documented as 'Client', actual 'Promise<Client>'
src/client.ts:15
README.md:28 Import 'formatJSON' from 'my-lib' does not exist in package exports
Did you mean 'formatJson'?
JSON output:
{
"ok": true,
"data": {
"lint": {
"count": 2,
"issues": [
{
"export": "parseConfig",
"issue": "@param 'options' type mismatch: documented as 'object', actual 'ParseOptions'",
"filePath": "src/config.ts",
"line": 42
}
]
},
"pass": false
},
"meta": { "command": "scan", "duration": 450, "version": "1.15.1" },
"next": { "suggested": "drift get <name>", "reason": "2 issues found" }
}
Exit 1 when issues are found. See CLI Reference.
Prose drift scans your markdown files for code blocks that import from your package. If an imported name doesn't exist in the package's exports, it's flagged as prose-broken-reference. Method calls in those code blocks are also checked: if a called method doesn't exist on any exported type, it's flagged as prose-unresolved-member. References to APIs the spec marks deprecated are flagged as prose-deprecated-reference — unless the surrounding prose (±5 lines) already acknowledges the deprecation. The check is on the resolved reference, never a bare name: z.url() is the export url, not the deprecated method ZodString.url; a member counts only when its receiver is bound to the type (const s = z.string(); s.url(), or the chain z.string().url()). A name an earlier code block of the page declared at its top level (const useStore = create(...)) is the reader's own in later blocks, not the export of the same name: it is not flagged as deprecated and a variable assigned from it is not typed by the export, until a block imports that name from the package again. A printed signature (function f(a: T): R with no body) is not such a declaration.
Drift includes fuzzy matching -- if you import formatJSON but the actual export is formatJson, the suggestion will say "Did you mean 'formatJson'?". Same for member calls, among the members of the receiver's own type.
By default, drift scans:
README.mddocs/**/*.mddocs/**/*.mdxAnd excludes:
node_modules/**dist/**.git/**Override with the docs config key:
{
"docs": {
"include": ["README.md", "docs/**/*.md", "guides/**/*.md"],
"exclude": ["node_modules/**", "dist/**"]
}
}
See Configuration for config file locations.
Override at the command line instead with --docs <patterns...> (globs or directories) — useful for pointing at a hosted docs site pulled down locally, without touching the config file. Runs for any language when passed explicitly, not just TypeScript:
drift --docs guides/**/*.md
drift --all
drift --all --private
Batch output shows per-package issue counts:
{
"packages": [
{ "name": "@scope/core", "exports": 45, "issues": 3 },
{ "name": "@scope/utils", "exports": 12, "issues": 0 }
],
"aggregate": { "count": 3 }
}