5 min read
On this page

Inline Comments That Matter

The most misunderstood advice in software engineering is "good code is self-documenting." It is half true. Good code is self-documenting about WHAT it does. A well-named function with clear variable names tells you what it does. But code cannot tell you WHY it does it that way. Code cannot tell you about the business rule that motivated a condition, the bug that led to a workaround, or the constraint that eliminated a simpler approach. That is what comments are for.

Comment the WHY, Not the WHAT

This is the single rule that makes the difference between useful comments and noise.

Useless comment (the WHAT):
  # increment the counter
  counter += 1

Useful comment (the WHY):
  # We count retries separately from attempts because the billing
  # system charges per attempt, and we need to reconcile the invoice
  # against actual charges, not including retries.
  retry_count += 1

The first comment repeats what the code says. A reader who can read code gains nothing from it. The second comment explains a business decision that cannot be inferred from the code. A reader who can read code gains crucial context.

More examples:

  Useless: # check if user is admin
  if user.role == "admin":

  Useful: # Admins bypass the rate limiter because their automated
  # scripts hit this endpoint 500x/hour during batch imports,
  # which would trigger the rate limiter. See incident #892.
  if user.role == "admin":

  Useless: # return early if no items
  if not items:
      return []

  Useful: # Empty cart is a valid state during checkout if the user
  # removed all items. We return early instead of raising because
  # the frontend handles the empty state with a specific UI.
  if not items:
      return []

When to Comment

Comments are appropriate in specific situations. Not everywhere, and not nowhere.

Business Logic That Is Not Obvious

# Discounts are applied before tax in all US states except
# Colorado, where tax is calculated on the pre-discount amount
# per state tax code 39-26-713.
if state == "CO":
    tax = calculate_tax(subtotal)
    total = subtotal - discount + tax
else:
    discounted = subtotal - discount
    tax = calculate_tax(discounted)
    total = discounted + tax

Without this comment, a future engineer might "simplify" the Colorado special case, introducing a tax compliance bug.

Workarounds and Known Issues

# WORKAROUND: The Stripe API returns a 200 with an error in the
# body (instead of a 4xx) when the card's issuing bank is
# unreachable. We check the response body even on 200s.
# Stripe ticket: #STR-44821, open since 2024-03.
if response.status_code == 200 and "error" in response.json():
    handle_stripe_error(response)

Without this comment, someone will reasonably conclude that checking for errors on a 200 is a bug and remove it. The comment explains that it is a workaround for a specific third-party behavior, with a reference for follow-up.

Performance Decisions

# We use a dict lookup here instead of a list comprehension because
# this function is called 50,000 times per request in the
# recommendation engine. The dict approach is O(1) per lookup
# vs O(n) for the list scan, which reduced p99 latency from
# 340ms to 12ms. See benchmark: benchmarks/recommendation_perf.py
result = lookup_table.get(item_id)

The simpler list comprehension would be more readable. This comment explains why the less-readable approach was chosen, with evidence.

Non-Obvious Constraints

# This timeout MUST be longer than the payment provider's
# internal timeout (30s) plus network latency buffer (5s).
# If our timeout fires first, we lose track of whether the
# charge succeeded, creating a potential double-charge.
# Minimum safe value: 40s. Current setting: 45s.
PAYMENT_TIMEOUT = 45

Without this comment, someone might reduce the timeout to 15 seconds "to fail faster." That change could cause double charges.

Regular Expressions

Regular expressions are almost never self-documenting. Always comment them:

# Match US phone numbers in formats:
# (555) 123-4567, 555-123-4567, 5551234567, +1-555-123-4567
# Does NOT match extensions (handled separately)
PHONE_REGEX = r"(?:\+1[-.]?)?\(?(\d{3})\)?[-.\s]?(\d{3})[-.\s]?(\d{4})"

Magic Numbers and Thresholds

# Circuit breaker opens after 5 consecutive failures.
# This threshold was calibrated against production traffic patterns:
# - Normal error rate: ~0.1% (1 in 1000)
# - Provider outage: >80% errors
# - 5 consecutive failures has a <0.001% chance of occurring during
#   normal operations, so false triggers are extremely rare.
CIRCUIT_BREAKER_THRESHOLD = 5

When NOT to Comment

Comments that restate the code are worse than no comments because they add visual noise and can become stale (the code changes, the comment does not).

Do not comment:
  # Get the user from the database
  user = db.get_user(user_id)

  # Loop through all orders
  for order in orders:

  # Initialize the list
  results = []

  # Set the name
  self.name = name

  # Return the result
  return result

All of these comments say exactly what the code says. They add no information. A codebase full of these comments trains engineers to skip all comments, including the useful ones.

Self-Documenting Alternatives

Often, the right response to "this needs a comment" is to improve the code instead:

