What breaks first when you stop maintaining a side project
Passion, 12 May 2026
One of our own projects still loads. The page paints quickly, the layout is fine, the sign-in card appears exactly where it should. You type an email address, press the button, and nothing happens, because the hostname the application talks to no longer resolves in public DNS. The front end is in perfect health. Everything behind it is gone.
This is the ordinary way software dies. Not with an outage anybody notices, but with a front end that keeps serving a 200 for years after the thing it depends on has quietly been switched off, and nobody finds out until they look.
Why the front end outlives everything else
A compiled front end has no runtime dependencies beyond the file host. It has no credentials to expire, no version to upgrade, no invoice attached. It cannot rot in place. Barring the hosting account itself going away, it will still be there in five years, painting a form that submits into nothing.
Everything else in a project has an account behind it, and accounts have owners, cards, renewal dates and dormancy policies. So the shape of the failure is always the same: the visible part survives and the working part does not, which is precisely the combination that defeats the way most people monitor things.
The list of things that expire
| What | How it usually ends | Loud or silent |
|---|---|---|
| Domain registration | card expires, renewal notice goes to an unread address | loud, everything stops at once |
| DNS zone | leaves with a hosting account | loud |
| Static hosting | it does not, mostly | this is the part that survives you |
| Build pipeline | the runtime version it pins gets retired | silent until the next deploy fails |
| Managed database project | dormancy policy pauses it, then removes it | silent, the site still loads |
| API key with a card behind it | card expires, key revoked | silent, one feature stops |
| Model or API version | provider deprecates the name you pinned | silent, one feature stops |
| Scheduled jobs | die with whatever hosted them | silent, content simply stops updating |
| OAuth client secrets | expire on a schedule you agreed to once | silent until nobody can sign in |
Six of those nine are silent and four of them leave the homepage working. That ratio is the whole problem.
Uptime checks that cannot detect this
A check that fetches the homepage and asserts a 200 will pass forever on a static site, no matter what has happened underneath. It is checking that a file exists.
A useful check exercises the dependency chain. The minimum version is a health endpoint that actually touches each backing service and reports per-dependency status, and a check that asserts on the contents rather than on the status code:
# passes only if the backing services are genuinely reachable
curl -fsS https://example.com/api/health \
| jq -e '.database == true and .storage == true and .queue == true'
For a project with no backend of its own to ask, the equivalent is a synthetic check that performs one real read through the same path a visitor uses. It costs a few minutes to write and it is the difference between finding out on the day and finding out eighteen months later.
The second half of the same idea is to make the failure visible in the interface. An app that cannot reach its backend should say so on the screen rather than leaving a form that swallows submissions. Users report a banner that says something is wrong. Nobody reports a button that does nothing, they just leave.
What we got wrong, specifically
The nutrition app in question is a single page application that calls a managed Postgres and a set of edge functions. Those functions handled the photo analysis, the meal suggestions and an hourly job that generated the recipe library. The host for all of it returns NXDOMAIN.
We do not know whether the project was deleted, paused after dormancy, or migrated somewhere and never repointed. That uncertainty is itself the finding. Nobody wrote down which account held it, whose card was attached, or what the dormancy policy was, so the honest answer to "what happened" is that we would have to go and find out.
Two smaller symptoms of the same neglect are worth naming because they are so common. The scheduled recipe generation lived inside the same project that vanished, so the content pipeline went with it, and there was no separate signal for "the content has stopped changing". And the canonical URL in the page head points at a different domain from the one the app is actually served on, which is the sort of drift that only ever surfaces when somebody audits.
The last commit is from October 2025. Nothing alerted anyone. We found out by looking.
Decide the status, do not drift into it
Every project should be in one of three states, chosen deliberately.
Maintained. Someone owns it, dependencies get updated, the checks run and page somebody. This costs real time each month, so you can only have a few.
Frozen. Working, not being developed, dependencies pinned, checks still running. The rule that makes frozen viable is minimising the number of accounts it depends on. A frozen project with one hosting account and no database survives; a frozen project with five vendor accounts is on a countdown.
Archived. Deliberately switched off. Done well, that means a static snapshot or a short page explaining what it was, the accounts cancelled on purpose rather than by expiry, the domain either parked or released with intent, and the code somewhere public if it is any use to anyone.
The fourth state is the one you get by default: still running, nobody watching, half the features broken, still accepting sign-ups. It is worse than archived in every respect, including how it looks to anyone who finds it.
Twenty minutes per project
Write the inventory. One file, one project, plain text, kept with the code:
360nutri
domain registrar A, renews 2027-03, card ending 4321, owner: studio
dns registrar A
frontend static host B, builds from main
backend managed postgres project C, free tier, dormancy: 7 days
model api provider D, key rotates annually, billing card ending 4321
scheduled job hourly, runs inside project C <-- single point of failure
proof of life curl /api/health | jq -e '.database == true'
The value is not the file, it is the act of writing it, because the single points of failure announce themselves as you type. In our case, one line would have said everything: the analysis, the recipe generation and the accounts all lived in one managed project with a dormancy policy nobody had read.
Put a calendar reminder on each project twice a year. Run the proof-of-life command. Decide again which of the three states it is in. Ten minutes, and it is the difference between a project you switched off and a project that decayed while you told people it was live.
The app in question, and what state it is actually in: 360nutri.