ConstructiCat Logo
CodeBust.
Browse section ▾

Verbose Boilerplate.

AI assistants pad simple logic with comments that restate each line, step-by-step narration, defensive null ladders, and long-hand expressions where an idiom or an existing helper would do — inflating line count without adding behavior.

##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. Other tells: empty pass-through constructors/wrappers, try { ... } catch (e) { throw e } ceremony, x ? x : fallback where ?? would do, and a bespoke email/URL validator sitting next to the project's existing validation module. When the same verbose shape is also pasted as getDisplayName, getLabel and getInitials, each hand-rolling logic a formatName() util already covers, verbosity has turned into Duplicate Code — see Duplication Instead of Extraction.

##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() or validateEmail() util already exists, so it re-derives it inline. This matches two patterns in the OX Security report: Over-Specification ("hyper-specific, single-use solutions that can't be adapted for other purposes," found in 80–90% of AI code) and Avoidance of Refactors (80–90%).
  • Generation, not consolidation. Agents emit fresh code per prompt and rarely circle back to tighten it. GitClear's 2025 analysis of 211 million changed lines found refactoring-associated ("moved") lines falling from 25% of changed lines in 2021 to under 10% in 2024, while copy/pasted lines rose from 8.3% to 12.3% and in 2024 overtook moved lines for the first time; blocks with five or more duplicated lines grew roughly eightfold during 2024.
  • 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: once a verbose block is copied, a bug or vulnerability must be fixed in N places — OX's Bugs Déjà-Vu pattern (70–80%), which causes "identical bugs to appear repeatedly throughout codebases, requiring the same fix multiple times."
  • 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/ruff and a jscpd duplication 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 and call it" (Extract Method).
  • Consolidate in review: when you see the third near-copy, apply Extract Method, Pull Up Method, or Consolidate Duplicate Conditional Fragments; where a comment only explains what an expression does, apply Extract Variable or Rename Method so the code carries the explanation and the comment can go.

Refactor (before → after)

// after: comments removed (code is self-evident), ceremony collapsed,
// and the existing formatName() util reused instead of re-derived inline
import { formatName } from "@/utils/format";

export const getFullName = (user?: User): string =>
  formatName(user?.firstName, user?.lastName);

And kill the duplicates outright — if getDisplayName/getLabel were doing the same thing, point their callers at one function and delete the copies (Inline Method); anything left without callers is Dead Code and goes too. 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