[drop_cap]I[/drop_cap] built my first home page in 1996 in Microsoft FrontPage. It looked like a digital ransom note, but it was mine. Instantly accessible from anywhere in the world, without permission, like global graffiti.
In the early 2000s, like many others, I learned CSS by endlessly customizing MySpace profiles. Turns out learning to type the same HTML and CSS tags over and over again actually does wonders for remembering. I wonder if prompting Claude Code has similar effects. Probably not.
Then I got into WordPress and PHP, and that’s basically where I’ve been ever since—the constant hype-cycle of new JS frameworks nothing but a faint murmur beyond the walls of my trusty LEMP stack.
Until very recently, my “version control” system consisted of renaming files to index_FINAL_v2_FIXED_FOR_REAL.php.BAK. My “deployment pipeline” was an SFTP client and a prayer. If the site broke, I edited the live code on the server while users watched. It was stupid, and I knew it, but I had other things to worry about.
I’m not a developer in the classical sense. My identity isn’t tied to “the craft”. I love messing around with tech, but at the end of the day I just need stuff to work so I can publish my books and get back to writing.
A Brave New World
Last year I got the Modern Dev itch. I jumped into React and Tailwind, and Vite’s hot module reload is a blast—seeing a color change instantly without a browser refresh almost made me forget I was building a single-page app held aloft by a rickety stack of ever-shifting npm modules. I finally even learned Git (though I still treat rebasing like a dark ritual that might accidentally delete my hard drive).
Then I needed a contact form.
Suddenly I wasn’t a writer anymore; I was a civil servant in a Terry Gilliam movie, shuffling JSON through pneumatic tubes just to appease the Gods of TypeScript. I was spinning up Next.js instances, worrying about serialization and environment variables, and spending three days building a space station just to send an email that says “Hi.” I’d traded my sanity for a node_modules folder spilling out clown-cars of esoteric dependencies.
Meanwhile, PHP has no build step. You write, you execute, you’re done. A PHP process runs and dies with the request—no long-running Node processes accumulating memory, no weekly vulnerability disclosures in your 47 transitive dependencies. These modern frameworks were designed by massive engineering teams at companies obsessed with scale. That’s fine for them. I’m just one dude.
So I tried headless WordPress—React on the front end, WordPress handling auth, email, and content management behind it, all pre-configured. It worked great until I hit the hydration problem. A single-page app is basically an empty HTML file that loads a giant JavaScript bundle, and search engines hate it. So now you need two versions of your site: a skeleton and the actual interface layered on top. I got it working, eventually, by mixing in some PHP. Very much reinventing the wheel. Not as bad as running your own Next.js “vulnerability accelerator,” but still annoying enough.
“In 2025, attackers published 454,648 malicious npm packages. The average npm project pulls in 79 transitive dependencies. A single compromised package can cascade through entire ecosystems within hours.”cyberdesserts.com
Back To Basics
For my last two projects I went a different direction: HTMX and Alpine.js. Two tiny libraries that give you 80% of React’s interactivity at maybe 10% of the complexity. No build step, no dependency hell, no fresh CVEs landing in your inbox on a Tuesday morning. You write HTML, sprinkle in a few attributes, and things respond dynamically. It’s not as slick as React with Vite, but the friction it removes makes the tradeoff feel obvious.
But the real kicker is the stability. React code from three years one week ago is usually a graveyard of deprecated hooks and broken builds. PHP and HTML? They just sit there and work. Modern web dev feels like babysitting a Tamagotchi that dies if I don’t massage it with npm audits and overrides every second day.
My current happy place is WordPress as the backend with a lean HTMX/Alpine theme on top, developed locally with LocalWP, versioned in Git, and deployed with a simple bash script. I’m not pretending the last few years didn’t happen—I just got selective about which parts to keep.
How The Sausage is Made
Let’s have a look at some concrete examples to better illustrate the madness:
Exhibit A: Search Suggestions
With React/Next.js/TS you need a Client Component, a Debounce Hook, a Fetcher, a JSON API route, and a TypeScript interface for the “Shape” of a Post:
So far so good, right? But, wait! That’s only the front-end. Now go write the api/search.ts route, define the Post interface twice, once for the API route, once for the front-end, naturally. Oh, and don’t forget to fix the hydration error because the server time doesn’t match the client time…
Now let’s look at the “boring” alternative with HTMX. You don’t manage state; the DOM is the state.
Yup, that’s it. That’s the whole code. Back-end? Eh. Let WordPress spit out HTML. Done.
Exhibit B: A Simple Toggle Or Drop-Down
With React this would look something like this:
With Alpine you can do the same, but with fewer lines and no bureaucratic javascript wrangling parade:
Don’t get me wrong—I haven’t completely thrown in the towel. I still maintain a few SPAs in React (including this headless blog) where a high-fidelity interface actually justifies the headache. But it’s simply not always worth the trouble. Sometimes the old way (turbocharged with some HTMX and Alpine) is just fine.
Maybe it’s a step backwards. I don’t care. As long as the site loads fast, my code is readable, and I can get back to doing actual work.
We’ve spent a decade making the web harder to build just so we can feel more important. I’m over it. I’ll take a boring site that works over a cutting-edge one that turns basic maintenance into a second job.
–



