I build infrastructure for a living. Distributed systems, CI/CD pipelines, Terraform — the kind of work where a wrong state in one place quietly corrupts everything downstream. So one evening, for no good reason, I decided to build a chess game in React, and the en passant rule and the checkmate logic turned out to be the hardest state-management problem I had touched in years. Nobody asked for it. There was no client, no ticket, no deadline. I just wanted to know if I could model something genuinely complex in TypeScript without losing my mind.

Sixty-four squares. Thirty-two pieces. A practically infinite number of legal game states. On paper it sounds like a weekend project. And rendering a board really is a weekend afternoon — a grid, some piece components, drag handlers, done. Making the game actually follow the rules is where I started questioning my life choices.

Rendering Is Easy. The Rules Are Brutal.

Here is the thing nobody tells you when you set out to build a chess game in React: the visuals are the trivial 10%. The other 90% is a rulebook written by committee over 500 years, and every rule has an edge case waiting to humiliate you.

React handled the board and the piece rendering. TypeScript did the heavy lifting on the game logic, and I do not think I could have finished without it. When you are validating chess moves, a runtime error is not just a bug — it is an illegal move that nobody catches until the board is already wrong. Type-safe move generation meant the compiler caught entire categories of mistakes before they ever reached the screen.

Then came the rules that do not fit in your head:

  • Check detection after every single move. Not just the move you are making — you have to prove your own king is not left in check after the move resolves. That means simulating the move, scanning every enemy piece's attack surface, and rolling back.
  • Castling has four conditions that must all be true at once. King has not moved. Rook has not moved. No pieces in between. And the king cannot start in check, pass through check, or land in check. I read the FIDE rulebook twice to get this right.
  • Pawn promotion, which sounds simple until you have to wire the UI and the state transition together.

En Passant Broke My Brain

En passant is the most confusing rule in chess, and it becomes even more confusing the moment you realize your code has to remember what happened on the previous turn to validate the current one. A pawn captures a square the opponent's pawn skipped over — but only immediately, only for one turn. Suddenly your move validator is not a pure function of the current board anymore. It needs history. That one rule forced me to rethink how I was storing game state entirely.

And then checkmate. In English, "stalemate" and "checkmate" are one letter apart. In TypeScript they are entirely different logic trees. Checkmate: the king is in check and has no legal move to escape. Stalemate: the king is not in check but has no legal move at all — and that is a draw, not a loss. Getting that distinction wrong does not throw an error. It just silently declares the wrong winner.

I spent three evenings debugging a scenario where the king could castle straight through check. The code did not complain. The tests I had did not catch it. I only noticed because I played a full game against myself and thought, "wait — that is illegal." That is the kind of bug that does not announce itself. It just sits there, quietly wrong.

What Chess Taught Me About Infrastructure

Here is the part that surprised me. This silly chess project taught me more about state management than any Redux tutorial I have ever followed.

When one wrong state corrupts everything downstream and the user can see it instantly on the board, you learn to think about immutability and validation very differently. You stop trusting that "it probably works." You start simulating the move, checking the invariants, and only then committing the change. That is exactly how I think about distributed infrastructure now: model the system, define what "valid" means, and never let a bad state through the gate.

Modeling interlocking rules where one wrong value corrupts everything downstream transfers directly to CI/CD logic, to Terraform plans, to anything where correctness actually matters. The chessboard just made the failures visible in a way a broken pipeline rarely does.

Build Something That Scares You

The point was never to build the next Chess.com. The point was to pick something complex and actually finish it.

If your side projects are all easy, you are staying comfortable. Pick one that makes you slightly nervous. The en passant rule, the checkmate logic, the four conditions of castling — none of it was on a job description. All of it made me a better engineer. Build the thing that scares you a little. That is how you grow.

What side project taught you something no course ever could?