Every developer has lost an afternoon to a bug that turned out to be a one-line fix. This post covers seven common ones across frontend, backend, and DevOps, with the fix for each.
1. CORS errors: "No 'Access-Control-Allow-Origin' header"
The problem: Your frontend calls your API and the browser blocks it.
Why it happens: CORS is enforced by the browser, and the server has to opt in. Changing frontend code won't fix it.
The fix (Express):
import cors from "cors"; app.use(cors({ origin: ["http://localhost:3000", "https://yourapp.com"], methods: ["GET", "POST", "PUT", "DELETE"], credentials: true, })); ``` **Gotcha:** If you use `credentials: true`, you can't use `origin: "*"`. List your origins explicitly. Also check that your server handles `OPTIONS` preflight requests. ## 2. The N+1 query problem **The problem:** Your API is slow, and the logs show hundreds of near-identical queries. ```javascript // Bad: 1 query for posts + N queries for authors const posts = await Post.findAll(); for (const post of posts) { post.author = await User.findByPk(post.authorId); } ``` **The fix:** Use eager loading or a join. ```javascript // Good: 1-2 queries total const posts = await Post.findAll({ include: [User] }); ``` **Tip:** Turn on query logging in development. N+1 problems are easy to spot once you can see the SQL. ## 3. Forgetting `await` (and unhandled promise rejections) **The problem:** Your function "works" but returns `Promise { <pending> }`, or crashes Node with an unhandled rejection. ```javascript // Bug: errors are swallowed, order isn't guaranteed items.forEach(async (item) => { await saveItem(item); }); ``` **The fix:** `forEach` doesn't wait for async callbacks. Use `for...of` for sequential work or `Promise.all` for parallel work. ```javascript // Sequential for (const item of items) await saveItem(item); // Parallel await Promise.all(items.map(saveItem)); ``` Always wrap the top-level call in `try/catch`, or attach `.catch()`. ## 4. Secrets committed to Git **The problem:** An API key or {% raw %}`.env`{% endraw %} file ends up in your repo. Bots scan public GitHub constantly, so a leaked key can be abused within minutes. **The fix:** 1. **Rotate the key immediately.** Deleting the commit isn't enough, because the key is still in history. 2. Add `.env` to `.gitignore`. 3. Scrub history with `git filter-repo` or BFG Repo-Cleaner. 4. Add a pre-commit scanner like `gitleaks` or `trufflehog`. ```bash # .gitignore .env .env.* !.env.example ``` Commit a `.env.example` with fake values so teammates know what's required. ## 5. Docker builds that never use the cache **The problem:** Every {% raw %}`docker build`{% endraw %} reinstalls all your dependencies. {% raw %} ```dockerfile # Bad: any code change invalidates the npm install layer COPY . . RUN npm install ``` {% endraw %} **The fix:** Order layers from least to most frequently changed. {% raw %} ```dockerfile # Good: dependencies are cached until package files change COPY package*.json ./ RUN npm ci COPY . . ``` {% endraw %} Add a {% raw %}`.dockerignore`{% endraw %} (with {% raw %}`node_modules`{% endraw %}, {% raw %}`.git`{% endraw %}, and {% raw %}`dist`{% endraw %}) to keep the build context small. ## 6. React: infinite re-renders and stale state **The problem:** {% raw %}`Too many re-renders`{% endraw %}, or a {% raw %}`useEffect`{% endraw %} that runs forever. {% raw %} ```javascript // Bug: new object every render, so the effect re-fires every time useEffect(() => { fetchData(options); }, [options]); // options = { page: 1 } defined inline ``` {% endraw %} **The fix:** Memoize objects and functions used in dependency arrays, or depend on primitives. {% raw %} ```javascript const options = useMemo(() => ({ page }), [page]); useEffect(() => { fetchData(options); }, [options]); ``` {% endraw %} If state looks stale inside a callback, use the functional update form: {% raw %}`setCount(c => c + 1)`{% endraw %}. ## 7. Memory leaks in long-running Node.js services **The problem:** Memory climbs until the process crashes with {% raw %}`JavaScript heap out of memory`{% endraw %}. **Common causes:** - Event listeners added but never removed - Unbounded in-memory caches (a plain {% raw %}`Map`{% endraw %} that only grows) - Timers ({% raw %}`setInterval`{% endraw %}) that are never cleared - Closures holding references to large objects **How to find it:** {% raw %} ```bash node --inspect app.js # Open chrome://inspect, take heap snapshots, compare them ``` {% endraw %} Take one snapshot at startup and another after load, then look at what's growing. For caches, use an LRU cache with a max size instead of a bare `Map`. ## Debugging checklist When you're stuck, run through this: - [ ] Can I reproduce it reliably? - [ ] What changed since it last worked? ({% raw %}`git bisect`{% endraw %} helps) - [ ] Am I reading the *actual* error message and stack trace? - [ ] Have I checked the network tab, logs, and environment variables? - [ ] Can I shrink it to a minimal reproducible example? ## Wrapping up Most of these bugs come from a handful of root causes: misunderstanding where a rule is enforced (CORS), hidden cost (N+1), async behavior, and configuration hygiene. Learning to recognize the *category* of a problem speeds up debugging a lot. **Which of these has cost you the most time? Share your worst debugging story in the comments.** 👇 

