Blog #41: The 2 AM Panic and Infinite Question Marks
Looking back at the time I almost cried over a 'Cannot read property of undefined' error and a lesson in defensive programming.
To me in my early years of working,
Do you remember that June night? The night your phone buzzed incessantly at 2 AM because a customer's payment page went completely white (White Screen of Death). You opened your laptop with trembling hands, staring at a console full of bright red errors: Uncaught TypeError: Cannot read property 'address' of undefined.
At that moment, your world felt like it was COLLAPSING. You told yourself: "I tested it so thoroughly locally, how could this happen?".
A Simple but Fatal Problem
To put it most simply: Your project was trying to open a drawer (property) of a cabinet (object) that... didn't exist. In JavaScript, when you access deep inside an object without checking if the upper layers exist, the browser immediately "goes on strike" and stops all operations.
Current Perspective: The Naivety of Trust
Looking back now, I see I was so naive to trust the "promise" of API data. You assumed that if the API returned a user, there would surely be a profile, and if there was a profile, there would surely be an address.
The truth is that in a production environment, anything can happen: An old record missing a data field, a database error causing a return value of null, or simply a slight change on the Backend that you hadn't updated yet.
// How you wrote it then (naive)
const city = user.profile.address.city;
// How juniors often do it (patchwork)
const city = user && user.profile && user.profile.address && user.profile.address.city;
A common junior mistake is believing that the API is always 100% correct. When encountering this error, you often used a "patchwork" of long && chains. It's not technically wrong, but it makes the code extremely dirty and hard to maintain.
Production vs Local
Why does this error rarely appear locally? Because test data locally is usually "clean" and perfect. But in Production, data is "trash"—it's whatever Users have entered over the last 5 years. That's where the durability of the code is verified.
Comparison of Solutions:
- Quick fix: Use
&&operators or nestedif/elseeverywhere to "block" errors. - Sustainable way: Use Optional Chaining (
?.) combined with Nullish Coalescing (??) to ensure there's always a default value. Further, use a schema validator likeZodto check data as soon as it leaves the API.
// The sustainable way now
const city = user?.profile?.address?.city ?? 'Unknown City';
Practical Lesson
Never access deeply nested data without "insurance." Get into the habit of Defensive Programming. Treat external data as something that cannot be 100% trusted until you've verified it or assigned a default value.
A small question mark ? can save your sleep.
Notes on caution before null values.
Series • Part 41 of 50