DEV Community

Timevolt
Timevolt

Posted on

Writing Code That Doesn't Need Comments: A Gandalf-Level Guide

The Quest Begins (The "Why")

I still remember my first real job fresh out of college. I was handed a legacy codebase that looked like a scroll written in ancient runes — every function was a wall of comments trying to explain what the code actually did. I’d spend hours reading a comment like “// increment the counter because we need to skip the header row” only to find the variable named i and the loop buried three levels deep.

One afternoon, after yet another bug turned out to be a mismatch between a comment and the actual logic, I felt like I was stuck in a loop of frustration. The comments weren’t helping; they were lying. I realized I was treating comments as a crutch instead of fixing the root problem: the code wasn’t speaking for itself.

That moment sparked my quest: write code so clear that comments become unnecessary.

The Revelation (The Insight)

The treasure I uncovered isn’t a secret syntax or a new framework — it’s a mindset shift: make the code self‑documenting. When you name things well, break logic into tiny, focused pieces, and let the structure tell the story, the need for explanatory comments evaporates.

Why does this work?

  1. Names are the first line of communication – a good name tells the reader what something is and why it exists.
  2. Small functions do one thing – when a function’s purpose fits on a single line, you don’t need a paragraph to explain it.
  3. Structure reveals intent – nested conditionals, early returns, and clear flow guide the reader through the logic without a tour guide.

When you rely on comments to explain bad naming or tangled logic, you create a maintenance liability. Comments drift. Code changes. Comments stay stale, and the next developer (or future you) ends up debugging a mismatch between what the comment says and what the code does.

Wielding the Power (Code & Examples)

Let’s look at a real‑world snippet I once saw in a payment‑processing module.

Before – the comment‑heavy struggle

// Calculate the total amount due for an order
// Includes tax, shipping, and applies any discount
function calculateOrderTotal(items, taxRate, shippingCost, discount) {
  // Start with the sum of all item prices
  let subtotal = 0;
  for (let i = 0; i < items.length; i++) { // iterate over each item
    subtotal += items[i].price * items[i].quantity; // price * qty
  }

  // Compute tax based on subtotal
  let tax = subtotal * taxRate; // tax = subtotal * rate

  // Add shipping cost
  let totalWithShipping = subtotal + tax + shippingCost; // subtotal + tax + shipping

  // Apply discount (if any)
  let discountAmount = totalWithShipping * (discount / 100); // discount percent
  let finalTotal = totalWithShipping - discountAmount; // subtract discount

  // Ensure we don't return a negative total
  if (finalTotal < 0) {
    finalTotal = 0; // floor at zero
  }

  return finalTotal; // return the amount to charge
}
Enter fullscreen mode Exit fullscreen mode

Read it. The comments are basically a translation of each line. If someone changes the tax calculation but forgets to update the comment, the documentation becomes misleading. Worse, the variable names (subtotal, tax, totalWithShipping) are okay, but the function does too much in one block, making it hard to grasp the overall intent at a glance.

After – the self‑documenting victory

function calculateOrderTotal(items, taxRate, shippingCost, discountPercent) {
  const subtotal = sumItemPrices(items);
  const tax = subtotal * taxRate;
  const discountedSubtotal = applyDiscount(subtotal + tax, discountPercent);
  const totalWithShipping = discountedSubtotal + shippingCost;
  return Math.max(totalWithShipping, 0);
}

// Helper: pure, single‑purpose, and self‑explaining
function sumItemPrices(items) {
  return items.reduce((total, item) => total + item.price * item.quantity, 0);
}

// Helper: apply a percentage discount, guarding against negative results
function applyDiscount(amount, percent) {
  const discount = amount * (percent / 100);
  return amount - discount;
}
Enter fullscreen mode Exit fullscreen mode

What changed?

  • Names now convey what each step does (sumItemPrices, applyDiscount). No comment needed to tell you that we’re adding up item prices.
  • Each function does one thing – the main calculateOrderTotal reads like a high‑level recipe: get subtotal, add tax, apply discount, add shipping, guard against negatives.
  • Helpers are reusable and testable – you can unit‑test sumItemPrices in isolation, which was impossible when it was hidden inside a monolithic block.

The code now tells its own story. If I need to know how the discount is calculated, I look at applyDiscount. If I wonder why we never return a negative, I see the Math.max guard at the end. No comment required, and therefore no comment can drift out of sync.

A second quick win – conditionals

Before:

// If the user is admin OR (has permission AND is active)
if (user.role === 'admin' || (user.permissionLevel >= 5 && user.isActive)) {
  grantAccess();
}
Enter fullscreen mode Exit fullscreen mode

After:

function userCanAccess(user) {
  return isAdmin(user) || (hasSufficientPermission(user) && user.isActive);
}

if (userCanAccess(user)) {
  grantAccess();
}

// Tiny predicates – self‑explanatory
function isAdmin(user) { return user.role === 'admin'; }
function hasSufficientPermission(user) { return user.permissionLevel >= 5; }
Enter fullscreen mode Exit fullscreen mode

Now the if reads like a sentence: “If the user can access, grant access.” The logic is hidden behind well‑named predicates, making the intent instantly clear.

Why This New Power Matters

Adopting this habit has changed how I work in three concrete ways:

  1. Fewer bugs from stale documentation – I no longer waste time reconciling a comment that contradicts the code.
  2. Faster onboarding – New teammates can dive into a function and understand its purpose without hunting through comment blocks.
  3. Confident refactoring – When the code speaks for itself, I can safely extract helpers, rename variables, or reorder steps, knowing the meaning stays intact.

Think of it like upgrading from a paper map to a GPS with voice guidance. The map (comments) can get torn, outdated, or misread. The GPS (self‑describing code) recalculates automatically when the terrain changes.

Your Turn – Embark on Your Own Quest

Pick a function you’ve written recently that leans heavily on comments. Strip those comments out, then rename everything so the purpose is obvious without them. If you feel the urge to add a comment back, ask yourself: Is the name unclear? Is the block doing too much? Fix those, and watch the comment disappear.

Give it a try in your next pull request and notice how the code feels lighter, cleaner, and more alive.

Happy coding, and may your functions always be as clear as a wizard’s spell! 🚀

Top comments (0)