What six months of fixing real sites with AI taught me
Six lessons from six months of fixing race websites, WordPress plugins, DNS and a home network with an AI beside me, from a race-day server crash to a bug that came down to one colon.
- Written by
- Naresh Kumar
- Published
- Reading time
- 7 min
- Topic
- Tech & AI
On October 17, the morning of the Backyard Ultra World Team Championship, the site I help run hit a server load of 67 on a 2-core box and started showing “Error establishing a database connection.” Teams from 67 nations were racing, and their fans were all trying to follow along. By that afternoon, load was back near 1.
I didn’t fix it alone. I fixed it with an AI working beside me, one command at a time. That incident was the loudest of about six months of real problems I worked through this way. They came from race websites, WordPress plugins, DNS records, a home network and a handful of side projects.
I’m an electrical engineer by training and a web developer by trade. Looking back over those fixes, the specific bugs matter less than the patterns. Here are the six lessons that kept repeating.
1. The bug is usually a layer, not a line
Most of my “bugs” weren’t in code at all. They lived in the layers between the code and the visitor.
One example: logged-in admins saw 657 races on our race calendar, while logged-out visitors saw 638. I had already cleared WP Super Cache and purged Cloudflare. Nothing changed. So we walked the stack one layer at a time: Cloudflare edge, the host’s page cache, object cache, server cache. Each one got ruled in or out with a header check.
The answer was WP Super Cache after all. In Simple mode it caches URLs with query strings, and I had only purged the plain /races/ page. Dozens of filter combinations like ?country=Venezuela were still serving old snapshots.
Race day was the same story in reverse. Our custom REST endpoints appended a timestamp to every request to “keep data fresh.” That made every page view a cache miss, so an expensive SQL query ran on every single page view. Removing two cache-busters and adding one Cloudflare cache rule did more than any server upgrade could have.
The lesson: before reading code, draw the stack. Then prove each layer innocent, one by one.
2. Check the output, not the mechanism
“I cleared the cache” describes an action. “The page now shows 657” is a result. Only the second one matters.
The cache fix finally landed when we stopped testing the homepage and ran curl on the exact URL the search form produced. That returned 638. The same URL with a random &nocache= parameter added returned 657. That pair of requests proved it was a stale cache, not code or data, faster than any amount of reasoning.
I saw the same pattern elsewhere:
- Remote desktop over Tailscale. Before touching any settings,
ncandlsofshowed the Mac was listening on port 5900 and reachable. That ruled out the network and the server in two minutes and pointed straight at the client config. - Missing email after a DNS move. The MX record was already correct when I checked it. But the old record had a 14,400-second TTL, so mail kept bouncing until caches expired. “I fixed the record” and “mail arrives” were hours apart.
- Race day. Success wasn’t “PHP-FPM is enabled.” It was watching load fall from 67 to about 1.
The lesson: define what “fixed” looks like as something you can observe, then go observe it.
3. The platform is part of your code
Code that works in isolation can break the moment it lands somewhere real. The host environment has opinions, and it rarely announces them.
- WordPress quietly stripped my styles. A race landing page I pasted into a Custom HTML block showed up completely unstyled. The giveaway was that the countdown still ticked: the JavaScript survived, but the
<style>block was gone. The fix was to put the CSS in Additional CSS and only markup in the block. - The theme fought back. Even with the CSS in place, Hestia’s padded container and global link styles bled into the page. A
100vwbreakout and a few targeted!importantrules settled it. - A sandbox blocked my map. A Leaflet map in a preview failed because the sandbox blocks external scripts and tile servers. I rebuilt it as a self-contained SVG with no dependencies at all.
- Safari has two memories. In my Keep in Touch app, Safari and the Home Screen version of the same site keep separate local storage. So do different addresses. “My data vanished” was really “I opened it from somewhere else.”
The lesson: treat the CMS, theme, browser and sandbox as dependencies with their own rules. Find those rules before you assume your code is wrong.
4. Tiny details, big consequences
Some of my longest debugging sessions ended on a single character.
- One colon. I couldn’t reach my Mac over VNC with
100.x.x.x:5900. RealVNC reads a single colon as a display number, not a port, so it was knocking on the wrong door. A bare IP, or::5900with two colons, worked right away. - One script tag in the wrong place.
L is not definedmeant my map code ran before Leaflet had loaded. Moving the script tag to just before</body>fixed the race condition. - One sort key. Our race calendar listed January 2027 races above August 2026. It sorted by
cal_month, which stores only 01 to 12. Sorting by the fullrace_dateputs years in the right order.
None of these were hard problems. They were hard to see, because everything around them looked right.
The lesson: when the whole system checks out and it still fails, read the inputs character by character.
5. AI is sometimes confidently wrong, so ask for evidence
The AI was a great partner, but not an oracle. Its mistakes sounded just as sure as its correct answers.
- A guess presented as fact. I asked what stack a tracker site used. The first answer was “Astro and Tailwind.” When I pushed for proof, it admitted it couldn’t see the raw HTML. Once I pasted the page source, the real answer was plain HTML, vanilla JS and Mapbox on Cloudflare Pages.
- Plausible but useless geometry. I asked for a GPX of a bus route. Hand-estimated coordinates drew a straight diagonal line across Atlanta instead of following I-75. Two revisions later it was still wrong. The real fix was real data: the transit agency’s GTFS
shapes.txtfile.
The second experience changed how the AI handled the next one. When I later asked it to trace a 60-mile running route from an illustrated map, it refused to invent coordinates. It gave me a labeled skeleton file and told me to snap it to real trails.
The lesson: ask “how do you know?” Make the AI point to a primary source: page source, a log, a database query, an official data file. A good assistant will tell you when it’s guessing.
6. Don’t edit what you don’t own
The changes that aged best were the ones that sat beside other people’s code instead of inside it.
- A redesign as a drop-in file. Our race director portal is a custom plugin from another developer. Instead of editing it, I added one must-use plugin,
byu-hub-polish.php, that layers new styling on top. Their updates can’t erase it, and deleting one file reverts it. - Config over copies. For a race series landing page, I put everything that changes between races (name, dates, colors, tracker links) in one
RACE_CONFIGobject at the top. The live tracker kit I built for a 250-mile relay works the same way: oneconfig.jsper event. - Check before you remove. Before deactivating a membership plugin I wasn’t sure I still used, we checked for restricted pages, shortcodes and site-wide settings. Deactivating it could have quietly made private pages public.
The lesson: keep your changes in places you control, and make them easy to undo.
How I work with AI now
Here’s the checklist I actually use when something breaks:
- Describe symptoms, not theories. “Admins see 657, visitors see 638” beats “I think the cache is broken.”
- Say what you’ve already tried. It saves a round of “have you cleared the cache?”
- Run the commands yourself and paste the real output. The AI suggests the test; the server gives the answer.
- Go one layer at a time. Rule each layer in or out before moving to the next.
- Agree on what “fixed” looks like before changing anything, then verify that exact thing.
- Ask “how do you know?” whenever an answer sounds certain but wasn’t checked against a source.
- Prefer changes you can undo. Drop-in files, config values and feature flags over edits to other people’s code.
- Write it down. After the race-day crash, I kept a handoff doc with the root cause, timeline, changes and follow-ups. The next fix starts from there, not from memory.
The real takeaway
AI didn’t make me skip the fundamentals. It made them faster. The habits that solved these problems are the ones good engineers have always had: isolate variables, measure instead of assume, respect systems you don’t own, and write down what you learn.
What changed is the speed. On race day I had a patient partner who knew which command to run next while the site was down. But I was the one who ran it, read the result and decided whether to trust it. That split feels right to me, and it’s how I plan to keep working.
What’s the smallest bug that cost you the most time? I’d love to hear it.