Note: This is not the article that was originally in this location. Mr Brown objected to that article for reasons I do not fully understand. Since I certainly never want to offend anyone, I removed the old article, replaced it with a placeholder, and am now replacing the placeholder with this article.
Mr Brown has posted this responding article and asked that it be linked, which I do with pleasure. I recommend reading other articles on his blog as well.
The previous article discusses Bob Martin’s “Programmer’s Oath”, responding to some tweets of Mr Gregory Brown, and my own concerns. That article includes some quotes Mr Brown sent me to clarify his concerns. Today, I take as my starting notion some key words and phrases from those quotes. These are my thoughts about those words, not thoughts about any particular individual.
Warming up …
Here are the full quotes again:
Gregory believes #2, #3, #5 come at a significant cost to uphold, both in technical learning time and hard-won understanding of the problem being worked on. Their potential benefits are variable, but the costs are fixed and paid up front.
There are circumstances where maximizing #2, #3, #5 is optimal, with sufficient resources and a sufficiently stable and learn-able problem domain. But when time, money, and stability of the business domain are limited, it’s a matter of finding the right balance between technical optimization and growing your understanding of the business and its customers, and their needs.
In other words, you have to look out for both the opportunity cost of committing to #2, #3, #5 as well as their potential for diminishing returns when considered in the full context of real work on products.
Investing a level of effort in technical concerns that is “fit to purpose” is optimal, and sometimes this means 100% effort on code quality concerns, other times it becomes a more complicated question. Experience, practice, and discernment are the guide that will tell you what to focus on, when.
These phrases strike me as significant and interesting enough to write about here:
- significant cost
- finding the right balance
- diminishing returns
- level of effort
- fit to purpose
- experience, practice, and discernment
Now as I read the full quotes above, as i focus on those selected phrases and try to absorb them, this is what I comes out:
Surely there must be important concerns beyond the code quality Bob talks about in the Oath. We’re being paid to build a product, and to the people who are paying us, the product is most important. Therefore we must build that product in a way that balances the cost of building it and maintaining it with the needs of the business and the users of the product. Bob’s Oath seems to go too far toward some technical concerns, and not far enough toward the business needs.
Writing a test or improving the code has a cost, often a “significant cost”. Sometimes that cost is worth it, because tests help prevent defects, and code improvement helps us go faster as we add more capabilities to the system. But it’s easy to come up with tests that don’t add information, and easy to come up with tweaks that maybe improve the code but won’t really help us go any faster.
We CAN do too much!
When Chet and I demonstrate pair programming and TDD, we generally use a bowling scoring example, because it’s complex enough to require some refactoring and simple enough to fit into an hour or ninety minutes. If you search for “bowling” on this site, you’ll find pages and pages of examples.
When we do this exercise, it goes roughly like this:
- Test all gutter balls, driving out a starting class and method with a “fake it till you make it” implementation.
- Test all open frames, driving out a simple summing of the rolls of the game.
- Test a spare, discovering that our summing solution can’t bear the weight, forcing us to refactor to add the notion of a Frame, and then to deal with the bonus roll.
- Test a strike, exposing the fact that not all frames are two rolls long, as we deal with the strike’s two bonus rolls.
At this point, the program is usually complete, although if we have chosen our tests poorly, sometimes it isn’t. If it isn’t, the fact will shortly come out. Almost always, however, the audience are not convinced that it works, usually because they think the “extra frames” at the end of the game are not handled.
We emphasize a key learning point at this moment in our demo, which is that when someone thinks the program doesn’t work, a productive thing to do is to come up with a test that the program will fail, not just argue about whether the code works or not. So we come up with a test, usually a perfect game, all strikes, and usually it works. Sometimes it fails and we discover a mistake. (It’s always the same mistake, by the way.)
Usually, by now, everyone is convinced that the program works. If they aren’t, we do a few more tests until everyone is convinced.
As to cost-effectiveness, I would argue that every one of the tests so far has paid back its cost. Each one has driven a requirement into the code, or has raised our confidence from “this doesn’t work yet” to “this does work”. I suggest that it’s clear that we need to check all the cases we can think of to be sure the program works.
Then we do a thing. Long ago, in Omaha, I worked with a team that had the Nebraska women’s bowling champion as a member. In the course of our demo, she told us an interesting and counterintuitive fact: Every bowling game consisting of alternating strikes and spares has a score of 200. I still find that amazing. So we tell that story, and we write that test (or sometimes two, strike/spare and spare/strike).
The alternating tests are fun, but they do not really provide any more certainty than we already had. Strictly speaking, they are not worth the cost of doing them.
Therefore, it is possible to do too much technical work in delivering the value needed.
How do we resolve this?
Well, picking from our key phrases above, we have to use experience, practice, and discernment to find a level of effort to put into our testing and refactoring. We need to find the right balance of internal technical improvement versus building the capabilities the business needs. We need to be sure that our technical work is fit to purpose. We need to avoid diminishing returns from our technical polishing, as in the case of the alternating strike / spare test. If we do not do that well, there could be significant cost added to the project, resulting in less capability than we might otherwise have had.
Wow, all put together that sounds like your typical corporate mission statement. Nonetheless, there’s truth there. Let’s drill down a bit.
Certainly we could write tests forever, or polish our variable names forever. In principle, we could get to a place where no real work at all took place. And I’ve written elsewhere of teams who did nothing but write tests for months, or nothing but “refactor” for 40 days and 40 nights. Those things were not productive.
The Oath makes no concessions to getting things done, as I read it. It’s all about doing the craft well. Since Uncle Bob is the founder of the Craftsmanship movement, it’s no surprise that he writes in those terms. But here in the “real world”1 we have to be more pragmatic and find a balance. The Oath doesn’t say that but it’s true.
Unfortunately, as our “mission statement” above says, it requires experience, practice, and discernment to do a good job of balancing code craft matters against building capability. I just have two points to make on that:
People new to the profession, and people new to the XP practices, do not have the experience, practice, and discernment to make good decisions. I believe, based on years of evidence, that people with less experience under-estimate the amount of testing and the amount of refactoring they need. They almost always do too little code crafting, and rarely do too much. That makes me want to couch my advice about craft on the strong side, closer to saying “always do this” than to saying “do this as you see fit”.
I am not new to the profession. I started writing software in nineteen sixty fucking one and I wrote some code yesterday. And I am here to tell you that very often I do not test enough, and defects creep in. Very often I do not improve my code enough, and changes I need are harder to do than they should be. I am far more likely to do too little craft than too much. This makes me want to push hard on “always do this” rather than “as you see fit”.
Whether built with too little experience, or with a great deal, most of the code I see could do with more craft, not less.
What it is
Yes, Bob’s Oath goes too far: no one could be that crafty. Yes, it is possible to do too much testing, too much code cleaning. Yes, it takes judgment, brains, and maturity to do the job right. Yes, the application of craft has to be in balance and fit for the purpose and yes, we have to keep our eye on the business and customer needs.
In my work, I read a lot of code, I talk with a lot of programmers, and I program every week if not every day. Yet I have never seen a real code base that had too much testing in it, or whose code was too clean.
Nonetheless, if you’re sure you’re spending too much time and effort on code quality, by all means do less. Observe what happens, tune your practice.
But when you’re just wondering whether you’re doing the right amount of testing or code improvement, my bet would be that you’re doing too little. Make your own decision, but my bet is that you’re not going too far on quality. Going too far is quite difficult to do.