HomeBytesheetsRoadmapsUI TemplatesUI ToolsStack Discussion
4 Topics
Chapter•65 Minutes

Production JavaScript Patterns

Apply JavaScript knowledge to real applications through immutability, composition, dependency injection, state management, concurrency control, and maintainable architecture.

Immutability and Structural Updates

Immutability means treating existing data as unchanged and producing new values when state changes. JavaScript does not make objects immutable by default, so application code must choose appropriate techniques.

Immutability is particularly valuable in state management because reference identity can efficiently signal that something changed.

Immutable Object Update
const user = {
  name: 'Suriya',
  preferences: {
    theme: 'light'
  }
};

const updatedUser = {
  ...user,
  preferences: {
    ...user.preferences,
    theme: 'dark'
  }
};

console.log(user.preferences.theme);
console.log(updatedUser.preferences.theme);
💡 The original nested object remains unchanged. New objects are created along the path that changed.
Key Takeaways
  • Spread syntax creates shallow copies.
  • Nested updates require copying each changed level.
  • Immutability makes state transitions easier to reason about.
Knowledge Check

Does object spread perform a deep clone?

Dependency Injection with Plain JavaScript

Dependency injection means providing dependencies to a component rather than having it construct them internally. This improves testability, flexibility, and separation of concerns.

JavaScript's functions and objects make dependency injection straightforward without requiring a framework.

Injecting a Repository
function createUserService(userRepository) {
  return {
    async getUser(id) {
      return userRepository.findById(id);
    }
  };
}

const service = createUserService({
  findById: async id => ({ id, name: 'Suriya' })
});
💡 The service does not create the repository. A caller supplies it, making the service easier to test with a fake repository.
Key Takeaways
  • Dependency injection separates construction from behavior.
  • Functions can receive dependencies directly.
  • Injected dependencies simplify unit testing.
Knowledge Check

What is the primary benefit of dependency injection?

Controlling Async Concurrency

Starting thousands of asynchronous operations simultaneously can overwhelm APIs, memory, sockets, or downstream systems. Concurrency limiting restricts how many operations are active at once.

Promise.all is useful for independent work but should not automatically be used for unbounded collections. Production systems often need batching, queues, backpressure, retries, timeouts, and cancellation.

Simple Batch Processing
async function processInBatches(items, size, worker) {
  const results = [];

  for (let i = 0; i < items.length; i += size) {
    const batch = items.slice(i, i + size);
    results.push(...await Promise.all(batch.map(worker)));
  }

  return results;
}

const results = await processInBatches(items, 10, processItem);
💡 Only one batch of at most ten operations is active at a time, reducing pressure on external resources.
Key Takeaways
  • Unlimited Promise concurrency can overload systems.
  • Batching is a simple concurrency-control strategy.
  • Production async systems often require backpressure and cancellation.
Knowledge Check

Why can blindly using Promise.all over thousands of items be problematic?

Pure Functions and Side Effects

A pure function produces the same output for the same inputs and does not cause observable side effects. Pure functions are easier to test, cache, compose, and reason about.

Side effects are not inherently bad; applications need I/O, logging, network calls, DOM changes, and state updates. The important architectural technique is to isolate side effects from deterministic business logic where practical.

Pure vs Side-Effecting Code
function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.price, 0);
}

function saveOrder(order) {
  return fetch('/api/orders', {
    method: 'POST',
    body: JSON.stringify(order)
  });
}
💡 calculateTotal is deterministic for the same input and has no external effect. saveOrder interacts with the network and therefore has a side effect.
Key Takeaways
  • Pure functions are deterministic and side-effect free.
  • Side effects are necessary but should be controlled.
  • Separating calculation from I/O improves testability.
Knowledge Check

Which function is most likely pure?