Design & Frontend Developer

Code Patterns That Actually Help

Simple patterns that make your code cleaner and easier to understand.

Return Early

Instead of nesting if statements inside each other, check for problems first and return immediately. This makes code flatter and easier to read.

Hard to read:

function processUser(user) {
  if (user) {
    if (user.email) {
      if (user.isActive) {
        return sendEmail(user.email);
      }
    }
  }
  return null;
}

Easy to read:

function processUser(user) {
  if (!user) return null;
  if (!user.email) return null;
  if (!user.isActive) return null;
  
  return sendEmail(user.email);
}

Use Objects for Choices

When you have multiple if/else conditions, use an object instead. It's cleaner and easier to update.

Multiple if statements:

function getStatusColor(status) {
  if (status === 'success') return 'green';
  if (status === 'error') return 'red';
  if (status === 'warning') return 'yellow';
  return 'gray';
}

Using an object:

const colors = {
  success: 'green',
  error: 'red',
  warning: 'yellow',
  default: 'gray'
};

function getStatusColor(status) {
  return colors[status] || colors.default;
}

Optional Chaining

Use ?. to safely access nested properties without checking if each level exists.

// Old way - checking each level
const city = user && user.address && user.address.city;

// New way - using ?.
const city = user?.address?.city;

Both do the same thing, but ?. is shorter and clearer.

Async/Await

When fetching data, use async/await instead of .then() chains. It reads more like normal code.

// Using .then() - harder to follow
fetch('/api/user')
  .then(res => res.json())
  .then(user => fetch(`/api/posts/${user.id}`))
  .then(res => res.json())
  .catch(err => console.error(err));

// Using async/await - clearer
async function loadUserPosts() {
  try {
    const userRes = await fetch('/api/user');
    const user = await userRes.json();
    
    const postsRes = await fetch(`/api/posts/${user.id}`);
    const posts = await postsRes.json();
    return posts;
  } catch (err) {
    console.error(err);
  }
}

Debounce Search

When users type in a search box, don't search on every single letter. Wait until they stop typing.

function debounce(fn, delay) {
  let timer;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn(...args), delay);
  };
}

// Search only after user stops typing for 300ms
const search = debounce((query) => {
  fetch(`/api/search?q=${query}`);
}, 300);

This saves API calls and makes your app faster.

Array Methods

JavaScript has built-in methods for working with arrays. They're easier than writing loops.

// Using a loop
const activeUsers = [];
for (let i = 0; i < users.length; i++) {
  if (users[i].isActive) {
    activeUsers.push(users[i]);
  }
}

// Using filter() method
const activeUsers = users.filter(user => user.isActive);

Other useful array methods: map(), find(), some(), every()

Check Problems First

Before doing the main work, check if everything is valid. This catches bugs early.

function createInvoice(user, items) {
  // Check for problems first
  if (!user) throw new Error('User required');
  if (!items.length) throw new Error('Need items');
  if (!user.isPremium && items.length > 10) {
    throw new Error('Free tier: 10 items max');
  }
  
  // Now safely create invoice
  return generateInvoice(user, items);
}

Keep Secrets Safe

Never put API keys or passwords directly in your code. Use environment variables instead.

// .env.local file
NEXT_PUBLIC_API_URL=https://api.example.com
API_SECRET_KEY=your-secret-key

// In your code
const apiUrl = process.env.NEXT_PUBLIC_API_URL;

This keeps secrets out of your code and makes it easy to change settings.

Better Debugging

Instead of adding console.log() everywhere, use debugger to pause and inspect.

function calculateTotal(items) {
  debugger; // Code pauses here, you can check all variables
  return items.reduce((sum, item) => sum + item.price, 0);
}

For arrays and objects, use console.table():

console.table(users); // Shows data in a nice table

Write Simple Tests

You don't need to test everything. Focus on the important parts of your code.

// Test that prevents bugs
test('cannot checkout with empty cart', () => {
  expect(() => checkout([])).toThrow();
});

// Test that math works correctly
test('10% discount on $100 = $90', () => {
  const items = [{ price: 100 }];
  expect(calculateTotal(items, 0.1)).toBe(90);
});

Tests help catch bugs before users find them.

Remember: Good code is easy to read and understand. Keep it simple!