It was a regular Tuesday. I deployed a small change to our user API — just a minor refactor to clean up some duplicate code. Three hours later, my phone started buzzing. Customers were seeing wrong profile data.
The Symptom
Support tickets started flooding in: “Why is my email showing as someone else’s?” Users were seeing each other’s information. Not all of them — just some. That made it worse because we couldn’t reproduce it reliably at first.
The Investigation
I rolled back immediately. The problem stopped. Good. But I needed to know what went wrong.
I spent the next hour staring at my code. The change was simple — I extracted a helper function to format user data. Here’s what I wrote:
function formatUser(user) {
return {
name: user.name,
email: user.email,
id: user.id
};
}
And here’s how I was calling it:
const user = getUserFromDb(userId);
const formatted = formatUser(user);
// ... later in the code
console.log(user.email); // wrong!
The Root Cause
JavaScript objects are passed by reference. My formatUser function wasn’t creating a new object — it was mutating the original user object. When multiple requests came in simultaneously, they were sharing the same object in memory.
I fixed it by spreading the object:
function formatUser(user) {
return {
...user,
name: user.name,
email: user.email,
id: user.id
};
}
What I Learned
- Always create new objects in functions that claim to “format” or “transform” data
- Test concurrent requests — this bug only appeared under load
- Use Object.freeze() if you want to catch accidental mutations early
The fix took 30 seconds. The debugging took 3 hours. That’s usually how it goes.
