There's a particular sort of bug that gets worse the harder you look at it. You know the one. It's reproducible, it's localised, you've narrowed it to a handful of lines, and yet every time you read those lines they look correct. You add a print statement. The print confirms exactly what you already believed. You add another. Same. After an hour of this you're not debugging any more, you're just rereading the same paragraph of code hoping it'll confess.
I had one of those this week. An off-by-something in a date calculation that produced the right answer for most of the year and a subtly wrong one for a fortnight in late autumn. The clue, with hindsight, is screaming in that sentence. But hindsight wasn't available at three in the afternoon with the heating on and my eyes glazing over.
the part where I gave up, sort of
At some point you have to accept that the next hour at the desk will be worse than the last one. I've learned to recognise the feeling: a kind of narrowing, where I stop forming new hypotheses and start re-running old ones. That's the cue. So I shut the laptop, which felt like surrender, put on a jacket and overshoes because it's November and the roads are filthy, and took the bike out.
It was about four degrees, low sun, that lovely flat autumn light where the hedgerows are all rust and the lanes are covered in wet leaves. I wasn't trying to think about the bug. That's the important bit, and it's the bit people get wrong when they're told to "go for a walk to solve problems". You can't go and deliberately not-think about something. You just go, and you pay attention to the road, the cold air, the dog that always barks at the same gate, and your back-of-house process keeps turning over without you supervising it.
I'd resisted going out, for the record. The little voice that says "if you just push a bit harder you'll crack it" is loud and persuasive and almost always wrong past a certain point. It's the same voice that keeps you reloading a page that isn't going to change. Part of what's taken me years to learn is that the voice doesn't get quieter, you just stop obeying it. The clincher this time was noticing I'd added the same debug line twice in different places, which is the unmistakable sign that I'd stopped reading the code and started performing the act of reading it.
the answer arrives unannounced
About forty minutes in, climbing a small hill I've climbed a hundred times, the answer simply presented itself. Not as a flash of genius. More like remembering where you left your keys: an unglamorous "oh, of course". The date library I was using counts months from zero in one place and from one in another, and my code had crossed that boundary without me noticing. The fortnight that broke was the seam between two months where the off-by-one finally produced a value wrong enough to fail a check rather than just quietly shift a day.
I didn't turn round and sprint home. The fix would keep, and it was a good ride. But I held the thought gently for the rest of the loop, the way you carry a full mug across a room, and when I got back it took about four minutes to change one line and watch the test go green.
why this works, as best I can tell
I don't think there's anything mystical here. When you're locked onto a problem, you're holding a particular model of it in your head, and that model is the thing that's wrong. More staring just reinforces the broken model, because you keep checking the code against the same flawed assumptions. The whole value of stepping away is that the model decays. You stop being so sure. And when you come back, or when your mind drifts back to it mid-ride, you rebuild it from scratch and the new version doesn't have the same blind spot baked in.
There's a related trick that works at the desk, and it's the reason rubber-duck debugging is real rather than a joke. Explaining the problem out loud, to a colleague or to an actual rubber duck if the office is empty, forces you to rebuild the model in words. The act of narrating exposes the assumption you've been silently making, because you have to say it, and the moment you say "and then it parses the month, which is one-indexed, obviously" you hear yourself and stop dead. The bike ride is the same mechanism wearing different clothes. Both of them pry your hands off the broken model long enough for a better one to form.
What stepping away doesn't fix is a problem you haven't actually understood yet. If you're genuinely missing information, no amount of fresh air conjures it. The ride helps when you have all the pieces and you've simply assembled them wrong, which, if I'm honest, is most of my hardest bugs. The data was always there in the failing test. I just couldn't see it for looking.
The bike is incidental, really. It could be a walk, a shower, washing up, the drive home. What matters is that it's absorbing enough to stop your conscious mind from grinding the same gear, but not so demanding that there's nothing left over. Cycling on familiar lanes is perfect for me because the route needs just enough attention to keep me present and not nearly enough to occupy all of it.
My partner finds this faintly ridiculous, the idea that the most effective thing I can do for work is leave it, and to be fair it does sound like the start of an excuse. But the evidence has piled up over the years. The bugs I've cracked on the bike, in the shower, halfway through the washing up, vastly outnumber the ones I've ground out by sheer stubbornness at the keyboard. The stubborn ones were usually fixed quickly the next morning anyway, which rather makes the point about diminishing returns.
The thing I keep having to relearn is that this isn't slacking. The forty minutes off the desk were more productive than the two hours on it. I'd just rather the lesson stuck, so I didn't have to spend the two hours first every time. Until it does, I'll keep the overshoes by the door. November debugging needs them.