Back to blog
Engineering20 min read

Prompting for Comments and Docstrings

A docstring documents an interface; a comment explains a decision. 27 copy-paste prompts for why-comments built from what only you know, plus audits for restate-the-code noise.

NH
Nafiul Hasan
Founder, Prompt Architects

TL;DR: Docstring prompts have a settled answer: name the convention and a model can follow it. A comment is the harder half, because the reason a line is shaped this way isn’t in the file; it has to come from you. Below: 27 copy-paste prompts for writing that “why” comment, and for auditing the comments you already have for noise that just restates the code.

Ask a model to document a function and it will happily write both a docstring and a comment, in whichever shape you didn’t specify. That’s worth stopping on, because the two fail differently. A wrong docstring misleads whoever calls the function without reading it. A wrong comment sits one line away from the code that contradicts it, and a reviewer reading top to bottom tends to believe the comment first.

This page is about the second artefact. If what you need is the docstring convention itself, meaning which heading name is Google style, which is NumPy, and which language puts the type in curly braces versus which omits it, that argument already exists, verified against nine primary specs, in the docstring generator post. What's underserved is the other kind of instruction: the comment that has to come from you, because nothing in the file can produce it.

What's the actual difference between a docstring and a comment?

A docstring documents the calling contract; a comment documents everything a caller doesn't need and a future maintainer does. One gets extracted by tooling into a reference a stranger reads before they open the file at all. The other lives beside the line, for someone who is already reading the file and about to change it.

Rust makes the split literal in its own grammar rather than leaving it to convention. A /// line becomes an actual #[doc="..."] attribute attached to the item; rustdoc renders it, and it is part of the public interface. A plain // line does not become anything: the Rust reference states flatly that "Non-doc comments are interpreted as a form of whitespace." The compiler doesn't see it, a caller never sees it, and no tool extracts it. It exists only for the next person editing that exact block.

Most languages don't enforce the split syntactically the way Rust does, but the boundary is the same everywhere. A docstring answers "what do I pass in, and what do I get back?" A comment answers "why is this line shaped like this, and what happens if I change it?" Confuse the two and you get a docstring padded with internal reasoning the caller never asked for, or a comment that just restates the signature: noise, relocated.

Why does a "why" comment matter more than a "what" comment?

Because a "what" comment is redundant with the line it sits on, and a "why" comment is the only information in the file the code itself cannot express. PEP 8 makes the point with two lines of identical code:

x = x + 1                 # Increment x
x = x + 1                 # Compensate for border

Both comments are grammatically fine. Only the second one is information; PEP 8 itself calls the first kind out directly: "Inline comments are unnecessary and in fact distracting if they state the obvious." The first line restates x = x + 1 in English. The second tells you why the line exists at all, and neither PEP 8 nor a model reading the file alone can know that without you.

Here's the same idea in a form that actually happens in review, with the fact supplied instead of assumed:

retry_count = 3  # number of retries
retry_count = 3  # upstream rate-limits after 3 attempts inside 10s and
                  # blocks us for an hour if we trip it — raising this is
                  # not free (see INC-4821)

The first line is what a model produces unprompted: accurate, and worth nothing. The second could only come from someone who watched the incident happen.

Google's Python style guide draws the same line from the maintenance side. Comments earn their place in "tricky parts of the code," and the guide is specific: "If you’re going to have to explain it at the next code review, you should comment it now." It's just as direct about the failure mode in the other direction: "never describe the code. Assume the person reading the code knows Python (though not what you’re trying to do) better than you do." A model defaults to describing the code, because that's the part it can actually see.

What do the style guides actually say about inline comments?

Less than they say about docstrings, and less than you'd expect. PEP 8 and Google's Python guide both name the same failure (restating the obvious), but they diverge on placement, and once you leave Python, only Airbnb's JavaScript guide addresses comments as a named topic at all.

Three real style guides, three different jobs assigned to a comment
FeaturePEP 8Google Python Style GuideAirbnb JavaScript
PlacementInline, 2+ spaces before the #Above tricky code, or line-end for non-obvious onesOwn line, above the code
Restating the codeNamed directly as the failure to avoidNamed directly as the failure to avoidNot addressed
TODO vs FIXMENot addressedNot addressedFIXME = problem, TODO = solution
Primary sourcepeps.python.org/pep-0008google.github.io/styleguide/pyguide.htmlgithub.com/airbnb/javascript

Airbnb's guide is also the only one of the three to give TODO and FIXME separate jobs: FIXME flags a problem that's still not understood, TODO flags a solution that's decided but not yet written. That split is worth keeping even outside JavaScript: it turns one vague marker into two different prompts, further down this page.

What goes in the context block you paste before every prompt below?

