Skip to content

The reset that wasn't as clean as I thought

I reset all three games -- fresh puzzles, re-scrambled lists. Two things stayed quietly alive: a streaks table the migration forgot, and a Date.now() the cache had frozen.

SD
Shubham Datarkar
· 2 min read

I reset all three games yesterday -- fresh puzzle epochs, re-scrambled answer lists, a clean slate for everyone. The slate was not clean.

The idea was simple. Each game (Alfazy, Hit and Blow, Integra) works out which puzzle you get from an epoch -- a fixed start date the day counter runs from. After weeks of testing, the answer lists had leaked into memory and the day math had drifted. So I moved every game onto its own epoch and re-scrambled the word lists behind it. New day, new puzzle, no ghosts.

Migration 20260715000001 did the reset. I ran it, played each game, watched the puzzles come up fresh, and shipped it. Then I noticed the streaks.

The table I forgot existed

Players who had built up a streak still had it -- counted against a puzzle history that no longer existed. The migration reset the puzzle state but never touched the streaks table. It sat one join away from everything I did clear, and I just did not think of it. The follow-up fix was a few lines. The lesson was longer: a reset is only as complete as the tables you remember you have.

I thought that was the end of it. It was not.

Date.now() was quietly lying

This version of Next caches render output aggressively. Somewhere in the games render path I was reading the current time with Date.now(). During a normal request that looks fine. But when the render is cached, Date.now() freezes at whenever the cache was filled -- so the game's idea of today could be stale, and the puzzle you saw depended on when the page last rendered, not when you opened it.

The fix was to stop trusting Date.now() inside render and read request-time through connection() instead, which forces a fresh evaluation per request. One line of intent, but it took staring at a puzzle that would not roll over to finally see it.

Two bugs, both from the same blind spot: assuming the obvious tool tells the truth. A migration touches the tables you name, not the ones you mean. Date.now() returns a number, just not the number you think, once a cache sits in front of it.

A reset is only as complete as the tables you remember you have.

Note to self, 2am

Today the games are actually reset -- puzzles, lists, streaks, and a clock that reads the real now. Small features on paper. The debugging around them is where the real learning hides.

Reactions

How was this article?

Newsletter

Never miss the next one

Get the latest playbooks, build logs, and the occasional unpublished idea straight to your inbox. One signal a week — no noise.