All posts

What Building 6 Games Taught Me About Writing Better Code

Building games has been the best education I've had as a developer. A bug in a to-do app can go unnoticed for weeks. A bug in a game is obvious the second you play it. The snake clips through a wall. The alien stops shooting. The tile merges wrong. You know immediately something is broken, and that tight feedback loop forces you to write better code.

Over the past year I built six games: Snake, 2048, Space Invaders, a Crossword Puzzle, a Quiz Game, and a Pixel Runner platformer. Each one taught me something I didn't expect going in.

The games and what they taught me

Snake — State machines are underrated

Snake seems simple until you start building it. Managing which direction the snake is currently moving, which direction it's about to move, and blocking it from reversing into itself requires careful state tracking. It taught me to think in terms of valid state transitions, which is a pattern I now use in basically everything.

2048 — Sometimes the clean solution exists

My first attempt at the tile merge logic was about 80 lines of nested conditionals. The clean version is 15 lines: collapse each row into an array, filter out the zeros, merge adjacent matching values, pad back to length. Finding that solution felt like a proper lightbulb moment. The simpler version is usually out there if you look for it.

Space Invaders — Collision detection goes deep

I rewrote the collision system three times. Pixel-perfect is too expensive. Pure bounding-box is fast but sloppy. I ended up doing a broad-phase check first and only running the precise check when objects were close. That's apparently a standard game engine pattern, and I arrived at it purely out of necessity.

Crossword — Get your data structure right before writing any logic

A crossword is a grid where letters share cells between words. I modelled it as a flat array initially, when I actually needed a 2D map with metadata on each cell. Refactoring halfway through cost me two full days. Now I spend real time on data design before writing a single function.

Quiz Game — Feel is part of the product

The quiz has 60 questions across 6 categories. Getting the questions right took maybe 10% of the total time. The rest was transitions, answer feedback animations, score tracking, and the results screen. How something feels to use is a code problem just as much as whether it works.

Pixel Runner — Use requestAnimationFrame

I used setInterval for the first game loop attempt. It was choppy and inconsistent across devices. Switching to requestAnimationFrame and calculating delta time so the speed stayed consistent regardless of frame rate made it feel completely different. The browser gives you the right tool for this, use it.

What all of them had in common

Every game followed the same arc. It worked, then it got complex, then I had to go back and refactor. The code that gets something to a working state is almost never the code you actually want. Getting from working to good usually means one honest rewrite of the worst part.

Don't get attached to code you've already written. If you can see a cleaner way to do it, take it. The first version got you to working. Now build the real thing.

Why I'd suggest it to any developer

Games make the feedback loop as short as possible. You write something, run it, and immediately know if it's right. That speed builds intuition faster than any other kind of project I've worked on.

They also drag you into problems you'd never go looking for yourself: rendering, physics, input handling, basic AI. Those problems teach you more than another CRUD app would. Pick a game you like, build your own version of it, and don't stop once it runs. Stop when it actually feels good to play.

All six are live on the Games tab if you want to try them.