Everything a model can't read from the file: the constraint, what it works around, the alternative you rejected, and what breaks if someone "fixes" it later. Leave a line blank rather than invent an answer for it: a blank slot is honest, and a filled one that isn't true is not.

CONTEXT I AM SUPPLYING — ground truth, do not infer:
- The constraint or bug this works around:
- What breaks if this line changes:
- The alternative I rejected, and why:
- Who else depends on the current behaviour:
(Leave a line blank and I will accept the omission. Do not fill it in for me.)

Document only what you can verify by reading the code I gave you. Where you
must reason rather than read, prefix that sentence with [INFERRED]. If a fact
is neither readable nor supplied above, write [UNKNOWN — needs author] instead
of a plausible guess.

Prompts for writing a "why" comment

Paste the context block once, then one of these. Each restricts the model to what it can verify and gives it somewhere to put what you supplied instead of inventing it.

1. A general-purpose "why" comment

Read the line or block below and, using ONLY the context I supply beneath it,
write one comment explaining why it is written this way — not what it does.

Do not restate the line in English. If you cannot produce a reason from the
context I gave you, write "no comment needed — nothing here is non-obvious"
instead of inventing one.

CONTEXT I AM SUPPLYING — ground truth, do not infer:
- The constraint or bug this works around:
- What breaks if this line changes:
- The alternative I rejected, and why:

CODE:
<paste>

2. A workaround for an external bug

Write a comment for the workaround below explaining which external system's bug
or limitation it exists for, not what the workaround itself does — that part is
already readable. Name the system and, if I gave you one, the ticket reference.
State the condition under which this comment and the workaround should both be
deleted.

CONTEXT I AM SUPPLYING:
- The system and the bug or limitation:
- Ticket or issue reference, if any:
- The condition that means this is safe to remove:

CODE:
<paste>

3. A magic number or threshold

For the literal value below, write a comment stating where the number came
from and what happens on either side of it — do not just restate the value in
words. If it must agree with a number defined somewhere else in the system,
say so explicitly, so a future edit doesn't change one without the other.

CONTEXT I AM SUPPLYING:
- Where this number came from:
- What happens if it's raised, and if it's lowered:
- Where else this value must match:

CODE:
<paste>

4. An ordering or sequencing constraint

Write a comment stating the order requirement on the lines below and what
specifically goes wrong if a future edit reorders them — a race, a stale read,
a leak, whatever it actually is. Do not write "order matters" alone; that
sentence has never once stopped a reorder.

CONTEXT I AM SUPPLYING:
- What breaks, specifically, if these run out of order:

CODE:
<paste>

5. A rejected alternative, recorded at the call site

Write a short comment at the line below naming the approach we tried or
considered instead, and the one-sentence reason it was rejected. This is not
an ADR — one sentence, at the code, for the next person who has the same idea.

CONTEXT I AM SUPPLYING:
- Alternative considered:
- Why it was rejected:

CODE:
<paste>

6. A concurrency or thread-safety guarantee

Write a comment stating the thread-safety guarantee (or lack of one) for the
code below, in terms of what a caller is and isn't allowed to assume — not a
restatement of any lock already visible in the code. State what happens if the
guarantee is violated, if you know.

CONTEXT I AM SUPPLYING:
- What a concurrent caller must NOT assume:
- What happens if this guarantee is violated:

CODE:
<paste>

7. A performance trade-off

Write a comment explaining why the slower-looking approach below was chosen
over the obvious faster one, in terms of the real constraint — memory,
correctness under some input, an external rate limit, anything except "it's
faster here," which is not a reason. Name the input or condition under which
the obvious alternative fails.

CONTEXT I AM SUPPLYING:
- Why the faster-looking alternative doesn't work here:

CODE:
<paste>

8. A regular expression

Write a comment above the regex below stating, in plain language, what it
matches and — separately — one input it is deliberately NOT designed to match,
if I give you one. Do not describe the regex syntax token by token; describe
what it's for.

CONTEXT I AM SUPPLYING:
- What this is deliberately not designed to match:

REGEX AND SURROUNDING CODE:
<paste>

9. A security-sensitive check

Write a comment for the check below stating what it prevents, in terms of an
attacker's action, not the mechanism already visible in the code. If removing
this check would be exploitable, say what the exploit looks like at a level a
reviewer without a security background can use to know not to remove it.

CONTEXT I AM SUPPLYING:
- What this check prevents:
- What removing it would allow:

CODE:
<paste>

10. A configuration default or feature flag

Write a comment for the default value or flag below stating why THIS default,
not another one, and who or what is affected if it changes. If the flag is
temporary, state the condition under which it should be removed.

CONTEXT I AM SUPPLYING:
- Why this default, specifically:
- Who is affected if it changes:
- Removal condition, if temporary:

