---
title: "Common Coding Bugs and How to Spot Them | Aback Games"
description: "The small bugs every programmer writes in JavaScript, Python and SQL: off-by-one loops, NULL and NaN checks, shared defaults, sorting surprises and more."
url: https://abackgames.com/guides/common-coding-bugs
---

Developer skills

# Common coding bugs and how to spot them

Off-by-one loops, NaN and NULL, falsy zero, shared defaults, sorting surprises and forgotten awaits: what goes wrong and the habit that catches each one.

Updated 1 November 2026 4 min readBy the Aback Games team

Most bugs are not clever. They are the same few slips, made in every language, that look right at a glance and quietly give the wrong answer. Learning to recognise them is one of the fastest ways to get better at reviewing and debugging code.

Each bug below is one you can practise spotting in [Bug Hunt](https://abackgames.com/games/bug-hunt), which shows short JavaScript, Python and SQL snippets with one broken line each.

## Off by one

A loop that runs once too often or once too few is the classic bug. In JavaScript, `for (let i = 0; i <= items.length; i++)` reads one past the end, where `items[i]` is undefined. In Python, `range(1, 10)` stops at 9, and the slice `items[-3:-1]` leaves out the last item.

**Habit:** check the first and last pass of every loop, and remember that ranges and slices stop before their end value.

## Comparisons that never succeed

Some values are not equal to anything, including themselves. In JavaScript, `x === NaN` is always false; use `Number.isNaN(x)`. In SQL, `phone = NULL` is never true; use `phone IS NULL`. In Python, `is` checks for the same object, so `role is "admin"` can be false even when the text is equal; use `==`.

Floating-point numbers belong here too: `0.1 + 0.2 == 0.3` is false in nearly every language. Compare floats with a tolerance, such as Python’s `math.isclose`.

## Zero is falsy

In JavaScript, `level || 50` falls back to 50 when level is 0, because 0 is falsy. The same trap catches empty strings. Use `level ?? 50`, which falls back only for null and undefined.

A relative of this bug: `if (list.indexOf(x))` is false when x is the first item (index 0) and true when it is missing (-1). Use `includes`.

## Values that are shared when they look copied

- Python creates a default argument once. `def add(tag, tags=[])` reuses the same list on every call; default to None and make the list inside.
- `[[0] * 3] * 3` in Python repeats one inner list three times, so setting one cell sets a whole column.
- A JavaScript spread, `{ ...defaults }`, copies only the top level. Nested arrays and objects are still shared.

**Habit:** whenever data is copied, ask whether anything inside it is a list or an object.

## Functions that do not return what you think

Python’s `list.sort()` sorts in place and returns None; `sorted()` returns a new list. Strings never change, so `text.upper()` on its own line does nothing useful. In JavaScript, `sort()` without a compare function sorts numbers as text, putting 100 before 25.

`["10", "10", "10"].map(parseInt)` gives `[10, NaN, 2]`, because map passes the index as parseInt’s second argument, the radix.

## Async code that is not awaited

`items.forEach(async (item) => await save(item))` does not wait for anything: forEach ignores the promises, so the code after it runs at once. Use `for...of` with await inside, or `await Promise.all(items.map(save))`.

## Closures that see the final value

A function made inside a loop sees the loop variable as it is when the function runs, not when it was made. With `var` in JavaScript, three timeouts all log 3; `let` gives each pass its own variable. Python lambdas behave the same way, so `lambda x: x * n` in a loop uses the last n; bind it with a default, `lambda x, n=n: x * n`.

## SQL that drops rows

- A condition on the right-hand table of a LEFT JOIN, written in WHERE, removes the rows with no match. Put it in the ON clause.
- `NOT IN (subquery)` returns nothing if the subquery yields a NULL.
- `COUNT(column)` skips NULLs; `COUNT(*)` counts every row.
- `BETWEEN` with a date on a timestamp column stops at midnight at the start of the last day.
- AND binds tighter than OR, so mixed conditions need parentheses.

## How to get faster at spotting bugs

Read the goal first, then look for the input that would break the code: an empty list, zero, NULL, the last item. Check every comparison operator and every boundary. And practise on short snippets where you know a bug is there; [Bug Hunt](https://abackgames.com/games/bug-hunt) gives you eight a round, with the cause and the fix after each.

## Frequently asked questions

What is the most common bug in programming?

Off-by-one errors are a strong candidate: loops, ranges and slices that include one item too many or too few. They are easy to write and easy to miss in review.

How do I find a bug that gives no error?

Reproduce it with the smallest input you can, then check each step against what you expect, by printing values or with a debugger. Silent bugs usually come from a wrong comparison, a shared value or a boundary.

Why is NULL never equal to NULL in SQL?

NULL means an unknown value, so comparing it with anything, even another NULL, gives unknown rather than true. Use IS NULL and IS NOT NULL to test for it.

## On this page

1. [Off by one](#off-by-one)
2. [Comparisons that never succeed](#comparisons-that-never-succeed)
3. [Zero is falsy](#zero-is-falsy)
4. [Values that are shared when they look copied](#values-that-are-shared-when-they-look-copied)
5. [Functions that do not return what you think](#functions-that-do-not-return-what-you-think)
6. [Async code that is not awaited](#async-code-that-is-not-awaited)
7. [Closures that see the final value](#closures-that-see-the-final-value)
8. [SQL that drops rows](#sql-that-drops-rows)
9. [How to get faster at spotting bugs](#how-to-get-faster-at-spotting-bugs)

Try it as you read

[Play Bug Hunt free](https://abackgames.com/games/bug-hunt)

## Games in this guide

Developer

### [Bug Hunt](https://abackgames.com/games/bug-hunt)

Spot the buggy line in short JavaScript, Python and SQL snippets. 8 a round, with the cause and the fix after each.

- Keyboard
- Touch

Developer

### [Regex Runner](https://abackgames.com/games/regex-runner)

Write a regular expression that matches every string on one list and none on the other. 24 puzzles, from anchors to lookarounds.

- Keyboard
- Touch

Developer

### [Code Typer](https://abackgames.com/games/code-typer)

A typing test for programmers: type real JavaScript, Python, Rust and SQL code against the clock.

- Keyboard
- Touch

## More guides

Developer skills

### [Regular expressions for beginners: a practical guide](https://abackgames.com/guides/regex-for-beginners)

The parts of a regular expression you will use every week, each with a small example, plus the mistakes that catch everyone out.

 4 min read

Developer skills

### [Git branching explained: commit, merge, rebase and reset](https://abackgames.com/guides/git-branching-explained)

What a branch actually is, what HEAD points to, and what merge, rebase, cherry-pick and reset do to the commit graph.

 4 min read

Strategy

### [Minesweeper patterns: how to read the numbers](https://abackgames.com/guides/minesweeper-patterns)

The handful of number patterns that solve most Minesweeper boards, why each one works, and what to do when logic runs out.

 5 min read

[All guides ](https://abackgames.com/guides)
