The "Comment" Fallacy: Why Self-Documenting Code is Still the Goal

Written by nikitakothari | Published 2025/11/27
Tech Story Tags: clean-code | software-engineering | software-development | programming | refactoring | best-practices | ai | clean-code-tips

TLDRIn the bible of software engineering, Clean Code, comments are often characterized as failures to write expressive code. Unfortunately, the rise of Generative AI has tempted many developers into the lazy habit of asking LLMs to just "add comments" to messy code blocks. This is a step backward. Instead of using AI to generate apologies for bad code, we should use it as a merciless refactoring partner to suggest better naming and function extraction, making comments obsolete.via the TL;DR App

Robert C. Martin (Uncle Bob) famously wrote in Clean Code: "Every time you write a comment, you should grimace and feel the failure of your ability to express yourself in code."

It’s a harsh truth that experienced engineers eventually learn: comments lie.

They don't start as lies. They start as helpful little notes. But code is a living, breathing thing. It gets refactored, debugged, and hotfixed. Often, the developer makes the code change in a hurry but forgets to update the comment sitting right above it.

The moment code and comment diverge, the comment becomes a trap. A lying comment is significantly worse than no comment at all because it actively misleads the next person debugging the system.

For years, the industry pushed towards "self-documenting code" - the idea that through descriptive variable names, small single-responsibility functions, and clear architecture, the code should read like prose.

Then, ChatGPT arrived.

The "Explain This" Trap

AI has introduced a dangerous new anti-pattern into workflows. It’s incredibly tempting to take a nasty, complex, poorly named block of legacy spaghetti code, paste it into an LLM, and type:

"Add comments explaining what this code does."

The AI will happily oblige. It will parse the Abstract Syntax Tree and generate grammatically correct English sentences describing the mechanical operations of the code.

You paste it back into your IDE, commit it, and feel productive. You have "documented" the legacy debt.

But you haven't fixed anything.

You have just added noise. You have plastered wallpaper over a cracking wall. You now have messy code plus paragraphs of text that will inevitably drift out of sync with reality.

AI, when used this way, acts as an enabler for bad habits. It reduces the friction of creating low-quality documentation.

Shifting the Frame: AI as a Refactoring Architect

If the goal is self-documenting code, we need to stop treating AI as a commentator and start treating it as a senior code reviewer obsessed with readability.

We don't need AI to explain what the code is doing (we can read syntax). We need AI to help us structure the code so the what is obvious, leaving room for humans to document the why (the business context, the weird edge case that forced this specific implementation).

Here is how to change your prompting strategy to achieve clean code.

1. The "Naming Engine" Strategy

Naming things is one of the two hardest problems in computer science. When you are deep in implementation details, your brain often defaults to tempVar, dataList, or processStuff().

Instead of commenting a bad name, ask AI to suggest good ones.

Bad Prompt: "Add a comment explaining what the variable d holds."

Good Prompt: "I am using this variable d to store the difference in days between the transaction date and today to determine eligibility for a refund. Suggest 3 descriptive, camelCase names for this variable that would make a comment unnecessary."

AI Output might be:

  • daysSinceTransaction
  • refundEligibilityWindow
  • daysElapsed

Swapping d for daysSinceTransaction instantly removes the need for an explanatory comment.

2. The "Extraction Specialist" Strategy

Comments are often used to delimit sections inside a massive, monolithic function. You’ll see things like // ---- VALIDATION LOGIC ---- followed by 20 lines of if statements.

This is a cry for help. The code is begging to be extracted into a function.

Bad Prompt: "Add comments to divide this long function into logical sections."

Good Prompt: "Analyze this 50-line function. It seems to be doing too many things. Identify distinct responsibilities that could be extracted into separate, private functions. Suggest names for those functions and show me the refactored main function."

The AI will identify that lines 10-30 are handling validation and suggest extracting them into a function named validateInputRequest(). The main function becomes cleaner, easier to read, and the comment // validation logic is replaced by an actual function call that says exactly what it does.

A Practical Example: Before and After

Let's look at a typical piece of "business logic" that might exist in a codebase.

The "Before" Code (Messy)

// Calculates final price
function calc(i, u, t) {
  let p = i.basePrice;
  
  // check if user is premium and apply discount
  if (u.tier === 'gold' || u.tier === 'platinum') {
      // 15% off for VIPs
      p = p * 0.85;
  }
  
  // check tax exemption status based on territory code
  // some territories like 'OR' or 'DE' don't have sales tax on digital goods
  if (t !== 'OR' && t !== 'DE') {
      p = p * 1.10;
  }

  return p;
}

This code is riddled with single-letter variables and comments explaining things that proper naming should handle.

The Lazy AI Approach

If you ask AI to "comment this code", you'll get noise:

function calc(i, u, t) { // Function to calculate...
  let p = i.basePrice; // Initialize price to base price
  
  // If user is gold or platinum
  if (u.tier === 'gold' || u.tier === 'platinum') {
      // Multiply price by 0.85
      p = p * 0.85;
  }
  // ... etc

This is useless. It just repeats the code in English.

The Clean Code AI Approach

Let's use the prompts described above.

  1. Prompt: "Suggest better names for arguments i, u, and t in the context of a pricing function." -> item, user, territoryCode.
  2. Prompt: "Suggest a better name for variable p." -> currentPrice.
  3. Prompt: "Extract the logic inside the first if statement into a function that determines if a user gets a VIP discount."

The resulting code needs zero comments to be understood.

The "After" Code (Self-Documenting)

function calculateFinalPrice(item, user, territoryCode) {
  let currentPrice = item.basePrice;

  if (isVipUser(user)) {
      const VIP_DISCOUNT_MULTIPLIER = 0.85;
      currentPrice *= VIP_DISCOUNT_MULTIPLIER;
  }

  if (isTaxableTerritory(territoryCode)) {
      const STANDARD_TAX_MULTIPLIER = 1.10;
      currentPrice *= STANDARD_TAX_MULTIPLIER;
  }

  return currentPrice;
}

// Helper functions (likely private)
function isVipUser(user) {
    return user.tier === 'gold' || user.tier === 'platinum';
}

function isTaxableTerritory(territoryCode) {
    const taxExemptTerritories = ['OR', 'DE'];
    return !taxExemptTerritories.includes(territoryCode);
}

No comments needed.
The naming, structure, and extracted helpers explain everything.

Conclusion

The purpose of code is communication - between humans, across time. You are communicating with your future self and your teammates.

While AI tools are awe-inspiring, we must be vigilant about how we use them. Using AI to generate comments on bad code is like using a high-end label maker to organize a hoard of garbage.

Don't lower the bar just because AI makes it easy to do so. Use AI to raise your standards. Demand that it helps you write code so clean that comments become unnecessary.


Written by nikitakothari | I am a Senior Member of Technical Staff at Salesforce, where I build AI-driven enterprise solutions that integrate LLM.
Published by HackerNoon on 2025/11/27