DEV Community

sizan mahmud0
sizan mahmud0

Posted on

JavaScript Clean Code Mastery: Part 6 - Code Structure and Logic Flow That Makes Sense

Welcome to the Final Stretch!

In Part 5, we mastered arrays and immutability. Today, we're tackling the structure that makes code easy or impossible to read: logic flow and conditional statements.

I once debugged a function with 8 levels of nested if-else blocks. Eight levels. Tracing through the logic felt like navigating a maze blindfolded. Refactoring it with guard clauses cut nesting from 8 levels to 2—and made the bug obvious.

Today's Mission:

  • Use guard clauses to reduce nesting
  • Write early returns for clarity
  • Avoid deep if-else pyramids
  • Make code self-documenting
  • Eliminate unnecessary complexity

Let's flatten your logic and make it crystal clear.


Practice 1: Use Guard Clauses (Early Returns)

The Problem: Deep nesting makes code hard to follow.

❌ Bad: Nested If-Else Pyramid

function processPayment(user, amount) {
  if (user) {
    if (user.isActive) {
      if (amount > 0) {
        if (user.balance >= amount) {
          user.balance -= amount;
          sendReceipt(user, amount);
          return { success: true };
        } else {
          return { success: false, error: 'Insufficient balance' };
        }
      } else {
        return { success: false, error: 'Invalid amount' };
      }
    } else {
      return { success: false, error: 'User not active' };
    }
  } else {
    return { success: false, error: 'User not found' };
  }
}
Enter fullscreen mode Exit fullscreen mode

✅ Good: Guard Clauses (Early Returns)

function processPayment(user, amount) {
  // Guard clauses - handle invalid cases first
  if (!user) {
    return { success: false, error: 'User not found' };
  }

  if (!user.isActive) {
    return { success: false, error: 'User not active' };
  }

  if (amount <= 0) {
    return { success: false, error: 'Invalid amount' };
  }

  if (user.balance < amount) {
    return { success: false, error: 'Insufficient balance' };
  }

  // Happy path - no nesting!
  user.balance -= amount;
  sendReceipt(user, amount);
  return { success: true };
}
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • Easy to read (happy path at the end)
  • No deep nesting
  • Easy to add new validations
  • Failures exit early (faster execution)

Practice 2: Avoid Else Blocks When Possible

The Problem: Unnecessary else blocks add cognitive load.

❌ Bad: Unnecessary Else

function getDiscount(user) {
  if (user.isPremium) {
    return 0.2;  // 20% discount
  } else {
    return 0.1;  // 10% discount
  }
}

