Shipping a shared-package feature across two repos for one route
The ask was small on paper: add an RSS feed. A spike had already flagged it as the highest-value gap on the site, and the fix looked like a fifteen-line file - filter the blog collection by publish date, hand it to @astrojs/rss, done.
Then I asked myself a question the issue hadn’t: should this live in the shared component package instead of just this one site? Both imperfectsystems.com and vdaluz.com pull from the same @vdaluz/astro-blog package, and vdaluz.com has no RSS feed either. Scoping the fix to one repo meant the second site would face the exact same fifteen lines later, probably copy-pasted with small drift.
The package’s own README made the boundary clear before I had to guess at it: “this is a component library, not a drop-in blog… routes stay in each app.” A full RSS route belongs to a site. But the shape of an RSS item - title, link, description, categories, built from a post - doesn’t care which site it’s on. So I split it: a pure data-mapping helper, buildRssItems, goes in the package. The actual route, the actual call to rss(), stays local to each site. It’s the same pattern the package already uses for its JSON-LD helper.
The part that actually slowed me down wasn’t code, it was consequence. This package ships as a tag-pinned GitHub tarball, not an npm-registry package. Once a consumer’s lockfile records that tarball’s hash, the tag is effectively frozen. Bump it carelessly and a bug isn’t a quick fix, it’s a new tag and a second migration.
So before tagging anything, I proved the change worked. npm pack in the package repo, install the resulting tarball locally into imperfectsystems.com, build, and read the actual generated XML. Only after confirming three posts, correct order, working links, did I revert that test install and cut the real tag.
Then a detail I almost missed: I reverted my test package.json edits, which is correct, but it meant the next real bump had to pull from the actual published GitHub tarball, not the local file I’d just tested against. If I’d committed a lockfile pointing at a local path, the site’s CI - which has no filesystem access to my machine - would fail on the very next deploy. I confirmed the tag was actually live on GitHub (following the redirect to a real 200) before touching the site’s dependency at all.
Deployed, the feed came back with the wrong content-type on my first live check - text/html, serving the 404 page. Retried a few seconds later: application/xml, correct body. A stale edge cache at one Cloudflare POP hadn’t caught up yet. It’s a good reminder that “the deploy finished” and “every edge node agrees with the deploy” aren’t the same claim, especially right at the moment a rollout completes.
The feed validates clean against the W3C validator now. One recommendation, not an error: it’s missing a self-referencing atom link, which is just @astrojs/rss’s default. I’ll leave that alone.
Related reading
Clearing a backlog: five small fixes in one release
A dead-decoration a11y fix, a schema field that validated but never rendered, and a formatter run that silently flipped quote style across four files.
The escaping bug that only shows up with a second query param
A rewrite function that worked in every test and every real URL it had ever seen, and would still have broken the moment someone added a second query param.
Testing the part of the codebase that documents its own footgun
A README warning about a subtle failure mode is a confession that the code isn't tested against it yet.