The tier decision nobody re-asks
This update was drafted on a schedule by the AI I build with, from real project notes — part of the vibecoding experiment this blog documents.
So I already told one version of this story — what the Supabase free tier costs you when a project goes quiet. The ending of that post was a rule: when a project gets its first outside user, re-decide the database tier that day. Don't let it graduate to production while the tier is still set for a toy.
What I want to do now is widen it, because the database tier is not the only decision like that. It's one instance of a whole category, and the category is the actual lesson. There's a set of choices you make in the first hour of a project — casually, correctly, because the project is nothing yet — and not one of them has a trigger that fires later to make you look again. The project changes. The decisions don't.
Here's the list I've actually been bitten by, or watched myself skate past.
The domain
Every new build gets a something-ten.vercel.app URL for free, instantly, and for a week that's exactly right. You're iterating. Nobody's looking. A custom domain would be premature.
Then you put the project on your portfolio. Or you send the link to someone who might pay you. And it's still communityos-green.vercel.app, because the moment to change it was "before you showed anyone" and that moment doesn't announce itself — it's just a Tuesday where you happen to paste the link somewhere that matters. Half my built-and-waiting projects are still on their scaffold URL right now. It's not a crisis. It's just that the URL is now doing first-impression work it was never promoted into.
Error tracking
When a project is you and a localhost tab, you don't need Sentry. If something throws, you're right there, you see it, you fix it. Adding error tracking to a toy is genuinely a waste of an afternoon.
The problem is what "in production with no error tracking" actually means: a real person hits a bug, gets a broken screen, and leaves, and you find out never. There's no log line that reaches you. The app is failing for someone and the failure is invisible on your end — which is the exact silent-failure shape I keep running into. The decision "no error tracking, it's just a toy" was correct when made and quietly became "I have no idea if my live product works for anyone but me."
Backups
Free tiers give you thin backup guarantees — short retention, or point-in-time recovery only on paid, check the current docs because the lines move. For a scratch project, fine. There's nothing in there you'd cry about.
But data accumulates silently. Six weeks after a real person signs up, that database has things in it that can't be regenerated — their account, their history, whatever they typed. The backup posture is still the one you'd have picked for an empty database. Nobody sat down and decided "the CommunityHQ data is worth a real recovery window now." It just crossed that line while the setting stayed put.
Log retention
Same story, different table. When you're debugging live, the question is always "what happened an hour ago" — and free-tier log windows are short. I said it in the pooler-migration story: doing forensics on a window that's already been deleted is its own special pain. Log retention feels like nothing until it's the difference between a 20-minute diagnosis and an evening of guessing.
The boring security stuff
Rate limiting on the API. Locking down the auth rules past "logged in = allowed." Moving secrets out of a .env var that was copy-pasted once and will never be re-read. None of this matters when the only client is you. All of it matters the moment the URL is public and someone curious is poking at it. And none of it has a prompt — no framework yells at you on your first real signup.
Why the pattern is the point
Every one of these has the same three properties. It was the right call when you made it. It has no natural re-evaluation moment. And the thing that makes it wrong later — a real user, a public link, accumulated data — arrives gradually and quietly, so there's no single day where the old decision visibly breaks.
That's what makes it a trap and not just a to-do list. You're not being lazy. You're being consistent with a judgment that used to be correct. The project graduated from toy to production and never told you, because that transition doesn't have a ceremony — it's just usage creeping up on a thing you built in a weekend.
When you're running nine of these, the graduations stack up and you lose track of which project is at which stage. The toy-tier defaults are still sitting under projects that now have real people on them, and I couldn't tell you offhand which ones without going and checking.
The fix is a checklist, not vigilance
I don't think you can solve this by "being more careful," because the whole failure mode is that nothing prompts you. Vigilance needs a trigger and there isn't one.
So it's a list you run on a fixed event. Same as the database rule from the Supabase post — I just widened the list and kept the trigger. First outside user, or first time the link goes somewhere public, whichever comes first: re-decide the domain, turn on error tracking, check the backup window, check log retention, look at the auth rules and rate limits. Fifteen minutes. Most of it you'll leave as-is, and that's fine — the point isn't to upgrade everything, it's to make the decision again now that the facts changed, instead of inheriting it from a version of you who was building a toy.
The money was never the hard part. It's noticing the situation changed, and I've only ever learned that by getting caught.
This one's auto-drafted from my notes on a schedule. If a number isn't in the notes, it doesn't show up here — I'd rather leave a blank than make something up.