function getUserStatus(user) {
  if (user.isActive) {
    return 'Active';
  } else {
    if (user.isSuspended) {
      return 'Suspended';
    } else {
      return 'Inactive';
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

✅ Good: Early Returns (No Else Needed)

function getDiscount(user) {
  if (user.isPremium) {
    return 0.2;
  }

  return 0.1;  // Else is implied by early return
}

function getUserStatus(user) {
  if (user.isActive) return 'Active';
  if (user.isSuspended) return 'Suspended';

  return 'Inactive';
}

// Even cleaner with ternary for simple cases
const getDiscount = user => user.isPremium ? 0.2 : 0.1;
Enter fullscreen mode Exit fullscreen mode

Rule: If you return in the if-block, you don't need else.


Practice 3: Use Object Lookups for Complex Conditionals

The Problem: Long if-else chains for mapping values.

❌ Bad: If-Else Chain

function getStatusColor(status) {
  if (status === 'pending') {
    return 'yellow';
  } else if (status === 'approved') {
    return 'green';
  } else if (status === 'rejected') {
    return 'red';
  } else if (status === 'cancelled') {
    return 'gray';
  } else {
    return 'black';
  }
}

function calculateShipping(country) {
  if (country === 'USA') {
    return 5;
  } else if (country === 'Canada') {
    return 7;
  } else if (country === 'UK') {
    return 10;
  } else if (country === 'Australia') {
    return 15;
  } else {
    return 20;
  }
}
Enter fullscreen mode Exit fullscreen mode

✅ Good: Object Lookup

const STATUS_COLORS = {
  pending: 'yellow',
  approved: 'green',
  rejected: 'red',
  cancelled: 'gray'
};

function getStatusColor(status) {
  return STATUS_COLORS[status] ?? 'black';
}

const SHIPPING_RATES = {
  USA: 5,
  Canada: 7,
  UK: 10,
  Australia: 15
};

function calculateShipping(country) {
  return SHIPPING_RATES[country] ?? 20;
}

// Even better: with functions as values
const STATUS_ACTIONS = {
  pending: () => showWaitingMessage(),
  approved: () => processOrder(),
  rejected: () => sendRejectionEmail(),
  cancelled: () => refundPayment()
};

function handleStatus(status) {
  const action = STATUS_ACTIONS[status];
  if (action) {
    action();
  }
}
Enter fullscreen mode Exit fullscreen mode

Practice 4: Extract Complex Conditions to Named Functions

The Problem: Complex boolean logic is hard to understand.

❌ Bad: Complex Inline Conditions

if (user.age >= 18 && user.hasLicense && !user.hasDUI && user.yearsOfExperience >= 2) {
  allowCarRental();
}

if ((order.status === 'pending' || order.status === 'processing') && 
    order.total > 0 && 
    order.paymentMethod !== null &&
    order.customer.address !== null) {
  processOrder(order);
}
Enter fullscreen mode Exit fullscreen mode

✅ Good: Named Boolean Functions

function canRentCar(user) {
  return user.age >= 18 &&
         user.hasLicense &&
         !user.hasDUI &&
         user.yearsOfExperience >= 2;
}

if (canRentCar(user)) {
  allowCarRental();
}

function isOrderReadyToProcess(order) {
  const hasValidStatus = ['pending', 'processing'].includes(order.status);
  const hasValidTotal = order.total > 0;
  const hasPaymentMethod = order.paymentMethod !== null;
  const hasShippingAddress = order.customer.address !== null;

  return hasValidStatus && hasValidTotal && hasPaymentMethod && hasShippingAddress;
}

if (isOrderReadyToProcess(order)) {
  processOrder(order);
}
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • Self-documenting (function name explains intent)
  • Reusable (use in multiple places)
  • Testable (can unit test the condition)
  • Easy to modify (change logic in one place)

Practice 5: Use Positive Conditionals

The Problem: Negative conditionals are harder to understand.

❌ Bad: Negative Conditions

if (!user.isInactive) {
  processUser();
}

if (!order.isNotShipped) {
  trackShipment();
}

// Double negatives are even worse!
if (!user.isNotVerified) {
  grantAccess();
}
Enter fullscreen mode Exit fullscreen mode

✅ Good: Positive Conditions

if (user.isActive) {
  processUser();
}

if (order.isShipped) {
  trackShipment();
}

if (user.isVerified) {
  grantAccess();
}

// Refactor boolean names to be positive
const isActive = !user.isInactive;  // Then use isActive
const isShipped = !order.isNotShipped;  // Then use isShipped
Enter fullscreen mode Exit fullscreen mode

Practice 6: Avoid Flag Arguments

The Problem: Boolean flags make function calls unclear.

❌ Bad: Boolean Flag Parameters

function createUser(name, email, true, false);  // What do these mean?!

function sendEmail(recipient, message, true, false, true);  // Impossible to understand

function formatDate(date, false);  // Is this ISO format? UTC?
Enter fullscreen mode Exit fullscreen mode

✅ Good: Named Options or Separate Functions

// Option 1: Named parameters (object)
function createUser({ name, email, isAdmin, sendWelcomeEmail }) {
  // Clear what each parameter means
}

createUser({
  name: 'Alice',
  email: 'alice@example.com',
  isAdmin: true,
  sendWelcomeEmail: false
});

// Option 2: Separate functions
function createAdminUser(name, email) {
  return createUser({ name, email, isAdmin: true });
}

function createRegularUser(name, email) {
  return createUser({ name, email, isAdmin: false });
}

// Option 3: Enums for clarity
const EmailOptions = {
  SEND_WELCOME: 'sendWelcome',
  SEND_CONFIRMATION: 'sendConfirmation',
  NO_EMAIL: 'noEmail'
};

function createUser(name, email, emailOption) {
  if (emailOption === EmailOptions.SEND_WELCOME) {
    sendWelcomeEmail(email);
  }
}

createUser('Alice', 'alice@example.com', EmailOptions.SEND_WELCOME);
Enter fullscreen mode Exit fullscreen mode

Real-World Example: Order Validation

Before: Nested Pyramid

function validateAndProcessOrder(order) {
  if (order) {
    if (order.items && order.items.length > 0) {
      if (order.customer) {
        if (order.customer.email) {
          if (order.paymentMethod) {
            if (order.shippingAddress) {
              if (order.total > 0) {
                // Finally, the actual logic!
                return processOrder(order);
              } else {
                throw new Error('Invalid total');
              }
            } else {
              throw new Error('Missing shipping address');
            }
          } else {
            throw new Error('Missing payment method');
          }
        } else {
          throw new Error('Missing customer email');
        }
      } else {
        throw new Error('Missing customer');
      }
    } else {
      throw new Error('Cart is empty');
    }
  } else {
    throw new Error('Order not found');
  }
}
Enter fullscreen mode Exit fullscreen mode

After: Guard Clauses

function validateAndProcessOrder(order) {
  // Guard clauses - fail fast
  if (!order) {
    throw new Error('Order not found');
  }

  if (!order.items || order.items.length === 0) {
    throw new Error('Cart is empty');
  }

  if (!order.customer) {
    throw new Error('Missing customer');
  }

  if (!order.customer.email) {
    throw new Error('Missing customer email');
  }

  if (!order.paymentMethod) {
    throw new Error('Missing payment method');
  }

  if (!order.shippingAddress) {
    throw new Error('Missing shipping address');
  }

  if (order.total <= 0) {
    throw new Error('Invalid total');
  }

  // Clean, flat, easy to read
  return processOrder(order);
}
Enter fullscreen mode Exit fullscreen mode

Quick Wins Checklist for Part 6

Audit your conditional logic:

Do you have nested if statements > 2 levels? (Use guard clauses)
Do you have if-else when you return in if? (Remove else)
Do you have long if-else chains? (Use object lookups)
Are complex conditions inline? (Extract to named functions)
Do you use negative conditionals? (Flip to positive)
Do you have boolean flag parameters? (Use named options)


Part 6 Conclusion: Flat is Better Than Nested

The Zen of Python says it best: "Flat is better than nested."

Before: 8 levels of nesting (maze)
After: 0 levels of nesting (straight path)

Clean logic flow means:

  • Reading top to bottom (no jumping around)
  • Happy path at the end (obvious what should happen)
  • Failures exit early (performance bonus)
  • Easy to modify (add validations without refactoring)

Challenge: Find your deepest nested conditional. Count the levels. Refactor with guard clauses. Share the before/after nesting count! 📊


Coming Up in Part 7: Real Refactoring & Tools 🚀

The series finale covers:

  • Complete real-world refactoring (shopping cart)
  • ESLint and Prettier setup
  • Husky pre-commit hooks
  • Testing clean code
  • Series wrap-up and next steps

Transform a real messy codebase into clean code perfection!


Ready for clean logic flow? 👏 Clap for flat code! (50 claps available!)

Don't miss the finale! 🔔 Follow me - Part 7 drops in 3 days with complete refactoring!

What's your maximum nesting level? 💬 Confess in comments - we've all been there! 😄

Share the guard clause pattern! 📤 Help teammates flatten their pyramids - they'll thank you!


This is Part 6 of the 7-part "JavaScript Clean Code Mastery" series.

← Part 5: Arrays & Immutability | Part 7: Real Refactoring & Tools →

Tags: #JavaScript #CleanCode #CodeStructure #GuardClauses #ConditionalLogic #Programming #WebDevelopment #RefactoringCode

Top comments (0)