- Consulting / Coaching
- Jeff’s Blog
I enjoy writing, though I’m not as prolific as I’d like to be. I tend to re-read what I wrote many times, often second-guessing it. Like any writer, I also find difficulty from time-to-time, getting stuck on how to phrase a paragraph or a sentence. I’ll typically mark things and return to them later. All-in-all, I can slam out text, but getting polished product out the door can be challenging.
I read and enjoyed Weinberg on Writing: The Fieldstone Method when it came out. I already had been employing its core idea to an extent: capture thoughts as they arise (index cards work great!), then incorporate them as appropriate in an article or book. Each idea on a card might act as a central idea around which writing can build, or it might be used to fill in a “hole” in the text. Emphasizing this technique has made writing a lot more fun, and I tend to not lose great thoughts that had popped into my head.
My best writing experience, however, has been collaborating online with Tim Ottinger, originator and co-author on Agile in a Flash. We had a slow winter–after writing a pile of articles for PragPub, we both got very busy with work, and did little writing. We finally found some time to commit to delivering a new Agile in a Flash post, Is Your Unit Test Isolated?
Two days ago, we got our headsets on, started up a Skype conversation, and talked for a few minutes to agree on what we’d work on. We started some single-threaded editing on a very rough draft at Blogspot, but that wasn’t effective, so I pasted it all into a Google Doc.
Google Docs doesn’t provide the best document editor in the world, but it is highly effective for collaborative writing. You can see each others’ cursor and typing as it occurs, and you can look at offline revisions if necessary for comparison.
Mostly we’re not talking via Skype (and in fact sometimes we’ll collaboratively edit without a voice conversation at all). Instead we’re communicating through the document. Tim will start re-working a paragraph that I might have just finished writing, and I’ll get out of the way. Sometimes I’ll watch and toss in a comment–just typing it right into the document–and other times I’ll go off and stab at a paragraph that Tim wrote. Or I might go off and re-read the document, looking for flow and completeness problems. It’s pretty haphazard, yet it works, and it’s fast. We might go back and forth at a paragraph a few times before we figure it works.
Collaborative writing works best if you grow a layer or two of skin. We’ve learned to be pretty direct: “[[ I don’t like that ]],” Tim might type (the brackets are our way of setting off comments). Ok! Good technical writing requires harsh critique. Better Tim drop a pointed comment than a few thousand readers.
At times I’ll return to find something I’ve written completely gone or gutted and rewritten. It was a little disconcerting at first, but I’ve learned to trust Tim’s reworkings. I can always look at the old revision if I thought an important idea got deleted.
The unit test article took us about an hour to get to 98%. We let a couple outstanding concerns sit for a few hours, made some offline changes, and published it the following morning (yesterday).
As with pair programming, I’ve found such highly collaborative pair-writing to go much faster than writing on my own, and produce a far more effective product.
Recently I’ve been getting a good number of calls from C++ shops interested in doing TDD, despite my heavy Java background. I took on some of the business and had to turn away some to avoid being swamped. Many other folks I know (name dropping time!)–Tim Ottinger, James Grenning, JB Rainsberger, others–have also reported doing C++ work recently.
Is TDD finally taking hold in C++ shops? Does TDD even make sense for C++? I think so, and two current customers believe they’ve been seeing great benefits come from applying it. Building and delivering a C++ TDD course recently helped me come back up to speed in the language to the point where I was comfortably familiar with all of the things I hated about it. 🙂 It makes no sense to take such a difficult language and stab at it without the protection of tests.
I’ve been simultaneously writing more (after a typical winter writing freeze) and looking at Erlang–a much cooler language, challenging in a different kind of way. Meanwhile, my editor at PragProg has been asking for new book ideas. Here were some of my thoughts:
No matter how hard I try to run screaming from C++, there it is right behind me. It’s indeed a powerful language, and there is gobs and gobs of code written in it, and it’s about time we started trying to figure out how to make the best of it. It’s not going away in my lifetime. I also think C++ programmers are not well-served in terms of writings on TDD out there.
So… I decided it was going to be TDD in C++. Tim Ottinger and I put together and just sent out a proposal for a book tentatively named TDD for C++ Programmers (with a catchy subtitle, no doubt). We hope there’s enough demand and interest to get the proposal accepted. If all goes well, we’ll be soliciting reviewers in a few weeks.
I look forward to writing again with Tim! More in an upcoming blog post about our collaborative writing experience.
Your legacy system presents you with a choice: Put a stake in the ground and attempt to do something about the daily pain it causes, or let it get incrementally and certainly worse. Often its challenges seem insurmountable–small improvements appear like ripples in the ocean. Perhaps an investment to improve the software product is not worth the return in value, given its future.
Chances are that most of the time, though, you’ll be stuck with your legacy system for some time coming, and it makes sense to put a foot down to stem the degradation. Working Effectively With Legacy Code provides you with primarily tactical guidance for preventing entropy, focusing mostly on individual code-level challenges. The Mikado Method provides you with primarily strategic guidance, focusing mostly on managing large-scale refactorings across a code base (legacy or not).
In terms of where to put your effort, the best strategy is always to begin with the areas of current impact–what needs to change for the feature you’re currently tackling? But if you can afford additional effort to add characterization tests–i.e. increase “confidence coverage”–consider this quadrant that pits rate of change against defects for a given class.
How might you determine what classes belong in which quadrants? Tim Ottinger devised a heat map while at one shop, a simple Python tool that cross-referenced JIRA tickets with trouble tickets reported in Git. (I got to help build it!) You might consider filtering the defects to ignore those with low severity.
The Trusty Old Engine. “If it ain’t broke, don’t fix it!” Classes that work and aren’t changing should be the last thing you worry about. Sometimes code in these areas is tempting: it may exhibit obvious duplication, for example, that a refactoring weenie might view as a quick-win opportunity. Don’t waste your time–there are bigger fish to try.
The Can of Worms. You should have very few classes in this quadrant–having lots of stable classes that exhibit many defects suggests a reset on how your team does triage. Fixing these troubled classes might seem like opportunity to make your customer happy. Open the code up, however, and you’re likely to find fear-inspiring code. You might view this quadrant as a sandbox for ramping up on legacy code techniques: Classes here will provide you the time to experiment without having to worry as much about contention issues with the rest of the team.
The Mystery. Getting all the tiny bits correct in the face of rapid change is a significant challenge. A single, low-defect class with lots of change could suggest at least a couple things: The changes are generally trivial in nature, or the class rampantly violates single responsibility principle (for example, a large controller class that doesn’t simply delegate but also does work). With respect to the latter, perhaps you’ve been fortunate so far, or perhaps you have good test coverage.
It might be worth a small amount of effort to prevent any future problems in such an SRP-violating class. The nice thing is that splintering off new classes can be a fairly quick and risk-free effort, one that should begin to isolate areas of risk. You might then be able to code a few key characterization tests without extreme difficulty.
The Garbage Disposal. Usually there are at least a few classes in your system that are very large and fraught with problems, and they change all the time. This is the obvious place to improve your confidence coverage.
Good design, which starts with the application of the SRP, starts to solve many problems that the garbage disposal represents. Small, single-purpose classes provide more opportunities for reuse, make performance profiling easier, expose defects more readily, and can dramatically improve comprehension of the code, to name a few benefits. A garbage disposal is a dangerous place–with lots of hands in one big sinkhole, contention is frequent and merges can be painful. Design that adheres to the SRP thus also makes continuous integration easier.