---
title: "Verbose Boilerplate"
type: "ai-smell"
slug: "verbose-boilerplate"
url: "http://localhost:3000/en/ai-smells/verbose-boilerplate.md"
category: "Clarity"
description: "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."
---
# 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.

```ts
// 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()` or `validateEmail()` 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`/`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 (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)**

```ts
// 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) (https://github.com/kucherenko/jscpd)
- **SonarQube / SonarCloud** `typescript:S4144` — Functions and methods should not have identical implementations (https://rules.sonarsource.com/typescript/RSPEC-4144/)
- **SonarQube / SonarCloud** `typescript:S1192` — String literals should not be duplicated (https://rules.sonarsource.com/typescript/RSPEC-1192/)
- **SonarQube / SonarCloud** `javascript:S1871` — Two branches in a conditional structure should not have exactly the same implementation (https://rules.sonarsource.com/javascript/RSPEC-1871/)
- **ESLint (eslint-plugin-sonarjs)** `sonarjs/no-identical-functions` — Functions should not have identical implementations (https://github.com/SonarSource/eslint-plugin-sonarjs/blob/master/docs/rules/no-identical-functions.md)
- **ESLint (eslint-plugin-sonarjs)** `sonarjs/no-duplicate-string` — String literals should not be duplicated (https://github.com/SonarSource/eslint-plugin-sonarjs/blob/master/docs/rules/no-duplicate-string.md)
- **ESLint (core)** `no-useless-constructor` — Redundant empty constructor / pass-through boilerplate (https://eslint.org/docs/latest/rules/no-useless-constructor)
- **ESLint (core)** `max-lines-per-function` — Bloated/verbose function length (partial proxy) (https://eslint.org/docs/latest/rules/max-lines-per-function)