CODE:
<paste>

11. A deliberate deviation from the file's usual style

The code below intentionally breaks a convention followed everywhere else in
this file, or suppresses a lint rule. Write a comment stating which convention
is broken and why, directly above the deviation, so the next linter run — or
the next contributor — doesn't "fix" it back.

CONTEXT I AM SUPPLYING:
- The convention or rule being deliberately broken:
- Why:

CODE:
<paste>

12. Non-obvious test setup

Write a comment for the fixture or value below explaining why THIS value was
chosen for the test — not what the assertion checks, which the test already
shows. Flag whether the value is tied to a specific bug this test exists to
catch.

CONTEXT I AM SUPPLYING:
- Why this specific value, not another one:
- The bug this test exists to catch, if any:

TEST CODE:
<paste>

Prompts for TODO and FIXME that don't rot

A bare TODO is a promise with no expiry date. These give it one, or force the choice between a FIXME (a problem, not yet understood) and a TODO (a solution, understood but not written).

13. A TODO with an expiry condition

Write a TODO comment for the item below that names the specific fix already
decided on and the condition under which it must be done — a version, a date,
a dependency upgrade, an event — never just "later." If I didn't give you a
condition, write [UNKNOWN — needs author] rather than inventing "soon."

CONTEXT I AM SUPPLYING:
- The fix that's already decided:
- The condition that means it's time:

CODE:
<paste>

14. A FIXME that names the actual problem

Write a FIXME comment for the code below. State the specific symptom or wrong
behaviour — not "this is broken" — and, if known, what confirming the fix
worked would look like. This marks a problem not yet understood; do not
propose a solution in the same comment.

CONTEXT I AM SUPPLYING:
- The specific symptom or wrong behaviour:

CODE:
<paste>

15. Reclassify every bare TODO/FIXME in a file

List every TODO and FIXME comment in the file below. For each: is it actually
a FIXME (problem, unsolved) or a TODO (solution, unwritten), regardless of
which word it currently uses? Does it name a specific fix or symptom, or just
say "fix this"? Flag any with no owner, no date, and no ticket reference as a
candidate for deletion or escalation — a marker nobody can act on isn't
documentation, it's litter.

FILE:
<paste>

How do you capture "why" before it disappears?

The best source for a why-comment is usually a conversation that already happened somewhere else: a review thread, an incident writeup, a Slack message. That source has a shelf life, and these three move it into the file before it's gone.

16. Turn a review-thread comment into a permanent one

Below is a code review discussion about the line that follows it. Extract only
the reasoning a future reader — who will never see this thread — needs to
understand why the line is shaped this way. Write it as an inline comment at
the line. Do not include the back-and-forth, only the resolved reason. If the
thread doesn't actually resolve why, say so instead of guessing which comment
"won."

REVIEW THREAD:
<paste>

LINE(S) THE THREAD IS ABOUT:
<paste>

17. Turn an incident writeup into a comment at the exact line

Below is an incident writeup and the fix that came out of it. Write one comment
at the fixed line, citing the incident by name or ID, stating what happened and
why this specific line prevents it recurring. Do not summarise the whole
incident — one sentence a reader needs before touching this line again.

INCIDENT WRITEUP:
<paste>

FIXED CODE:
<paste>

18. Interrogate before you write, for a comment, not an ADR

I'm about to add a comment explaining the decision below. Before writing
anything, ask me up to five questions a skeptical reviewer would ask about it —
what happens if this assumption stops holding, what else depends on it, what
was tried instead. Ask only; do not draft the comment yet.

DECISION AND CODE:
<paste>

Can a model find the comments that are already noise?

Yes. This is the audit half of the same discipline, pointed at comments you already have instead of ones you're about to write.

19. Find every comment that just restates its line

For the file below, list every comment that restates what the line already
says in code — a variable increment, a type already declared, a call already
named by its own function. Output: LINE | COMMENT | WHY IT ADDS NOTHING. Do
not flag a comment that states a reason, a constraint, or a reference, even if
it's short. Do not rewrite anything, only report.

FILE:
<paste>

20. Score a file's why-vs-what ratio

For the file below, classify every comment as WHY (states a reason,
constraint, or consequence not visible in the code) or WHAT (restates
behaviour already readable). Report counts for each, then list the WHY
comments only — those are the ones that would be lost if someone "cleaned up"
this file by deleting comments that "just repeat the obvious."

FILE:
<paste>

21. Find comments that contradict the current code

Here is a diff. For each comment near a changed line, state whether the
comment still describes what the code now does. Report: LOCATION | COMMENT |
STILL TRUE / NOW FALSE / CANNOT TELL. Mark NOW FALSE only if the diff makes it
false; use CANNOT TELL rather than guessing about anything the diff doesn't
show.

