Verbose Boilerplate.
AI assistants pad simple logic with redundant comments, defensive ceremony, and copy-pasted near-duplicate blocks instead of reusing or extracting existing code, inflating line count without adding value.
##Signs and Symptoms
A reviewer spots Verbose Boilerplate when a diff is much longer than the problem warrants: every line is narrated by a comment that just restates it, simple expressions are wrapped in defensive null/undefined ladders, and the same shape of code reappears two or three times instead of being factored into one helper. The give-away is a high comment-to-logic and line-to-behavior ratio, plus blocks that could have been three lines of idiomatic code.
// Function to get the user's full name
function getFullName(user: User): string {
// Check if the user is defined
if (user === null || user === undefined) {
// Return an empty string when no user is provided
return "";
}
// Get the first name, defaulting to empty string
const firstName = user.firstName ? user.firstName : "";
// Get the last name, defaulting to empty string
const lastName = user.lastName ? user.lastName : "";
// Concatenate the first and last name with a space
const fullName = firstName + " " + lastName;
// Trim whitespace and return the result
return fullName.trim();
}
Three lines of behavior buried in ~12 lines of ceremony. In a real PR you typically see this same pattern copy-pasted as getDisplayName, getLabel, and getInitials, each hand-rolling logic a formatName() util already covers — the classic Duplicate Code smell. Other tells: empty pass-through constructors/wrappers, try { ... } catch (e) { throw e } ceremony, and a bespoke email/URL validator sitting next to the project's existing validation module.
##Reasons for the Problem
Why models produce it
- Next-token verbosity bias. LLMs are trained on huge volumes of tutorial, Stack Overflow, and beginner code where step-by-step comments and explicit ceremony are the norm. The most probable continuation is the most heavily-explained one, not the most concise.
- RLHF rewards "looking thorough." Helpfulness/verbosity tuning pushes models toward output that appears complete and self-explanatory — comments on every line, defensive checks everywhere — which graders and users reward even when it adds no value.
- No repo context = no reuse. Without the surrounding codebase in context, the model can't know a
formatName()orvalidateEmail()util already exists, so it re-derives it inline. This is exactly OX Security's Over-Specification ("hyper-specific, single-use solutions instead of generalizable, reusable components," found in 80–90% of AI code) and Avoidance of Refactors (80–90%). - Generation, not consolidation. Agents emit fresh code per prompt and never circle back to dedupe. GitClear's 2025 analysis of 211M changed lines found copy/pasted code rose from 8.3% (2020) to 12.3% (2024) and overtook "moved" (refactored) lines for the first time, while refactored lines fell from ~24% to 9.5% and duplicated 5+ line blocks rose ~8x.
- Comment-everything default. OX Security found "Comments Everywhere" in 90–100% of AI-generated code.
Why it hurts
- Maintainability: more surface area to read and change; redundant comments drift out of sync with code and become actively misleading (Fowler's Comments smell).
- Correctness & security: duplicated blocks mean a bug or vuln must be fixed in N places — OX's Bugs Déjà-Vu (70–80%). Cloned code correlates with 15–50% more defects.
- Review load: large low-signal diffs hide real changes and induce reviewer fatigue, so genuine issues slip through.
- Compounding tech debt: each near-duplicate makes the next extraction harder, entrenching the problem the model can't see across files.
##Treatment
Review / prompting tactics
- Force reuse before generation: "Search the codebase for existing helpers (
formatName,validate*) and reuse them; do not re-implement." Provide the relevant utils in context. - Budget the output: "Keep this under ~15 lines; no comments that restate the code — only comments explaining why."
- Make the model run the tools: require
eslint/ruffand ajscpdduplication pass, and have it report and fix violations before returning. - Ask for the smallest diff and an explicit dedup step: "If any block repeats, extract one shared function (Extract Function) and call it."
- Consolidate in review: when you see the third near-copy, apply Extract Function, Pull Up Method, or Consolidate Duplicate Conditional Fragments, and Replace Comment with Code (rename so the code self-documents).
Refactor (before → after)
// after: comments removed (code is self-evident), ceremony collapsed,
// shared util reused instead of re-derived
function getFullName(user?: User): string {
return [user?.firstName, user?.lastName].filter(Boolean).join(" ");
}
And kill the duplicates outright — if getDisplayName/getLabel were doing the same thing, delete them and point callers at one function (Remove Dead Code / Inline Function). Replace the hand-rolled validator with the existing module import rather than keeping a parallel copy.
Rule of thumb for the catalog: treat any AI block where comments outnumber logic lines, or where you can name an existing helper it ignored, as a candidate for deletion-by-reuse — the fastest fix for boilerplate is usually less code, not more.
##Detected by
- jscpd duplication threshold (--min-tokens / --threshold) — Copy/paste detection (min-tokens threshold)
- SonarQube / SonarCloud typescript:S4144 — Functions and methods should not have identical implementations
- SonarQube / SonarCloud typescript:S1192 — String literals should not be duplicated
- SonarQube / SonarCloud javascript:S1871 — Two branches in a conditional structure should not have exactly the same implementation
- ESLint (eslint-plugin-sonarjs) sonarjs/no-identical-functions — Functions should not have identical implementations
- ESLint (eslint-plugin-sonarjs) sonarjs/no-duplicate-string — String literals should not be duplicated
- ESLint (core) no-useless-constructor — Redundant empty constructor / pass-through boilerplate
- ESLint (core) max-lines-per-function — Bloated/verbose function length (partial proxy)