DEV Community

Cover image for 5 JavaScript Patterns I've Seen Senior Developers Use (And Why They Matter)
Priyanshi Jain
Priyanshi Jain

Posted on

5 JavaScript Patterns I've Seen Senior Developers Use (And Why They Matter)

My code used to work... but it wasn't good. Through pair programming and countless PR comments, I started noticing patterns that separated beginner code from production-ready code.

Pattern 1: Early Returns (Guard Clauses)

How I used to write it:

function processUser(user) {
  if (user) {
    if (user.isActive) {
      if (user.hasPermission) {
        // actual logic here
        return doSomething(user);
      } else {
        return null;
      }
    } else {
      return null;
    }
  } else {
    return null;
  }
}
Enter fullscreen mode Exit fullscreen mode

What I learned from seniors:

function processUser(user) {
  if (!user) return null;
  if (!user.isActive) return null;
  if (!user.hasPermission) return null;

  return doSomething(user);
}
Enter fullscreen mode Exit fullscreen mode

💡 Why it matters:

  1. Reduces nesting - No more "pyramid of doom"
  2. Improves readability - Each condition is clear and isolated
  3. Easier to maintain - Adding/removing conditions is simple
  4. Lower mental load - You can scan the guards quickly

Don't over-engineer simple logic.

Pattern 2: Object Lookup Instead of Switch/If-Else Chains

❌ How I used to write it:

function getStatusMessage(status) {
  if (status === 'pending') {
    return 'Your order is pending';
  } else if (status === 'processing') {
    return 'We are processing your order';
  } else if (status === 'shipped') {
    return 'Your order has been shipped';
  } else if (status === 'delivered') {
    return 'Your order has been delivered';
  } else {
    return 'Unknown status';
  }
}
Enter fullscreen mode Exit fullscreen mode

✅ What I learned from seniors:

const STATUS_MESSAGES = {
  pending: 'Your order is pending',
  processing: 'We are processing your order',
  shipped: 'Your order has been shipped',
  delivered: 'Your order has been delivered'
};

function getStatusMessage(status) {
  return STATUS_MESSAGES[status] || 'Unknown status';
}
Enter fullscreen mode Exit fullscreen mode

💡 Why it matters:

  • More scalable - Adding new statuses is just one line
  • Better performance - O(1) lookup vs O(n) with if-else chains

Pattern 3: Optional Chaining & Nullish Coalescing

❌ How I used to write it:

function getUserCity(user) {
  if (user && user.address && user.address.city) {
    return user.address.city;
  }
  return 'Unknown';
}

const limit = config.limit !== null && config.limit !== undefined 
  ? config.limit 
  : 10;
Enter fullscreen mode Exit fullscreen mode

✅ What I learned from seniors:

function getUserCity(user) {
  return user?.address?.city ?? 'Unknown';
}

const limit = config.limit ?? 10;
Enter fullscreen mode Exit fullscreen mode

💡 Why it matters:

  1. ?. safely accesses nested properties - No more undefined errors
  2. ?? only defaults on null/undefined - Preserves 0, false, and '' as valid values
  3. Modern JavaScript - Supported in all modern browsers (2020+)

Pattern 4: Destructuring with Defaults

❌ How I used to write it:

function createUser(options) {
  const name = options.name || 'Anonymous';
  const age = options.age || 18;
  const role = options.role || 'user';

  return { name, age, role };
}

Enter fullscreen mode Exit fullscreen mode

✅ What I learned from seniors:

function createUser({ 
  name = 'Anonymous', 
  age = 18, 
  role = 'user' 
} = {}) {
  return { name, age, role };
}
Enter fullscreen mode Exit fullscreen mode

💡 Why it matters:

  • Cleaner function signature - Parameters are self-documenting
  • Handles missing object - The = {} prevents errors if nothing is passed
  • Avoids falsy value issues - Same problem as Pattern 3 with ||

Pattern 5: Composition Over Complex Conditionals

❌ How I used to write it:

function processData(data, shouldValidate, shouldTransform, shouldLog) {
  let result = data;

  if (shouldValidate) {
    result = validate(result);
  }

  if (shouldTransform) {
    result = transform(result);
  }

  if (shouldLog) {
    console.log(result);
  }

  return result;
}
Enter fullscreen mode Exit fullscreen mode

✅ What I learned from seniors:

const pipe = (...fns) => (value) => 
  fns.reduce((acc, fn) => fn(acc), value);

const processData = pipe(
  validate,
  transform,
  log
);

// Usage
const result = processData(data);

Enter fullscreen mode Exit fullscreen mode

💡 Why it matters:

  • Single responsibility - Each function does one thing
  • Easy to modify - Add/remove/reorder steps without touching logic

I'm still learning every day. If you're a senior developer reading this, what patterns would you add? If you're early in your journey like me, which of these resonates most with you?

Let's learn together in the comments. 👇

Top comments (0)