- Consulting / Coaching
- Jeff’s Blog
Student: “I know that I’ll need a HashMap somewhere in my very near coding future. Why shouldn’t I just introduce it now?”
Jeff: “This is one of the better questions about TDD, and one of the most common concerns about how it’s practiced. Do you really need a HashMap? Maybe you won’t by the time you’re done. By introducing this speculative design element now, you’d be creating complexity prematurely. You will incur additional cost every time you revisit the code in the interim. You might even end up with the unnecessary complexity forever.”
Student: “But I know I’ll need it in about 20 minutes.”
Jeff: “Maybe. Sometimes I’ve found that my most-cocksure design speculations were wrong, and TDD drove out a simpler solution. But for now, let’s say you’re right. With TDD, you’re learning how to think and build a solution more incrementally–a skill that can improve your ability to accommodate new, never-before-conceived behaviors into the codebase.”
Student: “Right, but this whole incremental thing requires some reworking of code. You told us that we start with the solution for the simplest case, which for this example requires only a hardcoded return value. Then we write another test that requires us to replace the hardcoding with a scalar. Then another test that requires segregating data, which I can do using a hash-based collection. Seems wasteful to write some code, only to throw it away a few minutes later.”
Jeff: “We’re trying to adhere to the rhythmic TDD discipline of red-greeen-refactor. Part of the success of TDD hinges on seeing a failing (red) test for a new behavior. One important reason: If you never see a test fail, you have little clue that it’s a legitimate test. When you write a more generalized solution than you need, writing a next test that fails first may be near impossible. Ultimately, not following the cycle leads to you writing fewer tests.”
Student: “Remind me, why is TDD and adhering to its cycle important?”
Jeff: “Foremost, the cycle lets us know we’re on track with building the correct code every few minutes. Also, as developers, we’re good about constantly creating lots of cruft–code that’s not as easily understood or maintained. In fact, cruft buildup is where a significant portion of your development costs rise. The cycle gives you continual opportunities to clean up the junk as soon as you create it–before it’s too late. The tests minimize your fear of making the necessary changes.”
“If you stick to the cycle, and write a failing test for each new behavior, you’ll end up with a set of tests for virtually all bits of logic you intended. These tests will document all behaviors you chose to drive into the system. Well-crafted tests can save everyone the countless hours otherwise needed to understand how the system behaves over time and as it grows.”
Student: “So, it’s also important that the cycles are short.”
Jeff: “Yes. It’s generally easier to find and fix problems when you find out about them sooner. Also, if you take larger steps, you’ll create more code than you’ll likely clean during the refactor step.”
Student: “But what about the wasted bits of code, the rework?”
Jeff: “You’re making a tradeoff. You are the tortoise, taking an incremental, steady route as opposed to the hare, who takes larger leaps requiring unpredictable amounts of route correction. The amount of code rework is often trivial, and if you learn to always take on the next-smallest case, the amount of new code is also small. You’ll learn to get good at taking small, incremental steps.”
Student: “Yeah, but rework?”
Jeff: “Let’s not forget that debugging cycles and defects represent rework costs that we rarely account for, and they are significant, even monstrous, on most development efforts. There’s also considerable cost when, however rarely, your speculation is wrong.”
Student: “Right, but I know more than just what’s needed for the current test case. Shouldn’t I create a design that accommodates all of that knowledge?”
Jeff: “It’s always valuable to create a design model given the needs you know, whether in your head or on the whiteboard. But don’t force that design into the system until the behaviors described by the tests demand it. And don’t spend a lot of effort on the details.
“As you test-drive, you can shift the design in the direction of your speculative model, as long as you’re keeping the code as clean as possible. Beck’s simple design rule #4 can help tell you when you’ve gone overboard: Avoid any additional moving parts or complexity than needed to pass the current set of tests.”
Student: “I would rather build the design to what I know I need over the next few hours.”
Jeff: “That’s your prerogative, though I suggest starting with the smaller, more incremental steps. It’s the better way to learn TDD: It’ll keep you honest, and generally result in more tests that describe the behavior you’re test-driving into the system.
“Once you’re comfortable with the TDD rhythm, you might take larger steps. I sometimes do. Much of the time, I end up ok, but every once in a while, I get bitten. That pushes me back in the direction of taking smaller steps.”
Student: “Right, but I know I need the HashMap. Here, see, I’m just about done, and there it is.”
Jeff: “Nice. But it turns out that the PO changed her mind. When you come in tomorrow, she’ll explain that we need to track things transactionally. The HashMap is the wrong abstraction for this–we want a time series collection. You’re going to have more rework than if you’d kept the design simple.”
Student: “Isn’t this rationale for spending just a bit more time on pinning down requirements up front?”
Jeff: ” One of the reasons we do agile is to allow for changes in direction based on feedback–from the marketplace, from the PO, from what we learn. TDD provides a cyclic mechanism that supports the cycle of agile and its goal of producing high-quality software that meets the current, changing needs of the customer.”
“TDD is all about continual, just-in-time, sufficient design. I like to say that design is so important, you can’t just consider it once. You have to address it continually.”
Student: “Sounds like it’s all your opinion. I’d like to hear from someone else.”
Jeff: Tim Ottinger also suggested that TDD gives you the ability to abandon the code after any integration step (which could be as often as every R-G-R cycle)–knowing it’s as clean and complete as the tests indicate.
“Tim also hinted at something else important: None of us are perfect, and many of our predictions about design are flat-out wrong. TDD provides continual humility lessons for most developers, helping us reign our natural hubris at minimal cost.”
Tired of the same old programming katas? Give this one a try!
In the game of Risk, you receive a card at the end of your turn if you captured at least one territory during that turn. If you end up with a proper set (to be defined shortly), you can redeem in on a later turn for bonus armies.
Cards either contain one of three symbols–horseman (H), cannon (C), soldier (S)–or are Jokers (J) that can act as any one of the three symbols.
This Kata has two parts. Please do Part 1 imagining that you know nothing about Part 2, i.e. minimally like a good TDDer should.
Determine whether or not a collection of three (selected) cards represents a valid set that can be turned in. A valid set contains three cards either all with the same symbol, or each will different symbols.
The user has selected exactly three valid cards–the UI constrains how many cards can be selected–so your solution should concern itself with no other possibilities.
Examples of valid sets:
C-S-J (with the Joker acting as an H)
S-S-J (with the Joker acting as an S)
Examples of invalid sets:
Determine whether or not a collection of 0 through 5+ cards (i.e. all the cards that a user holds) contains a valid set.
Five cards logically must always contain a valid set. The only case where a four card set is invalid is when it contains only two symbols, e.g. H-H-C-C.
This is not a long kata: My first implementation (in Java, I’ll try another language next time) that covers both parts is essentially a complex conditional with four conditional expressions, probably 20 minutes of effort. It did bring up a couple interesting questions as I ran through it.
I’d love to hear from you if you try this kata–please report back any interesting findings!
I was triggered to create this Kata after I saw a complex open source implementation (in a working Risk app) that required several dozens of lines of Java code. You can do much better.
My first implementation appears here.
You’ll find never-ending advice on how to best build software. Books, articles, and talks provide infinite words of wisdom. Consider replacing their advice with one overriding mentality: Stop wasting the time of other developers.
And so on. Each of these deficient approaches could be replaced with a better way of working that doesn’t waste everyone’s time. You can easily come up with more: What things do we do that cause our teammates, stakeholders, or users to grouse and grumble?
Code like you cared about other people. It’s easy to gauge whether or not you succeeded–just ask your teammates.
I’ve had the idea to write a book on refactoring for some time, and even produced a proposal a couple months ago. The focus would be “refactoring in the context of TDD,” aka continual design. I only got halfway through a sample chapter, which the publisher requests. (Writing the sample also reassures me it’s the book I want to and can write.) This past week I heard that a Big Name is currently working on a refactoring book rewrite. Hmm, maybe this project is not something that I should be doing.
Agile Java has been in print since 2005. It still sells copies–not a lot–and I also still receive thanks for the book from many folks who found it a great way to learn (TDD + OO + Java from the ground up). I feel a bit guilty that people still use a 12+year-old-book featuring a version of Java end-of-lifed for about 8 years! Time for a rewrite, as some have requested?
A dozen years later, what would change?
Otherwise, the tone and general flow would remain the same. Hopefully not the page length: Few folks want to read 750-page books anymore.
I’m the sort to find the stuff I wrote a while back to be lacking, so I might be tempted to rewrite a few things…
There are at least a hundred for-profit or open source tools to help you track your agile / Scrum project. Here is a list, from 2013, of 70 such tools.
If you’ve read Agile in a Flash or some of my blog entries, you’d quickly gather that I’m not a fan of most software-based project tracking tools–you’re better off using whiteboards and/or physical card walls. Introduce a software tool only if you must. And if your need is solely for reporting up to management (hmmm), remember that it’s the SM’s/project manager’s job to capture, consolidate, and distribute such information. Please don’t involve the rest of the team in this.
The tools I seek instead are those that help our team communicate and collaborate better. We’re 15+ years into “agile;” I’d hoped for far more collaboration tools than exist today. (What’s my excuse for not building any of it?)
IDEA, Eclipse, and even VisualStudio (with Resharper added) are powerful. To what extent do these tools directly support TDD? IDEA, Eclipse, and VS all support unit testing out of the box, but that scratches only the surface. While plugins exist for many of these interests, I’d like to see direct support for things like:
With TDD, tests and production code are inexorably linked. It would be nice to work in an IDE that supported TDD as the primary workflow for building software.
Pairing will never supplant loner development. But it’s still a preferred way of working for many developers, including those distributed geographically.
If we’re pairing and you’re sharing your machine and IDE, I’m stuck with your horrifying choices for keymaps (I need my vim!), colors, font, and font size. One or both of us must make concessions to the most-comfortable setup we would choose. How sad.
The problem of disconcerting configs would be reasonably easily solved if the major IDEs supported a universal config-switching scheme. Sets of profiles (one profile per IDE, I suppose) would be stored somewhere accessible via http. The first time I sit on your machine, I’d enter my profile-set location. The IDE would provide a universal hotkey that toggles between active profiles (right now, you would typically need to go through one or more Preferences/Settings pages to switch out all the preferences). My turn to pair? I press the toggle hotkey, and my preferred setup is loaded..
Another focus would include supporting accessibility considerations for pairs. Some folks need a considerably large font, for example. A pair-friendly IDE would provide the ability to easily sport multiple views across multiple monitors.
Using multiple machines might ultimately be a better world than my shared machine ideas above, even if I’m physically sitting next to you.
Screen-shared programming sessions usually suffer latency issues, particularly for the remote pair. If you’re a VIM or Emacs weenie, however, you can run an effective pairing session using tmux/tmate or screen. The character-mode interaction makes it almost as good as being there–particularly when coupled with cameras, quality audio, and a screen share so that you can see the UI interactions on the host machine. This is my preferred remote-pairing setup, but I admit that there are some great things I lose that IDEs like IDEA can provide.
Atom also supports a plugin called Motepair that “enables remote pair programming using Github’s editor.”
Subscription-based cloud IDEs like Cloud9, Codeanywhere (which has a free version), and Codenvy are an option, but for now, most of us would probably prefer to stick with our IDEA or VisualStudio or Eclipse. (And I’m resisting paying for yet another subscription…) Having not used these, I’m not sure how truly collaborative they are. Your experiences are welcome in the comments!
The subscription-based Floobits takes things one step further, offering the premise of allowing clients to use whatever IDE they choose… as long as it’s Emacs, IDEA, Neovim, Sublime Text, or Atom (no Eclipse or Visual Studio yet). I’ve not tried it, but it sounds fantastic. Koding appears to be another tool with a similar model.
Other attempts have been made to create remote pairing plugins for specific IDEs. I remember XPairtise, an Eclipse plugin, which was file-based (it synced at the file system level) and had no end of problems. Now there’s SAROS, a still-active open source initiative.
There’s little worse than watching someone struggle through typing story data into a tool like Rally. The tool often requires lots of clicking about, which can create a distraction for the rest of the team in the room.
In contrast, I’ve found simple virtual card wall solutions like Trello to be fantastic, however. I’ve run and been a party to iteration planning, retrospectives, and other distributed (and non-distributed!) meetings where Trello was the tool of choice. It’s highly tactile and responsive; all parties see changes as they occur. With plugins, things like dot-voting on cards promote Trello to a great general purpose tool for managing and tracking just about any collaborative meeting. Worst case is that you need to export the information to sync up another tool elsewhere.
Ultimately: I wish all applications supported true real-time collaboration. Google Docs is a great example of a tool that effectively supports live editing by multiple people.
If we really believe in agile, let’s insist that the tools we use and build all support collaborative capabilities, and not just as an afterthought.
With the goal of delivering quality software, I can help you with:
Want to hear more? Call 719-287-GEEK or use the Contact Me form to the left.