A dialog between two PairProgrammers:
A: Okay, I just got back from { the restroom | my XSO's booty call | walking the CFO's Yellow Labrador }. What's up?
B: How do I log a frim-fram to the system log?
A: Why are you in _that_ module?
B: The tests stopped passing. I need to log the priority details of the third and fifth frim-fram.
A: You had to MonkeyPatch the 3rd-party Fram module just to write that trace statement!
B: I'll take it out after I figure out the priority of the third and fifth--
A: How long have the tests been failing?
B: How long have you been gone?
A: Sorry. This is why we don't use a debugger here.
B: So I have to learn how to MonkeyPatch..?
A: We are allowed to use a debugger. We are just never _motivated_ too.
B: Okay.
A: We debug all the time. If we can't get a test to fail for the right reason, we add an extra "print foo", or an extra assert{ foo == 42 }. Then we run the tests again, just to see foo's current value. The difference is we have the option to not debug like that. Ideally, if we had a true GreenfieldApplication, we could never debug at all.
B: Nirvana.
A: Foo Fighters. But...
B: Why can't I debug this?
A: Because it's too hard, so the CostBenefitRatio? is way off. If we had no tests, we would have no choice but to debug. That's called PunitiveDebugging?, and it's what most developers do, most of the time. We do anything to avoid that.
B: So what can we do?
A: Did you make any valid changes while you were debugging?
B: Of course! I wrote three new tests, trying to see what was going on!
A: Props. Tests are illumination. But you didn't integrate them, right?
B: Of course not!
A: Okay, run svn diff.
B: It shows all my changes... I added a TODO... There's the MonkeyPatch.
A: Okay. run svn diff >../project.patch. Then close your editor. We don't trust it to pull up changed files.
B: Okay. Now what?
A: svn revert . -R
B: That won't fix the bug!
A: Of course not. It will get rid of the change that caused the bug. We don't even care what that change was. When we manually apply the project.patch, we might not even notice what caused the bug. We don't care.
B: Okay. All the code from the last successful integration is back.
A: And the build server is green?
B: Green! The bug is in the patch file now.
A: And we won't just "apply" it with the patch command, either. Okay, open the editor and load the project.patch file.
B: Woah it's all colored!
A: Modern editors know the syntax highlighting for .patch format. Now find the EEEEASIEST possible change, and apply it.
B: Just this TODO, right?
A: Copy it, paste it into the project, and INTEGRATE!
B: Uh, ookay.
A: If we couldn't integrate for a while, we respond by integrating too often.
B: Gotcha. It's integrated.
A: You can do those TODOs in a batch. After each one, erase it from project.patch.
B: Okay, now I should find an easy change, and integrate it?
A: Yeah!
B: What if the bug comes back?
A: Notice, if we do the changes in order from easy to hard, we should understand each change. The odds of rebugging, from the patch, are super-low.
B: So I don't apply the test that made me change the code to create the bug yet, right?
A: Sure; just apply that refactor. Copy it from the patch file, remove the + ticks, and remember to trigger its tests.
B: Okay. Now this one... and this one.
A: Add your exploratory tests.
B: Okay. Now I add the test that made me break the code... Hey! It passed!!
Branch
A: Imagine that. Now disregard the rest of that patch file and get back to work.
B: What happened?
A: You had a bad case of debugitis. Your debugging itself broke the code, and you couldn't tell when you actually made the test pass.
B: And if I had reverted at the first sign of trouble, I wouldn't have needed to make that patch, because I wouldn't have made any new edits without a GreenBar within reach.
A: Yep!
If "debugging" causes a program to break, then the patch file will include what broke the program. When you integrate it, it'll break again.
Please read it again - I would hope to think I'm not such a bad author. Nobody said "integrate it again". Nobody said "use LarryWall's patch program. The verbiage asks, and answers, (twice now) what happened to the bug, still lurking in that patch file...
Nobody implied Larry Wall's patch program (I often use vim to apply patches manually, since LW's patch VERY often gets things wrong due to intervening svn updates!).
I've read it four times already (including since your last refinement), and I have to concur -- this exchange is implying that simply re-applying the patch, albeit perhaps not in the original order they were made, will cause the bug to magically disappear. If two people have complained about this already, then I suggest that a refinement/clarification of what you tried to express is in order. Well, three times now. --Samuel A. Falvo II
You are not a bad author. I really enjoyed reading it this way. The problem is that the ending is - well - too HollywoodOs like. Debugitis is not so harmless. The problem may look to have disappeared, but the problem uncovered by it still lurks somewhere. The prose really should make this clear. So continuing at "branch":
A: Uhh oh. Then it must be a case of debugitis.
B: What happened?
A: Your debugging itself broke the code, and you couldn't tell when you actually made the test pass.
B: And if I had reverted at the first sign of trouble, I wouldn't have needed to make that patch, because I wouldn't have made any new edits without a GreenBar within reach.
A: Yep! But now we still have whatever caused the debugitis in the case. Possibly a race condition.
B: How do we find that?
A: If we try to extend this feature we might start to notice if there's trouble in this area, and we will remember to take smaller edits. We will spend more time running tests, and as our edits change the timing we might notice a pattern. Such as reverting too often.
B: What if we still have a bug, right now, that the tests don't expose?
A: The stress of frequent refactoring and testing is like fault-injection testing. But there's still no reason to debug for a long time, just to add a feature. We might decide to debug for a long time to isolate the problem. It's still not punitive debugging, because we still have the option to stop, integrate, and add other features, even if we know the really hard bug is still there.