In response to my Developing a Sense of Smell sesion at XPDays Benelux this week, Patrick Debois has begun collecting the smells he finds in operations and sysadmin work. The thesis behind the Benelux session was that code smells offer a good vocabulary to help discuss code’s maintainability; and at first glance I think Patrick’s idea could start to do the same for system administration. Nice work Patrick!
Clarke Ching has achieved a major coup by posting a 1-hour interview with Eli Goldratt. In the interview Goldratt talks about his new book Isn’t It Obvious, which is another business novel about the Theory of Constraints, this time applied to retail. It’s a great book, and a very good interview — good job Clarke.
Bill Wake and I have been interviewed about Refactoring in Ruby by Russ Olsen for InformIT; you can read the interview here. (I note that my answers have been re-spelt the American way :)
Interesting link:
In Improving Code using Metric Fu the folks at devver.net give a little insight into how they have been using Reek and the other Ruby quality tools to improve their codebase.
A couple of days ago I made a little change to Reek and to my surprise 89 tests broke, which is bad. I tweeted about it, and soon @logosity had asked me to write about why it had happened. I know the same sort of thing happens to people all the time, and it can be quite traumatic in the otherwise placid flow of test-driven development. So I’m hoping it will be somewhat helpful to uncover the path that led me from blissful ignorance to test calamity with one keystroke. So without further ado, let’s do some root cause analysis…
Undesirable event:
I deleted an attr_reader that was called in only one place and 89 of Reek’s 360-odd rspec examples broke. Of these broken tests, only three were tests of SimulatedPolymorphism#examine_context, which was the deleted attribute’s one caller.
Why did the other 86 tests break unexpectedly?
Because they each indirectly invoked the one method that now could no longer work.
Why did they invoke the broken method?
Because they are integration tests, checking the co-ordinated behaviour of a dozen or so classes.
The broken method sits on a relatively new class, an instance of which is automatically included in the framework that those tests invoke.
The central class in Reek is the Sniffer, which obtains a syntax tree from a Source (eg. a File or a String or an IO stream) and then traverses it, firing events at registered SmellDetector objects as it encounters syntax elements they subscribe to. The broken method is in one of these listeners, and it is fired whenever the parser encounters a new class definition in the syntax tree.
Reek has a couple of hundred tests that each run a Sniffer over a code snippet, checking that the detectors find a specific set of code smells (and don’t find any others). The broken detector (SimulatedPolymorphism) is fired on any code fragment that includes a class definition; it turns out that only 86 of these couple hundred tests do that. The damage could have been much worse.
All of which means that the failing tests are “integration tests”, checking the overall behaviour of over a dozen classes under a variety of specific circumstances. Under some circumstances one of those classes is broken, so some of these integration tests fail.
Why are there so many integration tests in the unit test suite?
Because they are much easier to write than the tests for a single class would be. And they express the desired behaviour in ways that unit tests can’t. So I never took the time to write the unit tests.
Here’s an example of one of these tests:
describe ControlCouple do
it 'should report a ternary check on a parameter' do
code = 'def simple(arga) arga ? @ivar : 3 end'
code.should reek_only_of(:ControlCouple, /arga/)
end
end
The corresponding unit tests would confirm that:
- the code parser and method context objects conspire together to record the method’s parameters
- the code parser sends an event to the control couple detector when, and only when, it sees a conditional expression
- the control couple detector can obtain the enclosing method’s parameters
- the control couple detector records a smell warning only when the conditional expression tests the value of a method parameter
- the smell warning includes the correct information
- no other detector records any warnings for the given code fragment
Unit tests exist for some of these cases and not for others. The ease with which I can write readable tests has made me lazy: I often don’t write the unit tests, because I get new features into Reek much more quickly this way. The penalty occurs when a change breaks one of the detectors or (worse) something in the Sniffer, because then I get dozens of broken tests and it can be difficult to pinpoint the problem quickly.
There’s also another reason I got lazy: As Reek’s internal architecture stabilized many months ago, most of the new code I write consists of adding new smell detector classes, which usually slot right into the existing stable framework with few surprises or problems.
So the root cause of my 89 test failures is the combination of a well-tested, stable, practically defect-free framework together with a great way to quickly write readable integration test examples. Is that so bad after all, I wonder.
Am I going to fix the problem? Yes and no. Each time this happens in future I will add the missing unit tests that should have failed. But I don’t want to lose the expressive value of these integration tests, so I plan to (gradually) organise them into a different test suite more closely related to Reek’s cucumber tests. What would you do?
In Ruby, one easy way to check for palindromes would be to write a method such as this:
class String
def palindrome?
self == self.reverse
end
end
But the C++ part of my brain screams that this is likely to be very inefficient. So, today’s kata: test-drive a palindrome? method for String, ensuring (with tests) that it is much faster than the above.
I’m currently debating with myself on how and where to take Reek next. Specifically, I’m thinking about the FeatureEnvy / UtilityFunction class of code smell and their relation with classes and class methods. I’ve begun unravelling some thoughts over on the ruby-reek mailing list, and I thought I’d seek your opinion too. Much of what follows may turn out to be complete tripe…
There are always two views of any code, imo: specification-time and run-time. Code smells are, I think, (very nearly) 100% in the specification-time space; they deal with the code as written: does this name communicate well, is this code fragment coherent with those around it, will these two fragments change at different times, and so on; aspects of the specification that don’t affect behaviour. Classes are the means by which we describe the behaviour of runtime objects. It’s nice and sexy that in Ruby we also have runtime objects representing those classes, but I don’t necessarily see them as being the same beasts. One is a specification, the other is a runtime mechanism.
So one view of FeatureEnvy and UtilityFunction is that they help to improve the cohesiveness of the specification, by encouraging us to move code fragments closer to the state/data on which they operate. And this is where it begins to get hazy for me…
Consider a “helper” method that is only called by the methods of a single class, but which makes no use of the state of that class’s instances; where should its specification live? Shoulld it live with the class that uses it, or should it move towards the data on which it operates? Usually such methods exist in order to help simplify the caller’s interaction with something — I’ll posit that such methods are usually Adapters. So which is worse — have the adapter live with the adaptee or with the only client? Should it only move to (or towards) the adaptee when a second client needs it, perhaps?
One might argue that moving it to the adaptee creates a case of SpeculativeGeneralisation; in which case it should remain with the only client until it is duplicated by some other class. On the other hand, moving the fragment towards the adaptee can help to encapsulate the adaptee better, often allowing it to expose less to the outside world. All of which seems to imply that we can’t determine the best response to seeing Feature Envy without looking at other smells, current and future.
One approach would be to not treat FeatureEnvy / UtilityFunction as smells, but instead to look at the adaptee and call foul if we see a getter or setter; and in the absence of getters and setters, wait for duplication to show us what code needs to be shared, and hence moved. I think I may try living with that approach for a month and see what happens…
I just watched a great video of a “performance kata” by Corey Haines. Highly recommended!
Watching Corey solve the kata differently than I would have done has taught me several new tricks. Notably spec -fs spec — I get a completely unreadable mess when I run that on my own code, so there’s some work to do there in terms of how I name my specs.
Also, my first instinct would not be to extract the do_replacement helper method. I noticed GeePawHill do that too during his Double Dawg Dare refactoring videos; so it’s common, and a couple of years ago I was doing it all the time. But recently I’ve tried to work the opposite way — if the tests need a helper, I take that as a sign that the API under test isn’t as developer-friendly as it might be. So I look for ways to DRY up the tests by making the test subject’s interface more fluid. The helper method approach certainly gets results more quickly, but I worry about the longer term maintainability of the code; maybe I shouldn’t; maybe I think too much instead of just doing TDD “as if I meant it”.
All of which means that performance kata are a fantastic way to share knowledge, develop new skills and hone techniques. Time for some more practice…
I just had news that our session “Developing a Sense of Smell” has been accepted for XPDays Benelux this year. I’ll be running the session with Lindsay McEwan, and we plan to explore the use of code smells as a tool for communicating about software design. Join us if you can!
For nine months now Corey Haines has been travelling around the Eastern US as a journeyman programmer. During that time he has run numerous code retreats, published numerous fantastic interviews with leading developers, and made dozens of blog posts and videos in which he reflects on the things he’s learned from the people he’s paired with. The body of Corey’s travails is already beginning to form an invaluable reference, and all of the insights he has pushed into the public domain add to our collective knowledge about how to develop software carefully (in both senses of the word).
Just as amazing as the project’s knowledge elicitation is the fact that Corey is self-funded. He pair programs for food and lodging, and just by doing that he has given back an enormous amount to the community. Well, now he’s skint. The journey will continue only if Corey can raise some funds.
So please visit Corey’s blog and pledge a few quid to keep the journeyman on the road. I did, and it didn’t hurt a bit.
And next year, Corey, how about we raise the cash to get you over to the UK for a tour?