Preparing for BlatBlat’s first off-season
BlatBlat was aggregating stats for four months during the off-season. A site built around a nightly game schedule has an obvious problem when there are no games. The naive version of this is a page that keeps asking for data nobody has produced, then reports its own confusion back to you. Ours said “No games for Thu, Aug 27”. Correct, and useless. Naming the date implies we expected something to be there.
So the off-season became a state we recognise rather than a gap we fall into.
Knowing which state you are in
I now know the trigger to stop crawling is the championship, but that does not mean we need to stop completely. It just means extending the days between each crawl while still trying to keep news and transactions up to date. The harder question is when basketball returns and how to record that.
Keying off the finals is preferable to a date-based rule because the calendar moves and the bracket does not lie. It also clears itself: no decided finals, no off-season, which means next June it resets without anyone touching it.
As for the return, in June that date is genuinely unknown. The league publishes its schedule in mid-August. You cannot hardcode a date you do not have, and guessing is worse than admitting ignorance. So we check once a week and say “schedule pending” until there is something to say.
Asking other people’s servers politely
Once a week is a deliberate number. The schedule is published once. Polling nightly would mean roughly three hundred requests to learn the same “not yet”, which is rude and buys nothing. Weekly costs almost nothing and is never more than seven days stale.
We read the league’s own static schedule first. If that fails we probe a second source across the fortnight the season has always opened in. Two sources, tried in order, no retries and no escalating waits. A failed request falls through to the next option immediately rather than sitting on a timer. If both come back empty that is an answer too, and we record it.
One rule matters more than the rest: a known date is never overwritten by a failure. Once we have a tip-off, a bad night upstream cannot erase it. Refreshes can confirm or move the date. They cannot delete it.
What keeps moving
Not everything freezes. The off-season is when transactions matter most. Free agency, trades, signings, waivers. Standings and leaders are final and labelled as such, but the transaction wire runs the whole way through, so that is the one crawl we keep alive while the rest is gated off.
This split has to be visible. A frozen number and a live number look identical if you do not say which is which. Each section now states its own condition.
Two things we had wrong
Building this surfaced a bug that had been live since July. The season identifier rolls over on the first of the month, but no statistics exist under the new one until October. For eight weeks the site asked for a season that had not happened and every player came back empty. The leaders panel had been quietly blank the entire time. It now falls back to the completed season, which is the only meaningful set of numbers available while waiting.
The second was subtler. Transactions were badged as debuts, but the badge was set at the moment a player joined a team. That is a roster move. A debut is a player taking the floor. A trade on a day with no game cannot be a debut, and a player re-signing with a club he was already at is not debuting for them. Both were on the page. Resolving it needs the whole transaction history plus the schedule, neither of which the parser sees, so the correction happens later, and only ever removes the badge. Missing history should not be able to invent a debut.
The general case
An empty state is still a state. It has facts: what just finished, what is coming, what is still moving, when we last checked. Presenting that is a different job from rendering nothing and hoping the user infers why.