What you'll be able to do

By the end of this page you should be able to scan your code against a short beginner-bug checklist and recognize several failure patterns before they waste an afternoon.

Previous post: debugging with console and breakpoints.

Who this is for

  • People mid-project who keep hitting "weird" behavior
  • Students reviewing homework before submit
  • Anyone who wants a printable mental list, not theory

You can skip this if these already feel obvious — skim for gaps, then move on.

Checklist (use it like a runway walk)

1. Types: string vs number

"10" + 20; // "1020"
Number("10") + 20; // 30

Scenario A — form values are strings until you convert.

Scenario B — API JSON numbers are fine; query params are strings.

2. Off-by-one in loops and slices

for (let i = 0; i <= arr.length; i++) {
  /* last i is out of range */
}

Scenario A — <= length vs < length.

Scenario B — slice end index is exclusive — easy to drop the last item.

3. DOM null

document.getElementById("missing").textContent = "x"; // throws

Scenario A — script in <head> before the element exists.

Scenario B — typo in id.

4. Async timing

let data;
fetch("/api").then((r) => r.json()).then((d) => (data = d));
console.log(data); // undefined still

Scenario A — using data before the promise settles.

Scenario B — UI updates inside then/await, not on the next line after starting fetch.

5. Mutation surprises

const a = [1, 2];
const b = a;
b.push(3); // a also changes

Scenario A — "copied" array that was only a reference.

Scenario B — sorting in place and losing the original order.

6. == vs ===

Scenario A — 0 == "" is true with ==.

Scenario B — prefer === unless you have a rare intentional coercion.

A tiny practice file

<!DOCTYPE html>
<html>
  <body>
    <ul id="list"></ul>
    <script>
      const items = ["a", "b", "c"];
      const list = document.getElementById("list");
      // Fix the off-by-one if you spot it
      for (let i = 0; i < items.length; i++) {
        const li = document.createElement("li");
        li.textContent = items[i];
        list.appendChild(li);
      }
    </script>
  </body>
</html>

Break it on purpose with i <= items.length and watch the console.

Mistakes I see a lot

1. Blaming the framework when the bug is a string total or a null node.

2. Fixing one checklist item and not re-testing neighbors (async + DOM together).

3. Silent catch that swallows the real error.

What to try before the next post

  1. Run this checklist on one of your old scripts.
  2. Keep a personal "bugs I hit" note with one line each.
  3. Re-read the debugging post if you still guess instead of observe.

Next in this series: clean code habits for juniors — naming, small functions, and readable structure without dogma.

Try this next outside the series

Engineering habits only stick when they show up in testing, security, and maintenance work.