# Bad: needs a comment because the code is unclear
# Check if the order is eligible for free shipping
if order.total > 50 and order.weight < 20 and order.destination.country == "US":

# Better: self-documenting, no comment needed
if order.qualifies_for_free_shipping():

# The logic is now inside a well-named method where it can have
# its own tests and the rules can be understood in isolation

If a comment explains WHAT the code does, consider making the code clearer instead. If a comment explains WHY, it should stay as a comment — no amount of good naming can explain business context.

Comment Maintenance

Stale comments are actively harmful. A comment that says "we do X because of Y" when the code no longer does X misleads every reader.

Strategies for keeping comments current:
  1. Comment close to the code they describe (not 50 lines away)
  2. Reference tickets, PRs, or incidents (verifiable context)
  3. During code review, check if changed code has stale comments
  4. Prefer explaining invariants over explaining implementation
     (invariants change less often than implementation)
  5. Delete comments when the code they explain is deleted

Invariant Comments vs Implementation Comments

Implementation comment (fragile, breaks when code changes):
  # We iterate backwards because items are sorted by priority
  # and we want the lowest priority first
  for i in range(len(items) - 1, -1, -1):

Invariant comment (durable, explains a rule):
  # Items must be processed in ascending priority order.
  # Lowest priority number = highest urgency.
  for item in sorted(items, key=lambda x: x.priority):

The invariant comment describes a rule that must hold regardless of implementation. If someone refactors the loop, the invariant comment is still correct. The implementation comment would become stale.

TODO, FIXME, HACK, and XXX

Marker comments are useful when used consistently and when they are actually addressed:

# TODO: add retry logic when the payment provider stabilizes their API
#       Expected: Q2 2026. Ticket: PAY-1234

# FIXME: this query times out on tables with >1M rows.
#        Needs an index on (user_id, created_at). Ticket: DB-567

# HACK: working around a bug in library v2.3.
#       Remove when we upgrade to v3.x. Ticket: DEPS-89

# XXX: this is a security concern. The token is logged in plaintext.
#      Must fix before the next security audit. Ticket: SEC-42

Rules for marker comments:

  • Always include a ticket reference (so someone can follow up)
  • Include context about when or why it should be fixed
  • Periodically grep for these markers and address or remove them
  • If a TODO has been there for 2 years, either do it or delete it

Real-World Example: The Comment That Prevented a Disaster

A fintech company processed millions of transactions daily. Deep in the payment processing code was this function:

def round_amount(amount):
    # DO NOT change to standard rounding (round half up).
    # We use "banker's rounding" (round half to even) because:
    # 1. Our banking partners require IEEE 754 compliant rounding
    # 2. Standard rounding introduces a systematic upward bias
    #    when processing millions of transactions
    # 3. The SEC requires this for certain financial calculations
    # Changing this will cause reconciliation failures with
    # partner banks and potential regulatory issues.
    # See: compliance review 2024-Q3, legal ticket LEG-1182
    return decimal_round(amount, 2, ROUND_HALF_EVEN)

A junior engineer was refactoring the payment module and was about to replace decimal_round with Python's built-in round() for "simplicity." The comment stopped them. Without it, the change would have passed code review (it looked correct), shipped to production, and caused reconciliation failures with banking partners — potentially a seven-figure problem.

Common Pitfalls

  • Commenting the WHAT — "increment counter," "return the result," "loop through items." These add noise and train readers to ignore all comments. Comment the WHY, not the WHAT.
  • Never commenting anything — "clean code does not need comments" is a misinterpretation. Clean code does not need comments that explain what it does. It still needs comments that explain why it does it.
  • Letting comments go stale — a wrong comment is worse than no comment. Review comments during code review, delete them when they no longer apply, and prefer invariant comments over implementation comments.
  • Commenting instead of improving the code — if a comment explains WHAT the code does, the code is probably unclear. Improve the names, extract a function, or simplify the logic. Reserve comments for WHY.
  • Writing TODOs without tickets — a TODO without a tracking ticket will never be done. It will sit in the code forever, accumulating guilt. Add a ticket reference or do not write the TODO.

Key Takeaways

  • Comment the WHY, never the WHAT. Code tells you what it does. Comments should tell you why it does it that way — the business rules, the constraints, the workarounds, the performance reasons.
  • The best comments explain things that cannot be expressed in code: business context, regulatory requirements, third-party quirks, and the reasoning behind non-obvious decisions.
  • Self-documenting code replaces WHAT comments. If you need to explain what the code does, improve the code instead of adding a comment. Extract functions, name variables well, use clear types.
  • Keep comments close to the code they describe, reference tickets and incidents for context, and delete comments when they become stale. Wrong comments are worse than no comments.
  • Always comment regular expressions, magic numbers, workarounds, and performance-critical decisions. These are the areas where future engineers are most likely to break things without context.