Making Debugging Visible

Learning a debugger is frustrating because the work is invisible. You’re stepping through execution paths you can’t see, inspecting variables whose values may change, and setting breakpoints on lines that may or may not get hit. The feedback loop between action and understanding is abstract. Students set a breakpoint, step through some code, and still don’t know if they’re any closer to finding the problem.

When I built my Xdebug course for CraftQuest, I wanted to solve this. What if debugging progress wasn’t invisible? What if fixing a bug in code made something happen on screen that was immediate, satisfying, impossible to ignore?

So I built a plugin called Buggy that spawns animated bugs in the Craft CMS Control Panel. These visual bugs represent actual software bugs in the code that students need to find and fix using Xdebug. Fix the defect, and the bugs disappear.

Bugs animated gif

When you first install Buggy, the page reloads and bugs start appearing one by one, crawling across your control panel until the screen is swarming with them. It’s unsettling in a way that’s hard to ignore. They keep moving, and there are enough of them to feel like an infestation. You want them gone.

So you follow the video course. You set a breakpoint, step through the code, and find the defect in the getSwarm() method. You fix it, reload the page, and watch the swarm of bugs shrink. Not gone, but noticeably smaller. That’s your feedback: visible proof that what you just did actually worked.

Two more bugs remain in the plugin code. You follow the course, debug each one using step debugging with Xdebug, and another wave of insects disappear from the screen. After the third fix, you reload the control panel and it’s clean. No bugs, no congratulations message. Just the control panel interface, the way it should be.

A bug-free control panel means you’ve finished the exercises in the course. No quiz score, no congratulations message, no “you passed” modal. Just the clean interface you earned, and the skills to find the next bug in your own code.

The Implementation

If you want to build something similar, the key challenge is running two versions of the same functionality: one that ties bug counts to actual code defects, and one that allows manual control after students have fixed everything.

I solved this with a helper class that acts as a service dispatcher:

public function getService(): BugService|BuggyService
{
    if (Buggy::$plugin->getSettings()->automaticBugSpawning) {
        return Buggy::$plugin->buggyService;
    }
    return Buggy::$plugin->bugService;
}

Controllers and template variables call this method without knowing which mode is active. When automatic mode is enabled, BugggyService runs internal checks against the codebase and returns a bug count based on how many defects remain. When automatic mode is off, BugService lets instructors spawn bugs manually for demonstrations or additional practice.

The pattern is simple, but it keeps the teaching logic cleanly separated from the rest of the plugin.

Runsheet

This month marks 11 years since I started running as a way defense against aging (but mostly a mid-life crisis) and actually identify as a runner. You know, the person who won’t get out of bed unless there’s at least 5-6 miles to run and has more running shoes than regular shoes.

For over 8 years I’ve been a member of a local running club. On long runs, the coaches hand out small strips of paper with the turn-by-turn directions. I know they spend a lot of time writing these up, so I figured there had to be a better way.

I created Runsheet, a lightweight tool that takes GPX data (uploaded, as a Strava route or from your Strava account) and converts it into turn-by-turn directions.

Runsheet does make it easier to generate the turn-by-turn directions. But not without some complications.

First, GPX data is riddled with noise. So many waypoints and many of them meaningless. If you cross the street while running, this could be interpreted as a turn, but it isn’t.

Second, while our GPS-enabled running watches also use this same GPX data to guide us with turn-by-turn directions from our watch, they have a significant advantage of knowing your location and position. Runsheet is just a dumb app, so it has to do a good job guessing (while also filtering through so many useless GPX waypoints).

This makes for a lot of hand-wavy data decisions and, well, frequent mistakes. This isn’t software that is flying airplanes or used in surgery, so the stakes are about as low as possible. However, I wanted to get it right.

To give users some flexibility, I added the ability to remove a line of directions, update the line of directions, and insert a new line. Sometimes this can be handy for adding a note (“water cooler at end of street”) or just removing egregiously bad directions that the app just couldn’t get right.

This is very much a niche side project but maybe you’ll find it helpful. If you do, let me know.

See my other projects

Finished reading: The Running Ground by Nicholas Thompson 📚

I don’t read many running books these days but I saw this one mentioned somewhere and decided to make it a Kindle read.

Thompson is obviously a gifted journalist and writer but the story always felt at arms length for me and self-indulgent. I didn’t connect with it like I have other memoirs.