The bugs you can see without running anything: one wrong line, or one input nobody imagined.
The story
A subscription service bills customers on the same day each month. It has run without a complaint for a year. Then, in early March, finance notices that customers who signed up on January 31 were never billed in February, and their billing day has quietly moved to the 3rd. Months later the same thing happens to customers who signed up on August 31: no charge in September, and a new billing day of the 1st.
The code is three lines:
ts
const next = newDate(lastBilled)
next.setMonth(next.getMonth() + 1)
return next
It's correct for 28 days of every month, which is why every test passed. setMonth doesn't clamp: January 31 plus one month is "February 31", which JavaScript rolls over to March 3 (or March 2 in a leap year).
No concurrency, no infrastructure, no attacker. Just one line and one input the author didn't picture. L1 and L2 bugs are the most common in any codebase, and the ones interviewers expect everyone to catch. The skill is building reflexes: a short list of questions you ask automatically of every line and every function.
Part 1: L1 reflexes — questions for every line
Ask
Because
Is this the right variable?
Copy-paste twins: start where end was meant
Is the boundary right?
< vs <=, slice(0, n - 1), "up to" vs "fewer than"
What if this is 0, "", null or undefined?
Truthiness checks and || defaults misfire on valid falsy values
Does it return / await what it computes?
Arrow functions with braces, missing await
Does it change something it was given?
sort, push, splice and property writes on inputs
What type is this really at runtime?
Query params and env vars are strings. JSON dates are strings.
What unit is this?
Seconds vs milliseconds, rupees vs paise
Does === mean what it looks like here?
Objects, arrays and Dates compare by reference
The drills below each target one family from the catalogue. Ask the reflex questions on every line.
Drill 1: Arrays
Bug hunt
Cart utilities
5 bugs planted. Click every line you think is wrong, then check.
0:00 / 3 min
Drill 2: Boundaries and comparisons
Bug hunt
Leaderboard
4 bugs planted. Click every line you think is wrong, then check.
0:00 / 3 min
Drill 3: References
Bug hunt
User settings
3 bugs planted. Click every line you think is wrong, then check.
0:00 / 3 min
Drill 4: Strings
Bug hunt
Text helpers
5 bugs planted. Click every line you think is wrong, then check.
0:00 / 3 min
Drill 5: Numbers and money
Bug hunt
Invoice maths
5 bugs planted. Click every line you think is wrong, then check.
0:00 / 4 min
Part 2: L2 reflexes — inputs nobody pictured
L2 bugs hide in functions that are right for the example in the author's head. For every function, run through this list of inputs:
Then three more questions:
State: is every transition allowed from every state it can be called in?
Errors: on the failure path, what's half-done, what's swallowed, what's retried?
Types: where does TypeScript believe something it never checked (as, !, any, JSON.parse, index lookups)?
Drill 6: Time
Bug hunt
Billing dates
5 bugs planted. Click every line you think is wrong, then check.
0:00 / 4 min
Drill 7: State
Bug hunt
Order status
5 bugs planted. Click every line you think is wrong, then check.
0:00 / 4 min
Drill 8: Error handling
Bug hunt
Profile sync
5 bugs planted. Click every line you think is wrong, then check.
0:00 / 5 min
Drill 9: TypeScript holes
Bug hunt
Plans and permissions
5 bugs planted. Click every line you think is wrong, then check.
0:00 / 5 min
Drill 10: Configuration
Bug hunt
App config
4 bugs planted. Click every line you think is wrong, then check.
0:00 / 3 min
Drill 11: Resources
Bug hunt
Leaks
3 bugs planted. Click every line you think is wrong, then check.
0:00 / 4 min
Cheat sheet
Reflex
Catches
"What if it's 0, "", null?"
Falsy checks, || defaults, missing null checks
"Does it return or await what it computes?"
Arrow-with-braces, missing await, return without await in try
"Line 3: setMonth overflows, so a customer billed on January 31 is next billed on March 3. Any subscription started on the 29th to 31st drifts. I'd clamp to the last day of the target month, and add tests for the 31st, February in leap and non-leap years, and a DST boundary."