What five years of neglect does to a side project
I built this site in July 2021, right after finishing a coding bootcamp, and then didn’t touch it for five years. When I opened the repo again in August 2026, it didn’t build. Not “had warnings” — the install itself failed before it got anywhere near the application code.
The specific failure is worth writing down, because it wasn’t caused by anything I did. It was caused by not doing anything.
The install died, not the app
gyp ERR! stack TypeError: Cannot assign to read only property 'cflags'
node_modules/node-sass → Build failed with error code: 7
node-sass compiles native code through node-gyp. It was deprecated in 2020, and the version I had pinned cannot compile against Node 18 or newer. The only reason it had ever worked on my machine was that my local Node was still v14.17.3 — a version that reached end of life in April 2023.
So the site had two clocks running against it. One was the dependency, frozen at a version that would never support a newer runtime. The other was my own environment, which happened to be old enough to hide the problem. As soon as I touched either, it broke.
The tell was in node_modules: it contained exactly two packages, both native image binaries. The install had crashed partway through and left the tree in a state that looked plausible from the outside.
Rot isn’t gradual
The thing I’d assumed about neglected projects is that they degrade — a deprecation warning here, a slightly wrong behavior there. That’s not what happened. It worked perfectly, right up until it didn’t work at all.
node-sass was the first wall, but not the only one:
- Material-UI v4 was the pre-v5 package namespace. Its entire styling API —
makeStyles,withStyles— doesn’t exist in React 18+. Not renamed. Gone. prop-typeswas imported in a component but never declared inpackage.json. It resolved only because a dependency happened to hoist it. That’s not a dependency; that’s a coincidence.sass-loaderwas installed and.scsswas in the webpack test regex, butsass-loaderwas missing from the actual loader chain. A latent break that never fired because nothing imported a.scssfile.- Every deployed link was dead. Heroku retired free dynos in November 2022, so all my project demos 404’d.
Each of these was fine in 2021. None of them were things I got wrong. They were bets on an ecosystem staying still, and it didn’t.
Rewriting was cheaper than fixing
I expected to migrate. I ended up rewriting, and the arithmetic was clearer than I thought it would be.
The site was 966 lines. Almost all of it was Material-UI tab plumbing — a single page with five tab panels, no routes, one URL. The actual content was about ten paragraphs of prose, three schools, a publication, and a project list.
Migrating meant porting a dead styling API to a new one, then still having a tab-panel architecture with nothing to link to and nothing for a blog to attach to. Rewriting meant moving ten paragraphs into a framework built for content. The thing I’d have been “saving” was the part with no value.
I moved to Astro, which builds to static files and treats Markdown as a first-class input rather than something you bolt on.
What the typecheck caught that the build didn’t
One detail from the rebuild stuck with me. Partway through, I put a comment in an invalid position inside a component’s attribute list. astro build accepted it and produced correct output. astro check reported eight cascading parse errors.
If CI had only run the build, that would have shipped. The typecheck is doing work the build isn’t, and I’d have assumed a green build was sufficient.
Same category of lesson as the node-sass failure: the signal you’re not collecting is the one that bites you.
What I actually changed
The concrete guards, in rough order of how much grief they prevent:
| Guard | Prevents |
|---|---|
.nvmrc + engines |
The runtime drifting out from under the dependencies |
npm ci in CI |
package-lock.json and package.json silently diverging |
| Typecheck as a separate CI step | Errors a lenient build tolerates |
| Pinned deploy tooling | A CLI release changing deploy behavior |
A .gitignore |
node_modules/ being one git add . from the repo |
That last one is embarrassing. The 2021 repo had no .gitignore at all. node_modules had stayed out of it purely by luck.
The part I’d tell 2021 me
Pick boring dependencies, and prefer ones with no native compilation step. node-sass is the whole argument on its own: dart-sass existed, was pure JavaScript, and would still work today. The choice between them looked irrelevant in 2021. It was the single decision that determined whether this project was alive in 2026.
And write down what the project needs to run. Not for anyone else — for yourself, five years later, when you’ve forgotten that the only reason it works is a Node version you’re about to upgrade.