AI Portfolio Lab Tools Games Blog Feedback
All Posts

I Left the Bug. Deliberately.

I deleted /now from my site. CloudFront kept serving HTTP 200 for that path. The fix takes five minutes. I decided not to fix it, and I want to be specific about why.

2 min read
infrastructureawsricoordonioops

I deleted /now. It was a “what I’m working on right now” page that I kept for a while and never consistently updated. Pulled it from the repo, deployed, moved on.

Search Console kept listing it as a live page with active impressions. I checked it. My homepage loaded. HTTP 200.

I thought the CloudFront cache was stale and ran an invalidation. Still 200. Ran it again.

Not a cache issue. The CloudFront distribution is configured SPA-style: any path that doesn’t exist in the S3 bucket responds with /index.html and HTTP 200. That’s a common setup for single-page apps that handle routing client-side. My site isn’t a single-page app. But when I set up CloudFront a while back, that was the pattern I grabbed, and I never revisited it. Every nonexistent URL on ricoordonio.com returns my homepage with a 200. Every deleted page looks alive to search engines. Every old link that goes nowhere resolves silently to whatever I’ve got on my front page this week.

The fix isn’t complicated. Add a src/pages/404.astro so there’s a real /404.html in the build output. Then update the CloudFront distribution to respond to 403s and 404s with /404.html and a proper HTTP 404 status code. Two changes.

The second one requires cloudfront:UpdateDistribution. My deploy IAM user has CreateInvalidation. That’s all it needs for deployments. I scoped it that way on purpose: least-privilege, nothing extra. If I’d given the deploy user permission to update distribution settings, it could change behavior I haven’t authorized it to change. That’s not how you scope deploy credentials.

So fixing the 404 behavior means going into the console myself. Five minutes, one-time change.

I chose to leave it.


Not because I forgot. I saw the fix clearly. I wrote it down. Then I made an actual call.

Here’s the logic: every deleted page returning my homepage is wrong, but it’s not harmful. Visitors get the current site, not a blank error. The SEO problem is real but low-urgency. The fix requires a console detour that means context-switching away from whatever I’m building, and every time I schedule it, something more pressing takes the slot. So the fix sits in the backlog next to Phase 2 of Kudos, next to the Supabase OAuth consent screen branding, next to a dozen other things that work wrong without breaking anything.

I know what’s in that backlog. Most of it stays there.


The thing I had to be honest about was the difference between “haven’t gotten to it yet” and “decided not to.” The first one implies it’s coming. The second one names the actual call.

They look identical from the outside. They’re not.

Most of my site’s soft edges are conscious deferrals. Not neglect. I made a call, noted it, and moved on. The IAM scoping is right. The bug can wait. When it finally bothers me enough, the fix is five minutes in the console.

Know the difference between your open to-dos and your active decisions. The backlog isn’t a queue of things you’re planning to fix. It’s a ledger of decisions you haven’t reversed yet.