DIFF:
<paste>

22. Find dead TODO/FIXME entries

List every TODO and FIXME in the excerpt below. For each: does it name an
owner, a date, or a ticket reference? If none of the three, flag it as dead —
not because it's wrong, but because nothing forces anyone to ever act on it.
Separately, flag any that reference a ticket number so I can check whether
it's already closed.

CODE:
<paste>

23. Audit only the comments a diff actually touched

Here is a diff. Look only at comments that were added or changed in it —
ignore untouched comments elsewhere in the file. For each new or edited
comment, classify it WHY or WHAT using the same test as before, and flag any
WHAT comment as something the reviewer should push back on before merge.

DIFF:
<paste>

Why is a wrong comment worse than no comment?

Because a missing comment sends a reader to the code, and a wrong one stops them looking. A reviewer trusts a comment sitting right next to a line more than their own read of that line, which is backwards, and it's exactly why a confidently wrong one survives review after review. It isn't a refusal or a visible error; it's one fluent, well-placed sentence that happens to be false, the same quiet hallucination risk documentation carries generally, just closer to the code it can damage.

24. Verify a comment against the code, in a fresh turn

Below is a line of code and a comment claiming to explain it. Do not rewrite
either. State whether the comment's claim is SUPPORTED (you can point at the
code proving it), CONTRADICTED (the code does something else), or NOT
DETERMINABLE (the claim is about intent, history, or an external system not in
what I gave you — do not mark this SUPPORTED because it sounds plausible).

CODE AND COMMENT:
<paste>

25. Batch-add "why" comments across a diff, skipping the unfilled ones

Below, each line flagged CONTEXT-NEEDED either has a reason filled in beneath
it or is left blank. Write a why-comment only for the ones with a reason. For
any left blank, do not write a comment and do not flag it as missing — a blank
slot means I decided this line doesn't need one, not that I forgot.

LINES WITH CONTEXT:
<paste>

26. Apply the file's own comment convention

Rewrite the comment below to match the placement and spacing already used
elsewhere in this file — above the line versus at the end of it, a space
after the marker versus none, a full sentence versus a fragment. Change only
its form, not what it says, and tell me which existing comment in the file
you matched it to.

FILE (for convention) AND COMMENT TO REFORMAT:
<paste>

27. Decide: comment, docstring, or commit message?

I have one fact to record about the code below: <state the fact>. Tell me
which of a docstring, an inline comment, or a commit message is the right home
for it, and why — based on whether a caller needs it without reading the body
(docstring), whether it explains this exact line to the next editor (comment),
or whether it explains a change that is now in the past (commit message). If
it belongs in more than one, say which is primary.

FACT AND CODE:
<paste>

Where do you actually run these?

Wherever the model can read the file and, for the audit templates especially, the diff: a coding agent in your editor or CI, not a chat window where you'd have to paste in history it can't otherwise see.

Prompt Architects works on prompt text, not on your repository. The features page lists prompt enhancement, intent detection, history, and the browser extension; there's no code-reading feature and no public API, and that same page's roadmap section still lists API access as unreleased. What's relevant here is the MCP server at mcp.prompt-architects.com/mcp, with four tools (improve, refine, shorten, enhance) reachable from Claude Desktop, Claude Code, Cursor, and Codex by OAuth or a pa_live_ token, per the MCP integrations page. When a template above is close but not quite tuned to your file's convention, that's what sharpens it in place. The free plan covers 5 enhancements a day, per the FAQ page. Your coding agent still reads the file and writes the comment.

Free Chrome Extension

Stop rewriting prompts. Start shipping.

Works with ChatGPT, Claude, Gemini, Grok, Midjourney, Ideogram, Veo3 & Kling. 4.8★ on the Chrome Web Store.

Create An Account

The discipline underneath all 27 of these is the same one that makes a code review prompt worth running rather than decorative: restrict the model to what it can verify, and be explicit about where your own knowledge enters. It's also why regenerating comments wholesale carries the same risk as rewriting code nobody asked you to touch: a comment someone wrote from a fact you can't see is exactly the context an unscoped rewrite deletes without noticing. If the fact belongs in a commit instead, the PR description prompts pick up from here. And if it belongs in your agent's standing instructions rather than one file, that's what CLAUDE.md is for. A comment is the cheapest place to keep a fact, right up until the fact is gone, and all that's left is a line that used to make sense to somebody.

Frequently asked questions

Free Chrome Extension

Stop rewriting prompts. Start shipping.

Works with ChatGPT, Claude, Gemini, Grok, Midjourney, Ideogram, Veo3 & Kling. 4.8★ on the Chrome Web Store.

Create An Account