Humanizing Code
YAML--- name: humanizing-code description: Writes and edits code so it reads like an experienced human wrote it, not an AI assistant. Use when the user asks to humanize, de-AI, or clean up code, or anytime code is being written/edited and should avoid AI tells like excessive comments, docstring bloat, banner separators, generic names, and over-engineered structure. ---
Humanizing Code
Write the code a good senior developer would leave in review: direct, idiomatic, quiet. Comments are the exception, not the default. When in doubt, write no comment. When a comment is needed, it's a single // or # line — never a block, never a banner.
Before touching code:
- Open two or three neighbouring files. Match their comment density, docstring style, naming, and formatting.
- Note the public API, serialized keys, and config names — these don't get renamed.
- Write the simplest direct version. No speculative abstraction, no unrequested helpers.
- Delete every comment that restates the line below it. Delete every separator/banner line.
- For any comment already in the file, run the Existing Comment Check below before touching it.
- Run the repo's formatter, linter, and tests.
Default budget: zero comments, zero docstrings.
Add a comment only when it explains something the code cannot show:
- a non-obvious why
- a constraint or invariant
- a workaround with a reason
- a unit or side effect that would surprise someone
If added, it's one short line, using the language's single-line syntax only (// or #). Never:
- block comments (
/* ... */,""" ... """) used just to hold a note - docstrings with
Args:,Returns:,Raises:,Example: - comments restating the next line ("Initialize...", "Loop over...", "Check if...", "Return...")
- "This function...", "Note that...", "Here we...", "Step 1:", section banners
- separator/divider lines of any kind — no
// ---------------, no# =====, no//////test//////, no ASCII art, no repeated dashes/slashes/equals as visual dividers - TODO or placeholder comments
- emoji, chatty remarks
Docstrings only if the repo already uses them on comparable functions, or the user asks — then one sentence, no parameter list. If the user says "no comments," write none, even on public functions.
Whenever a comment is encountered in code being written or edited, classify it before doing anything else:
-
Short and reasonable (single line, plain, not restating the obvious, not a banner): → Leave it, but ask the user: "Found this comment — want me to rewrite it or leave it as is?" Do not change it unless they say yes.
-
Long, sloppy, AI-sounding, a banner/divider, a docstring block, or a redundant restatement: → Auto-replace or remove it without asking first. Explain the change briefly in chat, not in the file.
This check applies to comments on code too (e.g. a comment hanging off a line of logic), not just standalone lines. When in doubt between category 1 and 2, lean toward category 2 if it's more than one line or contains filler phrasing ("This code...", "Here we...", "Basically...").
Progress:
- Survey 2-3 neighbouring files for conventions
- Identify public API / serialized keys / config names to preserve
- Write the simplest direct implementation
- Name variables by domain role, not generic placeholders
- Strip every comment that restates code, and every separator/banner line
- Classify any pre-existing comments (short-ask vs long-auto-fix) per Existing Comment Check
- Match repo's formatting/typing style, don't over-format
- Run formatter, linter, tests
- Re-read diff for scope creep, leftover comments, and stray dividers
- Freeze behavior first: tests, public API, serialized keys stay stable.
- Keep the diff small — don't reformat the whole file.
- Rename only locals whose role is unambiguous; skip unclear ones.
- To rewrite identifiers, use the parser/AST — never regex over source text (Python:
ast/libcst; JS/TS: Babel/recast; Java: JavaParser; C++: libclang). - Apply the Existing Comment Check to every comment touched or passed over in the diff.
- Don't add comments just because you touched the line.
Example 1:
Input: "Clean up this function" (AI-style with docstring and line comments)
Pythondef summarize_orders(orders): """ Summarize order data. Args: orders: A list of order dictionaries. Returns: A dictionary with revenue, pending, and average. """ # Filter completed orders completed = [o["amount"] for o in orders if o["status"] == "completed"] # Count pending orders pending = sum(1 for o in orders if o["status"] == "pending") # Return the summary return { "revenue": sum(completed), "pending": pending, "average": sum(completed) / len(completed) if completed else 0, }
Output:
Pythondef summarize_orders(orders): completed = [o["amount"] for o in orders if o["status"] == "completed"] return { "revenue": sum(completed), "pending": sum(1 for o in orders if o["status"] == "pending"), "average": sum(completed) / len(completed) if completed else 0, }
(Docstring and restating comments are long/sloppy → auto-removed, mentioned in chat.)
Example 2:
Input: a generic helper used once — def process_data(data): return [x.strip().lower() for x in data] called at a single site.
Output: inline it at the call site as [name.strip().lower() for name in raw_names], named for what it actually holds.
Example 3:
Input file contains:
JavaScript// ////////////// BROWSER LAUNCH ////////////// // this code runs browser function launch() { ... }
Output: the slash-banner line is a divider → auto-removed. The line // this code runs browser is short and plausible → left in place, and the user is asked: "Found // this code runs browser — want me to rewrite it or leave it?"
Example 4:
Input file contains a 6-line comment block above a function explaining, in a rambling way, what the function does, parameter by parameter, including "This function was created to handle..." → long and AI-sounding → auto-replaced with either nothing or one short // line capturing the real non-obvious reason, with a brief note in chat.
- Name things for their domain role (
pending_orders, notresultortemp). - Validate at boundaries, not with exhaustive checks everywhere.
- Match the repo's existing type-hint and formatting density — don't impose a new style.
- Stay in scope: no unrequested files, dependencies, or tests.
- Explain reasoning to the user in chat, not in the file.
- Use single-line
//or#only — never decorative borders, boxes, or repeated punctuation runs.
- Typing
"""or/* */out of habit for a note that should be one//line or nothing. - Writing a comment that starts with "Initialize", "Loop", "Check", "Return", "This", or "Note" — delete it.
- Adding a divider line like
// ----------or/////////////to separate sections. - A comment longer than the line it describes.
- Commenting a line you already named well.
- Explaining what instead of why.
- Leaving a TODO instead of finishing or omitting it.
- Hand-aligning or reformatting code the user didn't ask you to touch.
- Silently rewriting a short, reasonable existing comment without asking first.
- Asking the user about a comment that's clearly long/sloppy AI filler instead of just fixing it.
- No comment restates code
- No divider/banner lines, no long docstring, no Args/Returns block, no TODO
- Every pre-existing comment was classified and handled per the Existing Comment Check
- Names are domain-specific
- Diff is small; nothing unrequested
- Formatter, linter, and tests pass