TL;DR: A documentation prompt generator is a set of templates, one per artefact, because a docstring, a README, an ADR and a runbook obey different rules. Below: thirty copy-paste prompts across Python, TypeScript, JavaScript, Java, Go and Rust, with tag names verified against each convention's own specification.
Here is the problem with asking a model to document your code. It can read the source, so it can tell you what the code does. That is the least valuable documentation there is, because the reader can get it from the code. What a reader actually needs is why it works this way, what it must not do, what breaks if you change it, and which alternative was rejected. None of that is in the file.
So the interesting part of a documentation prompt is not the instruction to summarise. It is the slot where your knowledge goes, and the instruction that stops the model filling that slot with a guess.
What can a documentation prompt produce from source alone?
Two things, reliably: the calling contract, and observable behaviour. Everything else is inference.
From the source a model can read parameter names and types, the return shape, exceptions raised or errors returned on paths it can trace, obvious preconditions such as a null check at the top, and the call sites inside the same file. It cannot read your reasons.
Google's Python style guide draws the boundary in one sentence: "A docstring should give enough information to write a call to the function without reading the function’s code." That is a contract, not a summary. And the guide is explicit that implementation details which do not affect the caller are "better expressed as comments alongside the code than within the function’s docstring" — a different artefact, with a different prompt.
The rustdoc book makes the same point from the other side. Because Rust's type system already declares what a function takes and returns, "there is no benefit of explicitly writing it into the documentation". A docstring that restates the signature in English has added a maintenance burden and no information.
The difference is easy to see side by side. Here is the output of an unguided prompt on a cache invalidation helper:
def invalidate(user_id: str, scopes: list[str], force: bool = False) -> int:
"""Invalidate cache entries.
Args:
user_id: The user ID.
scopes: A list of scopes.
force: Whether to force. Defaults to False.
Returns:
An integer.
"""
Every line of that is derivable from the signature, so the block has told a reader nothing and now has to be kept in sync forever. Here is the same function documented with the author's knowledge supplied:
def invalidate(user_id: str, scopes: list[str], force: bool = False) -> int:
"""Drop this user's cached entries for the given scopes.
Scopes are dropped one at a time rather than in a batch, because the
upstream store applies its own per-key rate limit and a batch delete
silently drops the tail. Do not "optimise" this into a single call.
Args:
user_id: Owner of the entries. Entries for other users are never
touched, even if a scope is shared.
scopes: Scope names to drop. An empty list is a no-op, not a
wildcard — passing [] to clear everything is a common bug.
force: Skip the last-write-wins check. Only safe during migration,
when no writer is running.
Returns:
Number of entries actually removed, which may be lower than
len(scopes) if an entry had already expired.
"""
Nothing in the second version could have been read from the source. The prompt's job is to make room for it and refuse to counterfeit it.
Every template below therefore carries the same two mechanisms. The first is a context block you fill in before sending:
CONTEXT I AM SUPPLYING — treat as ground truth, do not infer, do not invent:
- Why it works this way:
- What it must NOT do:
- What breaks if this changes:
- Alternative we rejected, and why:
- Known callers that depend on current behaviour:
(Leave a line blank and I will accept the omission. Do not fill it yourself.)
The second is an inference marker. If the model cannot see something, it must say so in the output rather than write around it:
Document only what you can read in the source I gave you.
Where you must infer, prefix that sentence with [INFERRED] and name the
evidence you inferred it from. Do not remove the marker in later passes.
If a fact is neither readable nor supplied, write [UNKNOWN — needs author]
instead of a plausible sentence.
Paste both into any prompt on this page. They are the reason these produce something a reviewer can trust.
Which convention should each template follow?
Name the convention in the prompt, and name only one. Where a language has two live conventions, blending them yields output that no documentation tool parses cleanly, and reviewers stop noticing the difference.
| Feature | Google style | NumPy style | Sphinx reST |
|---|---|---|---|
| Parameters | Args: | Parameters (hyphen underline) | :param name: |
| Return value | Returns: | Returns (hyphen underline) | :returns: and :rtype: |
| Generators | Yields: | Yields | No dedicated field |
| Exceptions | Raises: | Raises | :raises ValueError: |
| Types | Only if no annotation | After the colon; always for returns | :type name: |
| Class attributes | Attributes: | Attributes | :ivar name: |
The numpydoc style guide is specific about the mechanics: sections are separated by headings and "Each heading should be underlined in hyphens", with the ordering fixed. Google style instead uses a heading line ending in a colon and a hanging indent. Sphinx's Python domain recognises info field lists inline, and accepts param, parameter, arg, argument, key and keyword as synonyms, along with returns/return, rtype, and raises/raise/except/exception.
The same fork exists in TypeScript. JSDoc puts the type in curly braces: @param {string} somebody. TSDoc omits the type — TypeScript already has it — and requires a hyphen before the description: @param x - The first input number. TSDoc exists, in its own words, "so that different tools can extract content without getting confused by each other's markup". Note also that TSDoc classifies @throws as an Extended tag rather than a Core one, and suggests each exception type gets its own @throws block.
The practical cost of blending them is that neither renderer wins. A NumPy-style Parameters heading dropped into a Google-style file is not an error anyone sees at build time; the section simply fails to be recognised and renders as a paragraph of loose text in the generated docs, usually noticed months later by a reader rather than by the author. Naming the convention in the prompt costs one line and removes the whole class.
Java has one convention and a modern wrinkle. @param, @return (singular, methods only) and @throws are the block tags; the JDK spec also defines an inline {@return description} form which supplies the first sentence and the Returns section at once. Go and Rust have no tag vocabulary at all — Go doc comments are complete sentences beginning with the symbol name, and rustdoc uses CommonMark with conventional # Panics, # Errors and # Safety headings.
| Language | Convention | Primary specification | Verified |
|---|---|---|---|
| Python | Google style | google.github.io/styleguide/pyguide.html §3.8 | 28 Aug 2026 |
| Python | NumPy style | numpydoc.readthedocs.io/en/latest/format.html | 28 Aug 2026 |
| Python | Sphinx reST fields | sphinx-doc.org Python domain, info field lists | 28 Aug 2026 |
| Python | Baseline formatting | PEP 257 | 28 Aug 2026 |
| JavaScript | JSDoc | jsdoc.app tag reference | 28 Aug 2026 |
| TypeScript | TSDoc | tsdoc.org tag reference | 28 Aug 2026 |
| Java | Javadoc | JDK 25 doc-comment spec, Oracle | 28 Aug 2026 |
| Go | Go doc comments | go.dev/doc/comment | 28 Aug 2026 |
| Rust | rustdoc | rustdoc book + Rust API Guidelines | 28 Aug 2026 |
How do you prompt for a docstring that is not just the signature in prose?
By naming the convention, restricting the model to what it can see, and giving it somewhere to put your knowledge. Eight function-level templates follow, one per convention.
1. Python, Google style
Write a docstring for the function below, following the Google Python Style
Guide section 3.8. Use the section headings Args:, Returns: (or Yields: for a
generator), and Raises:, each ending in a colon, with a hanging indent of four
spaces. Omit a section that does not apply. Do not repeat a type that is already
in the annotation. Do not document exceptions raised when the documented API is
itself violated.
Open with one imperative or descriptive summary line, consistent with the rest of
the file (I will tell you which if it matters). Then a blank line, then any
extended description, then the sections.
Document only what you can read. Prefix any inference with [INFERRED] and name
the evidence. Write [UNKNOWN — needs author] rather than guessing.
CONTEXT I AM SUPPLYING — ground truth, do not infer:
- Why it works this way:
- What it must NOT do:
- What breaks if this changes:
- Rejected alternative:
FUNCTION:
<paste>
2. Python, NumPy style
Write a numpydoc-style docstring for the function below, following the numpydoc
style guide. Use the section order: short summary, extended summary, Parameters,
Returns, Raises, See Also, Notes, Examples. Underline every section heading with
hyphens matching the heading length. In Parameters, put a space before the colon
and give the type after it; mark optional arguments with ", optional". Enclose
parameter names in single backticks when referring to them in prose. Omit any
section that does not apply.
Do not put implementation detail or background theory in the extended summary —
that belongs in Notes.
Document only what you can read. Prefix any inference with [INFERRED]. Write
[UNKNOWN — needs author] rather than guessing.
CONTEXT I AM SUPPLYING — ground truth, do not infer:
- Why it works this way:
- What it must NOT do:
- What breaks if this changes:
FUNCTION:
<paste>
3. Python, Sphinx reStructuredText fields
Write a docstring for the function below using Sphinx info field lists, as
recognised by the Sphinx Python domain. Use :param name: for each argument,
:type name: only where there is no annotation to read, :returns: for the return
value, :rtype: only where there is no annotation, and one :raises ExceptionType:
line per exception. Keep the one-line summary above the field list, separated by
a blank line. Do not mix in Google or NumPy section headings.
Document only what you can read. Prefix any inference with [INFERRED]. Write
[UNKNOWN — needs author] rather than guessing.
CONTEXT I AM SUPPLYING — ground truth, do not infer:
- Why it works this way:
- What it must NOT do:
- What breaks if this changes:
FUNCTION:
<paste>
4. JavaScript, JSDoc
Write a JSDoc comment for the function below, following jsdoc.app. Use
@param {Type} name - description for each parameter, with the type in curly
braces and a hyphen before the description. Document object properties with
additional dotted @param entries such as @param {string} options.retries.
Use @returns with a braced type, @yields instead of @returns for a generator,
and one @throws {ErrorType} line per error the function can throw.
Add @example only if you can write one from the code without inventing an API.
Document only what you can read. Prefix any inference with [INFERRED]. Write
[UNKNOWN — needs author] rather than guessing.
CONTEXT I AM SUPPLYING — ground truth, do not infer:
- Why it works this way:
- What it must NOT do:
- What breaks if this changes:
FUNCTION:
<paste>
5. TypeScript, TSDoc
Write a TSDoc comment for the function below, following tsdoc.org. Do NOT put
types in curly braces — TypeScript already declares them. Use
@param name - description with a hyphen. Keep the first paragraph as the summary
and start the detail with @remarks. Use @returns for the return value,
@typeParam for generics, and a separate @throws block per exception type, each
starting with a line containing only the exception name. Use @defaultValue where
a default exists and @deprecated where it applies.
Document only what you can read. Prefix any inference with [INFERRED]. Write
[UNKNOWN — needs author] rather than guessing.
CONTEXT I AM SUPPLYING — ground truth, do not infer:
- Why it works this way:
- What it must NOT do:
- What breaks if this changes:
FUNCTION:
<paste>
6. Java, Javadoc
Write a Javadoc comment for the method below. Order the tags: @param (one per
argument, in declaration order), @return, @throws (alphabetical by exception
name), @see, @since, @deprecated. Use angle brackets around type parameter names,
as @param <T>. Omit @return on a void method. Prefer the inline {@return ...}
form only if the summary would otherwise repeat it.
Write the first sentence as a complete summary — it is what appears in the
index. Do not restate the signature.
Document only what you can read. Prefix any inference with [INFERRED]. Write
[UNKNOWN — needs author] rather than guessing.
CONTEXT I AM SUPPLYING — ground truth, do not infer:
- Why it works this way:
- What it must NOT do:
- Thread-safety guarantees I am promising:
METHOD:
<paste>
7. Go, doc comments
Write a Go doc comment for the declaration below, following go.dev/doc/comment.
Start with a complete sentence whose subject is the symbol name itself. There are
no tags in Go — write prose. For a boolean-returning function use the phrase
"reports whether". Refer to named parameters and results directly, without
backquotes. If the function is safe or unsafe for concurrent use in a way a
caller would not assume, say so. If it is deprecated, add a paragraph starting
exactly with "Deprecated: " and name the replacement.
Document only what you can read. Prefix any inference with [INFERRED]. Write
[UNKNOWN — needs author] rather than guessing.
CONTEXT I AM SUPPLYING — ground truth, do not infer:
- Why it works this way:
- What it must NOT do:
- Concurrency guarantee:
DECLARATION:
<paste>
8. Rust, rustdoc
Write a rustdoc comment for the item below using /// outer doc comments and
CommonMark. Structure: one summary line, blank line, detail, then a fenced
Examples section the reader can copy and run. Add a # Panics section if any path
can panic, a # Errors section describing each error variant if it returns Result,
and a # Safety section listing the caller's invariants if the function is unsafe.
Do not restate the types — the signature already has them, and rustdoc links
them. In examples, use ? rather than unwrap.
Document only what you can read. Prefix any inference with [INFERRED]. Write
[UNKNOWN — needs author] rather than guessing.
CONTEXT I AM SUPPLYING — ground truth, do not infer:
- Why it works this way:
- Invariants the caller must uphold:
- What breaks if this changes:
ITEM:
<paste>
What belongs in a module or package overview?
Orientation, not an inventory. A model handed a package will happily list every exported symbol, which the reader can already get from the generated index. The overview earns its place by saying what the package is for, what the zero value or default state means, and which entry point to start from.
9. Python module docstring
Write a module-level docstring for the file below, following PEP 257 for
formatting. One summary line, blank line, then prose. Cover: what this module is
responsible for, the one or two entry points a new reader should start from, and
any module-level state or import side effects. Do not enumerate every public
symbol — the generated index already does that. Do not describe internal helpers.
Prefix any inference with [INFERRED]. Write [UNKNOWN — needs author] rather than
guessing.
CONTEXT I AM SUPPLYING:
- Why this module exists separately from its neighbours:
- What does NOT belong in it:
MODULE:
<paste>
10. Go package comment
Write a package comment for the Go package below, per go.dev/doc/comment. The
first sentence must begin with "Package <name>". Describe what the package does,
name the primary types and the constructor a caller starts with, and state the
meaning of the zero value where it is useful. Put it in exactly one file of the
package. If any exported name is deprecated, add a "Deprecated: " paragraph
naming the replacement.
Prefix any inference with [INFERRED]. Write [UNKNOWN — needs author] rather than
guessing.
CONTEXT I AM SUPPLYING:
- Why this package exists:
- What must NOT be added to it:
PACKAGE FILES:
<paste>
11. Rust crate-level documentation
Write crate-level documentation for lib.rs using //! inner doc comments. First
line: one sentence a reader can use to decide whether this crate solves their
problem. Then a copy-pasteable example that works without shortcuts, then the
crate's features and any cargo feature flags. Link to items with intra-doc link
syntax. Do not compare the crate to other crates.
Prefix any inference with [INFERRED]. Write [UNKNOWN — needs author] rather than
guessing.
CONTEXT I AM SUPPLYING:
- The problem this crate solves that alternatives do not:
- Non-goals:
CRATE ROOT AND PUBLIC MODULES:
<paste>
How do you prompt for a README that is not a restatement of the file tree?
By deciding first who the README is for. Unlike docstrings, README has no normative specification, which is exactly why an unguided prompt produces the same badge-and-file-tree template every time. The prompt has to supply the structure the ecosystem does not.
12. README for a library
Write a README for the repository below, for a developer deciding in ninety
seconds whether to adopt it. Sections, in order: one-sentence description; the
problem it solves; install; a minimal working example that runs as written; a
second example showing the most common real configuration; requirements and
supported versions; where the full docs live; licence.
Rules: no badge wall. No file tree. No "Features" bullet list that restates the
API index. Nothing about contribution or code of conduct — those are separate
files. If you cannot produce a runnable example from what I gave you, write
[UNKNOWN — needs author] rather than an invented one.
CONTEXT I AM SUPPLYING:
- Who this is for:
- Who it is NOT for:
- The closest alternative, and when to use that instead:
REPOSITORY (package manifest, entry point, public API):
<paste>
13. README for an internal service
Write a README for the internal service below, for a teammate who has been paged
about it at 2am and has never seen it. Sections: what it does in one sentence;
what breaks downstream if it is down; how to run it locally; required environment
variables and where their values come from; how to tell whether it is healthy;
where the logs, dashboards and runbook are; who owns it.
Do not write marketing copy. Do not include an architecture diagram description
you inferred. Where you do not know a value, write [UNKNOWN — needs author].
CONTEXT I AM SUPPLYING:
- Owning team and escalation path:
- Downstream consumers:
- The failure mode that has actually happened before:
SERVICE (entrypoint, config, deployment manifest):
<paste>
Can you generate an API reference from source?
Partly. Signatures, types and status codes are readable. Semantics — idempotency, rate limits, what a field means when it is absent, which errors are retryable — are not, and those are the parts an integrator actually needs.
14. HTTP endpoint reference
Document the HTTP endpoint below as reference material. For each endpoint give:
method and path; path, query and body parameters with types and whether each is
required; a request example; a success response example with every field
annotated; every error status the handler can return and the condition that
produces it.
Read the status codes and error conditions from the handler body — do not assume
a conventional set. For each of these, answer or mark [UNKNOWN — needs author]:
is it idempotent, is it paginated and how, is it rate limited, is it
authenticated and with what.
Prefix any inference with [INFERRED].
HANDLER AND ROUTE DEFINITION:
<paste>
15. Reference page for a public class or type
Produce a reference page for the type below. Structure: purpose in one sentence;
construction, including what an uninitialised or default instance means; the
public members grouped by task rather than alphabetically; the lifecycle
(what must be called before what, what must be released); thread-safety;
one worked example.
Group by task, not by visibility. If two members must be called in order, say so
explicitly — that constraint is invisible in an alphabetical index.
Prefix any inference with [INFERRED]. Write [UNKNOWN — needs author] rather than
guessing.
CONTEXT I AM SUPPLYING:
- Ordering constraints between members:
- Members that are public but not intended for external use:
TYPE:
<paste>
Architecture decision records: the one the model cannot write alone
An ADR is the clearest case of the whole argument. Michael Nygard's original post, which introduced the format, opens with the reason it exists: "One of the hardest things to track during the life of a project is the motivation behind certain decisions." Motivation is not in the diff. A model that writes an ADR from your code has written a description of the outcome and called it a decision.
The format is five parts: Title, Context, Decision, Status, Consequences. Nygard is specific that Consequences covers everything — "All consequences should be listed here", positive, negative and neutral alike — and that the document should read as "a conversation with a future developer".
Status is the part teams drop and then regret. Nygard's original scheme keeps a reversed decision in the repository and marks it superseded, with a pointer to its replacement, precisely so that a future reader can see what the team used to believe and what changed. Delete the old record instead and you have removed the only evidence that the question was ever asked.
So the ADR prompt inverts the usual shape. You supply the substance; the model does the structuring and the interrogation.
16. Draft an ADR from a decision you have already made
Turn my notes into an architecture decision record with exactly five sections:
Title, Context, Decision, Status, Consequences.
Title: a short noun phrase, not a sentence.
Context: the forces at play — technical, organisational, and local to this
project. Value-neutral. State the tensions between them explicitly.
Decision: full sentences, active voice, beginning "We will".
Status: proposed or accepted.
Consequences: positive, negative and neutral, all of them. Include what becomes
harder.
Write prose in paragraphs, not bullet fragments. One to two pages.
Do not invent a force or a consequence I did not give you. Where the record needs
something I have not supplied, list it at the end under "Questions for the
author" instead of filling it in.
MY NOTES:
<paste>
17. Interrogate a decision before writing it up
I am about to write an ADR for the decision below. Before drafting anything, ask
me the questions a future maintainer would ask, in priority order, up to ten.
Cover at minimum: what alternatives were considered and why each was rejected,
what has to be true for this to remain the right call, what would make us reverse
it, who is affected outside this team, and what this makes harder.
Ask only. Do not draft the ADR and do not answer your own questions.
DECISION:
<paste>
Migration guides and changelog entries
Both are derived from a diff rather than a snapshot, which changes the prompt. Keep a Changelog is blunt about the failure mode: "Using commit log diffs as changelogs is a bad idea: they're full of noise." Its six change types are Added, Changed, Deprecated, Removed, Fixed and Security, with an Unreleased section at the top and one entry per version. If your commit messages are the raw material, the sibling guides on generating conventional commit messages and on turning them into PR descriptions cover the upstream half; this prompt assumes both and starts from the diff.
18. Changelog entry from a diff
Write changelog entries for the diff below, following keepachangelog.com. Group
strictly under the applicable headings from: Added, Changed, Deprecated, Removed,
Fixed, Security. Put them under ## [Unreleased].
Write for a user of this software, not a contributor: describe what changed for
them, not which functions were edited. One line per change. No commit hashes, no
merge commits, no internal refactors that produce no observable difference —
omit those entirely rather than padding.
If a change is breaking, say so in the line itself and name what the reader must
do. If you cannot tell whether a change is observable from outside, list it under
"Needs author review" at the end instead of guessing a heading.
DIFF:
<paste>
19. Release notes for a version bump
Write release notes for version <X.Y.Z> from the changelog entries below.
Structure: one-paragraph summary of what this release is about; Breaking changes
with a migration line each; then Added, Fixed, Security. Order by what most
affects a reader upgrading, not by size of change.
State the version increment and whether it is compatible with the previous
version. If the entries include a breaking change under a minor or patch bump,
flag that contradiction rather than smoothing it.
CHANGELOG ENTRIES:
<paste>
20. Migration guide for a breaking change
Write a migration guide for the breaking change below, addressed to someone whose
build has just failed. Structure: what broke, in the words of the error they are
seeing; why it changed, in two sentences; the before-and-after code, minimal and
complete; the mechanical steps in order; what cannot be migrated automatically
and what to do instead; how to verify the migration worked.
Lead with the symptom, not the rationale. Include the actual error text if you
can read it from the source or from what I supplied; otherwise write
[UNKNOWN — needs author].
CONTEXT I AM SUPPLYING:
- Why we made this change:
- What we considered and rejected to avoid breaking it:
- The deprecation path, if any:
BEFORE AND AFTER:
<paste>
21. Deprecation notice
Write a deprecation notice for the symbol below, in the convention of its
language. For Go, a paragraph beginning exactly "Deprecated: " in the doc
comment. For Java, the @deprecated block tag plus the @Deprecated annotation.
For TypeScript, the @deprecated TSDoc tag. For Python, a note in the docstring
plus whatever runtime warning the project already uses.
Every notice must contain three things: that it is deprecated, what to use
instead by name, and the version or date after which it may be removed. If I have
not told you the replacement or the removal date, write
[UNKNOWN — needs author] rather than "a future version".
SYMBOL AND REPLACEMENT:
<paste>
When should the model write an inline comment instead of a docstring?
When the fact is about the implementation rather than the contract. That is the same line Google's style guide draws, and it is a useful instruction to give the model directly, because the default behaviour is to put everything in the doc block.
Inline comments are also where generated documentation does the most damage, because a comment sitting beside a line reads as an eyewitness account. If the model guessed, the guess now looks like a note from whoever wrote the line.
22. Comment only the non-obvious lines
Read the function below and add inline comments ONLY where a competent reader of
this language would otherwise have to stop and work something out. Candidates: a
non-obvious algorithmic choice, a workaround for an external bug, an ordering
constraint, a magic number, a deliberate deviation from the surrounding style, a
performance trade-off.
Do not comment anything that restates the line. Do not add a comment to every
block. If nothing in this function qualifies, say "no comments needed" and stop —
that is a valid answer and I would rather have it than filler.
For each comment, prefix it with [INFERRED] if you are reasoning from the code
rather than reading a fact, and I will replace it or delete it.
FUNCTION:
<paste>
23. Explain a magic number or constant
For each literal constant in the code below that is not self-evident, tell me
what you can and cannot determine: where the value came from if it is traceable
in the source, what breaks if it is changed in either direction, and whether it
must agree with a value defined somewhere else.
Output a table, one row per constant, with a column for "known from source" and a
column for "needs the author". Do not write the comment yet. I will fill in the
second column and then ask you to write it.
CODE:
<paste>
What does a runbook need that a README does not?
A decision procedure under stress. Like README, a runbook has no normative specification, so the prompt supplies the shape: symptom first, one action per step, an explicit stop condition, and an escalation path. Prose paragraphs are the wrong format for someone reading at 3am.
24. Runbook for an operational procedure
Write a runbook for the procedure below, for an on-call engineer who has never
run it. Structure:
1. When to run this — the exact alert or symptom, quoted.
2. Preconditions and access required.
3. Numbered steps. One action per step. For each: the exact command, what a
successful result looks like, and what to do if it does not appear.
4. Verification — how to confirm the problem is actually resolved.
5. Rollback — how to undo each step that is undoable, and which are not.
6. Stop condition — when to stop and escalate, and to whom.
No prose paragraphs. No step that says "investigate" without saying where to
look. Mark any command you have not been shown as [UNKNOWN — needs author]
rather than inventing plausible syntax — an invented command run at 3am is the
worst possible outcome of this prompt.
CONTEXT I AM SUPPLYING:
- Escalation contact:
- Steps that are irreversible:
- What has gone wrong with this procedure before:
PROCEDURE NOTES, SCRIPTS AND DASHBOARDS:
<paste>
25. Convert an incident postmortem into a runbook
From the incident writeup below, extract a runbook for the same failure happening
again. Take only what the writeup evidences. For every step, cite the sentence in
the writeup it came from.
Then list separately: things the responders did that the writeup does not explain
well enough to repeat, and the detection gap — what would have caught this
sooner. Do not merge those into the steps.
WRITEUP:
<paste>
Why is a wrong docstring worse than no docstring?
Because of what each one does to the reader. A missing docstring sends them to the code. A confident, wrong one stops them looking. Reviewers trust doc blocks and almost never re-derive them from the body, so a fabricated detail can survive years of review and shape decisions made by people who never questioned it.
This is the specific way a documentation prompt fails. It is not that the model refuses; it is that it produces a fluent, well-formatted block containing one sentence about behaviour the function does not have. That sentence looks exactly like the true ones around it.
The mitigations are the two mechanisms from the top of this page — document only what is readable, and mark every inference — plus one separate pass that no generation prompt should be asked to do at the same time.
26. Verify a doc block against the code
Below is a function and a doc block that claims to describe it. Do not rewrite
anything. For each factual claim in the doc block, output one row:
CLAIM | SUPPORTED / CONTRADICTED / NOT DETERMINABLE | evidence (line or reason)
A claim is SUPPORTED only if you can point at the code that makes it true.
Anything about intent, performance, thread-safety or downstream effects that is
not visible in this file is NOT DETERMINABLE — do not mark it supported because
it sounds right.
Then list, separately: behaviour the code has that the doc block omits.
CODE:
<paste>
DOC BLOCK:
<paste>
Run that against a block you generated last month and it will find something. That is the point: the hallucination risk in documentation is not dramatic, it is a single quiet clause, and only a claim-by-claim pass surfaces it.
What happens to these docs in six months?
They rot. A generated block is a snapshot of one moment in the file's history, and nothing in the toolchain re-runs when the code changes. Six months of edits later, the docstring is describing a function that no longer exists in that form — and it still looks authoritative.
Treat documentation as code that needs maintaining. Concretely, that means three habits. Store the prompt in the repository next to the doc it produced, so the next person regenerates rather than reinvents. Re-run generation from the current source rather than editing the old block, because editing preserves stale sentences you did not think to check. And audit on the diff, not on a calendar.
27. Audit a file's documentation against its current state
Below is a source file. Read every doc block, docstring and inline comment in it
and report drift only. For each problem, one line:
LOCATION | ISSUE | current code says | doc says
Issue types: CONTRADICTED (doc states something the code does not do), STALE
(refers to a parameter, return value, error or symbol that no longer exists),
INCOMPLETE (a documented function gained a parameter, error path or side effect
that is undocumented), OR NOISE (the block only restates the signature).
Do not rewrite anything. Do not report style. If a file is clean, say so.
FILE:
<paste>
28. Audit on the diff
Here is a diff. For each changed symbol, tell me whether its documentation must
change too, and why. Consider: signature changes, new or removed error paths,
changed defaults, changed side effects, changed thread-safety, and anything the
doc block asserts that the diff falsifies.
Output: symbol | doc needs update? yes/no | what specifically is now wrong.
Then list documentation elsewhere in the repository that likely references these
symbols and should be checked — README, migration guides, runbooks — naming the
file if it appears in the diff context.
Do not write the updated docs. I want the list first.
DIFF:
<paste>
29. Regenerate a doc block from the current source
Regenerate the doc block for the function below from the current source, in
<convention>. Then output a second section, "What changed and why", listing every
sentence in the old block that you dropped or altered, with the reason.
Preserve verbatim any line in the old block marked as author-supplied context,
any [UNKNOWN — needs author] marker still unanswered, and any rationale that the
code does not contradict — that content did not come from the source and you
cannot regenerate it.
OLD BLOCK:
<paste>
CURRENT SOURCE:
<paste>
30. Documentation coverage report for a module
For the module below, produce a coverage table: every public symbol, whether it
has documentation, and whether that documentation includes each of — purpose,
parameters, return value, error conditions, and any rationale beyond restating
the signature.
Then rank the undocumented and thinly-documented symbols by how much a reader
would suffer without docs: exported and non-obvious first, exported and obvious
next, internal last. Do not write any documentation yet.
MODULE:
<paste>
Where do you actually run these prompts?
Wherever the model can read the file, which for most of this list means a coding agent in your editor rather than a chat window — you do not want to be pasting a module into a browser tab. A useful pattern is to keep the prompts in the repository, alongside your agent instructions (CLAUDE.md templates for Claude Code covers that file), so the convention choice and the inference-marker rule are already loaded before anyone asks for a docstring.
Here is the honest scope of what we do, since this is our blog. Prompt Architects works on prompt text. It does not read your repository and it does not write your documentation — the features page lists prompt enhancement, intent detection, before/after comparison, history, the prompt libraries and the browser extension, and no documentation feature. There is no API, either. If you want a tool that ingests a codebase and emits docs, this is not it, and the honest recommendation is your coding agent plus the templates above.
What we do have that is relevant: an MCP server at mcp.prompt-architects.com/mcp, with four tools — improve, refine, shorten and enhance, also available as slash commands like /mcp__pa__improve. The MCP integrations page publishes quick-start guides for Claude Desktop, Claude.ai, Cursor, Claude Code, Codex and Codex CLI, using OAuth or a pa_live_ personal access token. So when a template above is nearly right but too vague for your codebase, you can sharpen it in place without leaving the editor. The agent still does the reading and the writing. The free plan covers 5 enhancements per day, per the FAQ page.
Stop rewriting prompts. Start shipping.
Works with ChatGPT, Claude, Gemini, Grok, Midjourney, Ideogram, Veo3 & Kling. 5.0★ on the Chrome Web Store.
Create An AccountThe pattern underneath all thirty of these is the same one that makes a code review prompt useful rather than decorative, and the same one that makes reading an unfamiliar codebase with AI work: name the artefact, restrict the model to what it can actually see, and be explicit about where your knowledge enters. Documentation is the case where that discipline matters most, because the failure is silent. Nobody gets an error from a wrong docstring. They just make a decision based on it.