What they are, why they matter, and how to manage them for clean, predictable code.

Introduction
Every JavaScript app has side effects. You can’t build anything useful without them. Updating the DOM, fetching data, writing to a database, logging errors — all are side effects.
But here’s the kicker: uncontrolled side effects make your code unpredictable, hard to test, and full of hidden bugs. Controlled side effects make your code reliable, testable, and easier to maintain.
In this article, we’ll demystify side effects in JS with practical examples, real-world scenarios (frontend + Node.js), common pitfalls, and strategies for keeping them under control.
1. What Is a Side Effect?
A side effect is any observable change outside of a function’s scope.
👉 If a function changes something beyond returning a value, that’s a side effect.
Examples:
- Modifying a global variable.
- Writing to the DOM.
- Logging to the console.
- Fetching data from an API.
- Writing a file.
- Generating random numbers.
- Using the current date/time.
Pure vs Impure Functions Recap
- Pure: No side effects, same input → same output.
- Impure: Causes side effects or depends on external state.
2. Side Effects in the Browser
DOM Manipulation
function addItem(text) {
const li = document.createElement("li");
li.textContent = text;
document.querySelector("ul").appendChild(li); // side effect
}
Event Handling
button.addEventListener("click", () => {
alert("Clicked!"); // side effect (UI, global alert box)
});
Local Storage
function saveToken(token) {
localStorage.setItem("auth_token", token); // side effect
}
3. Side Effects in Node.js
File I/O
const fs = require("fs");
function saveData(data) {
fs.writeFileSync("data.json", JSON.stringify(data)); // side effect
}
Database Queries
async function getUser(id) {
return db.findById(id); // side effect: DB query
}
Logging
function log(message) {
console.log(message); // side effect: writes to stdout
}
4. Why Side Effects Are Necessary
Without side effects, your app can’t:
- Render UI.
- Fetch or save data.
- Communicate with APIs.
- Provide real interactivity.
👉 The goal isn’t to eliminate side effects. It’s to control and isolate them.
5. Why Side Effects Are Dangerous
- Hard to test: You need mocks/spies.
- Unpredictable: Depends on external state (e.g.,
Date.now()
). - Hidden mutations: Mutating objects/arrays in place breaks expectations.
- Race conditions: When multiple async side effects interact unpredictably.
Example bug:
let cart = [];
function addToCart(item) {
cart.push(item); // mutates external state
}
Calling addToCart
changes global state—hard to predict in large apps.
6. Common Side Effect Pitfalls
a) Mutating Arguments
function addItem(arr, item) {
arr.push(item); // ❌ mutates input
return arr;
}
Fix (pure):
function addItem(arr, item) {
return [...arr, item];
}
b) Using Randomness or Time
function getOrderId() {
return Math.random(); // ❌ impure, unpredictable
}
Fix: Inject randomness/time as dependency for testability.
function getOrderId(randomFn = Math.random) {
return randomFn();
}
c) Hidden Async Effects
function fetchData() {
let result;
fetch("/api/data").then(r => result = r.json()); // ❌ async side effect not exposed
return result; // undefined
}
Fix: Return the promise.
function fetchData() {
return fetch("/api/data").then(r => r.json());
}
7. Managing Side Effects in Practice
Strategy 1: Functional Core, Imperative Shell
- Core = pure functions (calculations, validation, transforms).
- Shell = impure functions (I/O, DOM, API calls).
// Pure
function calculateTotal(cart) {
return cart.reduce((sum, i) => sum + i.price * i.qty, 0);
}
// Impure
function checkout(cart) {
const total = calculateTotal(cart);
console.log("Charging $" + total); // side effect
}
Strategy 2: Isolate Side Effects
Wrap side effects so they’re easy to swap/mocks in tests.
function apiFetch(url, options) {
return fetch(url, options);
}
// usage
const data = await apiFetch("/api/user");
Now you can replace apiFetch
with a mock in tests.
Strategy 3: Declarative Frameworks
React, Redux, Vue — designed to reduce manual side effects.
- React components are mostly pure (render output based on props).
- Side effects live in
useEffect
or actions.
useEffect(() => {
fetch("/api/data").then(setData); // isolated side effect
}, []);
Strategy 4: Async Patterns
- Use Promise chains or
async/await
to structure async side effects clearly. - Centralize error handling with
.catch
ortry/catch
. - Limit concurrency with
Promise.allSettled
,Promise.any
, or custom pools.
8. Testing Side Effects
Pure Function
test("calculateTotal", () => {
expect(calculateTotal([{ price: 10, qty: 2 }])).toBe(20);
});
Easy — no mocks.
Impure Function
test("logs checkout", () => {
const spy = jest.spyOn(console, "log").mockImplementation(() => {});
checkout([{ price: 10, qty: 2 }]);
expect(spy).toHaveBeenCalledWith("Charging $20");
spy.mockRestore();
});
Requires mocks/spies.
9. Async Side Effects: Real-World Examples
React State
setCount(count + 1); // side effect: schedules a re-render
API Request with Timeout
const withTimeout = (p, ms) =>
new Promise((res, rej) => {
const t = setTimeout(() => rej(new Error("Timeout")), ms);
p.then(v => { clearTimeout(t); res(v); },
e => { clearTimeout(t); rej(e); });
});
await withTimeout(fetch("/api/data"), 5000);
Node Server Request
http.createServer((req, res) => {
res.writeHead(200);
res.end("Hello World"); // side effect
}).listen(3000);
10. Quick Reference: Side Effect Do’s and Don’ts

Conclusion
Side effects are unavoidable — but they don’t have to be unmanageable. The key is to keep core logic pure and isolate side effects at the edges. That way:
- Your logic stays predictable.
- Your tests stay simple.
- Your side effects are easier to control, monitor, and debug.
Pro tip: Next time you write a function, ask: Does this only compute and return a value, or does it also change something outside? That’s how you spot side effects — and decide where they belong.
Call to Action
What’s the trickiest side effect you’ve wrestled with — stale closures, async API calls, or hidden DOM mutations?
💬 Share your story in the comments.
🔖 Bookmark this as your side-effect survival guide.
👩💻 Share with a teammate who thinks console.log
isn’t a side effect.
Leave a Reply