graceful degradation is an invariant, not a vibe
updated Jul 4, 2026
This site aggregates my other projects as live data — the reading tracker, the riichi trainer, market briefings out of Google Drive. Every source sits behind a connector with one hard rule: if anything fails — missing config, dead upstream, bad data — the connector returns placeholder data, never a crash. That’s why CI builds with zero secrets and the site renders even when a source is down. Degradation is designed, not hoped for.
Then an audit of my own code found two connectors quietly breaking the rule. Both did fallible setup before the try: one awaited a Google Drive token on its first line; the other constructed a database client, which throws synchronously on a malformed connection string. The catch guarded the query, not the setup — so the invariant held for runtime failures and silently didn’t for setup failures. One transient auth blip and the page whose whole promise was “never crash” would have crashed.
The fix was moving two lines. The lesson is the class of bug: graceful degradation fails at the seams. “This function never throws” is a claim about its first fallible expression, not its happy path — so the try has to start there. And now tests hold the promise in place: mock the token to reject and the client to throw, and assert the sample data comes back instead of a rejection.
The mirror of this rule is my config policy: environment validation fails loud at startup, while public read paths fail quiet. That’s not a contradiction — it’s the same question answered per edge. Config breaks in front of the person deploying; read paths break in front of a visitor. A failure policy is chosen per edge, then enforced like any other invariant — with tests, not intentions.
the hub · warm terminal