I started this project on November 4, 2023 with a 3×3 grid of colored squares. The first commit isn’t a hello-world Flutter app; it already has cell selection, drag-to-fill, a peek button, and the ghost of a game loop. Not because I was showing off. Because I already knew what a nonogram was and didn’t feel like pretending I didn’t.

For those who don’t: a nonogram is a logic puzzle where a grid’s rows and columns each carry a list of numbers telling you how long the runs of filled cells are. You deduce the picture. Think “paint-by-numbers for programmers.”

What followed was two-and-a-half years of an on-and-off solo project. Almost everything interesting happened in the last sixty days. Today the 1.1.0 build went to TestFlight, and I want to write the arc down while it’s still fresh.

The long quiet

Between November 2023 and early 2026, the project lived in a comfortable hole. I added row and column hints, a “five errors and you’re out” lives system, random board generation, a CI pipeline, a victory overlay. The whole game ran out of a ~290-line main.dart. It worked on my phone. That was enough, because nobody else was playing it.

This is how a lot of side projects die. They achieve “works on my machine” and then they stop.

Responsive layout was the wall

In February 2026 I decided to actually ship this thing, and within a week I was staring at a UI that looked great on a Pixel 6 and collapsed on a tablet. Fixing it one viewport at a time wasn’t going to work.

So I did the move that makes side projects survive: I wrote golden tests for multiple viewports — iPhone SE, iPhone 14 Pro, landscape, iPad, desktop — and let them fail. That’s the moment the project stopped being a toy. Tests aren’t how I prove the code works. They’re how I stop myself from being clever about things I can’t see.

The layout rewrite that followed was the first commit I co-authored with Claude. Not because I couldn’t do it. Because Claude was going to be faster at threading LayoutBuilder logic through side panels and hint regions, and I wanted to spend my time on decisions, not plumbing.

Extracting widgets, finally

With responsive layout working, I pulled the widgets out of main.dart. Row hints, column hints, grid cell, toolbar, victory overlay, game-over overlay — each got its own file. This is the kind of refactor that goes on the “I’ll do it when I have time” list and never comes off. Getting it done in February meant the next two months of changes were actually tractable.

Moral: if a refactor is blocking other work, it isn’t “tech debt.” It’s in your critical path, and you should treat it that way.

The uniqueness bug

Here’s one I didn’t see coming. Random boards can have multiple valid solutions. A player could look at a hint, deduce something that felt correct, and be wrong — not because their logic was bad, but because the puzzle was genuinely ambiguous. You play a nonogram trusting that the puzzle has one answer, and this was breaking that contract.

The fix was a real constraint solver. Line-wise propagation, enumeration of valid placements, backtracking with solution counting. The generator now produces a candidate board, runs the solver, and only accepts the board if the solution is unique. If it isn’t, throw it out and try again.

This is the first place in the project where the word “algorithm” felt earned. It’s not tutorial code. There’s branch pruning, cycle detection on propagation, placement caps, and a deadline timeout for candidates that take too long. I wrote a whole separate post on the performance work the solver needed once it existed.

Product touches that aren’t in the rules

Things I care about that aren’t in a nonogram rule book:

  • Double-tap to toggle fill/clear mode, with the active mode visible in the unknown cells. No separate fill/clear buttons.
  • Ripple animation and a heart-shake when you make a mistake. Games feel different when they punish you with texture.
  • A progress spinner and dimmed board while a hard puzzle generates. Nothing worse than a button that silently does nothing for two seconds.
  • TextScaler threaded through hint measurement so OS-level font scaling doesn’t clip the numbers. Accessibility is not a feature you bolt on.
  • A live timer, because I wanted stats at the end.

None of these are senior-engineer showpieces. They’re ten-minute changes that make the game feel like somebody cared. Side projects fail at the last ten percent of polish more often than they fail anywhere else, and the last ten percent is made of things that individually don’t matter.

How the AI collaboration actually looks

I started co-authoring commits with Claude in February 2026 and the velocity shifted. I want to be specific about what that looked like, because “I use AI” is the least useful thing anyone can say right now.

  • I kept the design decisions. What the game looks like, what the difficulty tuning means, what the solver’s contract is, what the UI should feel like. Nobody delegates that.
  • Claude was faster than me at mechanical refactors, performance micro-optimizations, golden test maintenance, and anything that involved threading a parameter through seven layers.
  • I wrote a CLAUDE.md that tells the model how to work in this repo: TDD, red-green, list all test cases in PR descriptions, don’t commit without running dart format, prefer extraction over comments. It’s a contract, not a ritual.
  • The structured commit messages and co-authored tags are real. Every perf commit has a benchmark, because Claude doesn’t forget and I do.

This isn’t a person being replaced by a model. It’s a person with a force multiplier who has more time to think and less time to type. The visible output is more commits and tighter work. The invisible output is that I’m making better decisions, because I’m not exhausted from writing the boring parts of them.

What I’d tell November 2023 me

  1. The thing blocking you from shipping isn’t the feature list. It’s the test story.
  2. Write a CLAUDE.md on day one. Future-you and future-Claude both need it.
  3. A 3×3 grid is a perfectly good starting point if you already know where it’s going.
  4. “Just ship it” is advice for people who haven’t fixed their responsive layout yet.
  5. The refactor that feels like tech debt is probably in the critical path. Stop treating it like a luxury.

Version 1.1.0 went to TestFlight today. That’s two and a half years from the first commit. Most of the work happened in the last two months. None of it happened accidentally.