Lesson 17 of 54

Comments and Self-Documenting Code

Self-Documenting Code Techniques

The best documentation is code that doesn't need documentation.

Self-documenting code explains itself through structure, naming, and clarity. It makes comments unnecessary by being so clear that the "what" is obvious, leaving comments only for the "why."

Technique 1: Extract Well-Named Functions

The function name is the ultimate documentation. When you extract code into a function with a good name, that name explains the intent.

Before: Code with Comment

Javascript
// Check if user is eligible for the loyalty discount
if (user.totalOrders > 10 && user.accountAge > 365 && !user.hasRecentReturn) {
  discount = 0.15;
}

After: Self-Documenting

Javascript
if (isEligibleForLoyaltyDiscount(user)) {
  discount = LOYALTY_DISCOUNT_RATE;
}

function isEligibleForLoyaltyDiscount(user) {
  const hasEnoughOrders = user.totalOrders > LOYALTY_ORDER_THRESHOLD;
  const hasOldEnoughAccount = user.accountAge > LOYALTY_TENURE_DAYS;
  const hasCleanHistory = !user.hasRecentReturn;
  
  return hasEnoughOrders && hasOldEnoughAccount && hasCleanHistory;
}

The function name documents the intent. The implementation is broken into understandable pieces.

Technique 2: Explanatory Variables

Complex expressions should be broken into named parts.

Before: Cryptic Condition

Javascript
// This is hard to parse
if (dueDate < Date.now() && !wasPaid && attempts < 3) {
  sendReminder();
}

After: Self-Explaining

Javascript
const isOverdue = dueDate < Date.now();
const isUnpaid = !wasPaid;
const hasRetriesRemaining = attempts < MAX_REMINDER_ATTEMPTS;

if (isOverdue && isUnpaid && hasRetriesRemaining) {
  sendReminder();
}

Each piece has a name that explains its meaning.

Another Example

Javascript
// Before: What is this checking?
if (order.total > 100 && order.items.length > 5 && !order.isGift) {
  // ...
}

// After: Clear intent
const qualifiesForBulkDiscount = 
  order.total > BULK_DISCOUNT_MINIMUM &&
  order.items.length > BULK_DISCOUNT_ITEM_COUNT &&
  !order.isGift;

if (qualifiesForBulkDiscount) {
  // ...
}

Technique 3: Named Constants

Magic numbers and strings are enemies of self-documenting code.

Before: Magic Numbers

Javascript
if (user.age >= 21) { }
if (password.length < 8) { }
if (retryCount > 3) { }
setTimeout(callback, 86400000);

After: Named Constants

Javascript
const LEGAL_DRINKING_AGE = 21;
const MINIMUM_PASSWORD_LENGTH = 8;
const MAXIMUM_RETRIES = 3;
const ONE_DAY_MS = 24 * 60 * 60 * 1000;

if (user.age >= LEGAL_DRINKING_AGE) { }
if (password.length < MINIMUM_PASSWORD_LENGTH) { }
if (retryCount > MAXIMUM_RETRIES) { }
setTimeout(callback, ONE_DAY_MS);

Each constant explains what the value means and why it matters.

Technique 4: Types as Documentation

In typed languages, types document the contract.

Before: Untyped

Javascript
function processOrder(order, options) {
  // What is order? What options are valid?
}

After: Typed

Typescript
interface Order {
  id: string;
  items: OrderItem[];
  customer: Customer;
  shippingAddress: Address;
}

interface ProcessOptions {
  sendConfirmationEmail: boolean;
  priority: 'normal' | 'rush' | 'overnight';
  giftWrap?: boolean;
}

function processOrder(order: Order, options: ProcessOptions): Promise<OrderResult> {
  // Contract is clear from types
}

The types document:

  • What fields exist
  • What values are valid
  • What the function returns
  • What's optional vs. required

Technique 5: Enums Over Strings/Numbers

Enums document valid options.

Before: Magic Strings

Javascript
user.status = 'A';  // What does 'A' mean?

if (order.priority === 1) { }  // Is 1 high or low?

After: Enums

Typescript
enum UserStatus {
  Active = 'ACTIVE',
  Inactive = 'INACTIVE',
  Suspended = 'SUSPENDED',
  PendingVerification = 'PENDING_VERIFICATION'
}

enum OrderPriority {
  Low = 1,
  Normal = 2,
  High = 3,
  Rush = 4
}

user.status = UserStatus.Active;

if (order.priority === OrderPriority.High) { }

Enums are self-documenting and type-checked.

Technique 6: Positive Conditionals

Negative conditions are harder to read.

Before: Double Negatives

Javascript
if (!user.isNotVerified) { }

if (!items.isEmpty()) { }

const isNotDisabled = !disabled;
if (!isNotDisabled) { }  // What?!

After: Positive Conditions

Javascript
if (user.isVerified) { }

if (items.hasItems()) { }

const isEnabled = !disabled;
if (!isEnabled) { }  // Or just: if (disabled)

Technique 7: Early Returns

Deep nesting is hard to follow. Early returns flatten the structure.

Before: Nested

Javascript
function processPayment(order) {
  if (order) {
    if (order.items.length > 0) {
      if (order.paymentMethod) {
        if (isValidPaymentMethod(order.paymentMethod)) {
          // Actually process the payment (finally!)
          return charge(order);
        } else {
          return { error: 'Invalid payment method' };
        }
      } else {
        return { error: 'No payment method' };
      }
    } else {
      return { error: 'Empty order' };
    }
  } else {
    return { error: 'No order' };
  }
}

After: Early Returns

Javascript
function processPayment(order) {
  if (!order) {
    return { error: 'No order' };
  }
  
  if (order.items.length === 0) {
    return { error: 'Empty order' };
  }
  
  if (!order.paymentMethod) {
    return { error: 'No payment method' };
  }
  
  if (!isValidPaymentMethod(order.paymentMethod)) {
    return { error: 'Invalid payment method' };
  }
  
  // Happy path is obvious
  return charge(order);
}

The structure documents the validation flow.

Technique 8: Consistent Patterns

When similar things look similar, readers understand faster.

Consistent Naming

Javascript
// Consistent pattern: isX for booleans
const isActive = user.status === 'active';
const isVerified = user.emailVerified;
const isAdmin = user.role === 'admin';

// Consistent pattern: xCount for quantities
const orderCount = orders.length;
const itemCount = items.length;
const errorCount = errors.length;

Consistent Structure

Javascript
// All validators follow the same pattern
function validateEmail(email) {
  if (!email) return { valid: false, error: 'Email required' };
  if (!EMAIL_REGEX.test(email)) return { valid: false, error: 'Invalid format' };
  return { valid: true };
}

function validatePassword(password) {
  if (!password) return { valid: false, error: 'Password required' };
  if (password.length < 8) return { valid: false, error: 'Too short' };
  return { valid: true };
}

Patterns document themselves through repetition.


Key insight: Self-documenting code uses names, types, constants, and structure to explain itself. When code is self-documenting, comments become unnecessary for explaining "what"—freeing them to explain "why."