Wednesday, November 26, 2008

reek for Ruby rocks

I've been interested in developer tools for Ruby for a while now, and was pretty excited to find reek by Kevin Rutherford. After playing with reek a bit on a variety of code, I decided to ask Kevin a little more about it. Here's what we talked about.

What prompted you to write reek?

Kevin Lots of little nudges all occurred simultaneously and added up to one big push!

In January 2008 I had just finished a coding project for a client, so my hands were itching. I think I started on reek as soon as I discovered the ParseTree gem by Ryan Davis of seattle.rb. It struck me that Java has a rich set of code quality tools but — at the beginning of 2008 — Ruby had none.

I wrote the first draft of reek in early 2008, but only went public with it when I was confident that the book would complete too. I was also hunting around for medium-sized codebases to use for examples in the book, so starting on a tool development project seemed like it might provide some fodder in that direction. (It didn't, as things turned out.)

What response have you seen from the Ruby community?

Kevin Generally very positive (over 500 downloads of the gem at time of writing). I've had a few emails from people who were perplexed by the Feature Envy warnings, and that's probably because reek isn't (yet) doing enough of the right kinds of static analysis. I hope the tool's early excesses haven't soured its audience too much, because it is getting smarter (and hence more reasonable) all the time!

I noticed you've tried out flay. What other refactoring tools have you seen/used for Ruby?

Kevin I think I've tried everything in this space as of now — flay, flog, roodi, towelie (the set is growing so quickly that there may be even more by now). I like flay a lot, and I'm sure it will also continue to get better and better. For some reason, complexity metrics and suchlike leave me cold; so although I respect flog etc, I don't use them. (I think it's very healthy that Ruby now has a growing corpus of code quality tools, and it's a great tribute to the Seattle.rb folks that such tools have suddenly begun to appear now, since the easy availability of parse trees.)

I also use the Ruby refactoring support in NetBeans for my day-to-day Ruby development. It does some simple things well right now &mdash although I wish the scope of a "rename" were tied down a lot better! And it goes without saying (although we've devoted a whole chapter in the new book to this) that refactoring is only safe in the presence of a version control tool (I use git) and various different kinds of automated tests (I mostly use rspec).

In fact, that's another reason why there's a need for Ruby refactoring books. One of the practical tips in Fowler's original Refactoring book is to use the Java compiler: When you make a change to a method signature, for example, the compiler will provide you with a list of everything that now no longer compiles; you can use the compile errors as a to-do list as you work through the refactoring steps. But Ruby can't do that; you're completely reliant on test coverage to tell you what's broken. If something in Ruby isn't tested, you are in much more dangerous territory than you would be in Java. Which means that tools such as heckle and rcov are much more important in Ruby, and the discipline of test-driven development (and test-driven bug-fixing) is even more necessary.

In discussing reek, you wrote about looking for a code base to mine for examples. Did you end up finding anything interesting?

Kevin I run reek on all of the gems I use, so that I have a broad view of whether it is reporting reasonable things about the code. So the open source community is a rich source of test material, and as reek evolves and deepens that will continue to be the case.

But the Ruby Refactoring Workbook is intended to be a self-study guide, and so the exercises work best when they are short and focussed. Real code is usually too large and complex (and messy, it has to be said) to be helpful in that context. The book has a few chapters in which the reader gets to work on more "complete" applications, and I was hoping reek's own code might be useful as one of those; but in the end, we again fabricated them from scratch in order to focus on specific themes.

What's your typical workflow in applying code quality tools to a Ruby codebase?

Kevin I follow the same general patterns I use in Java, C# and C++ projects, although it's only in the last few months that Ruby has begun to offer some of the tools...

I use TDD from the outset, and these days that means rspec. So my code grows alongside a set of examples, and rcov always shows coverage in the high 90s. Once I have the "walking skeleton" working (usually within a couple of days), I will briefly experiment with quality tools to find out what settings work best for this particular codebase. Then I'll add tests so that any future code will fail the "quality gate" if it isn't up to scratch. So for example reek's test suite includes a run of reek over it's own source -- any warnings reported are treated as fatal errors and the tests fail. I'll be adding flay to that suite soon too.

When a defect or feature request comes in, my first response is to write a new test (or set of tests) to describe the correct desired behaviour; I'll set these to "pending" until I have time to implement a fix.

Early in reek's development I also took a snapshot of a few source files from gems that stretched reek into new places. I have a "golden" report stored for each of these, and reek's test suite includes a regression run to make sure any new version of reek still finds their smells. (There are also unit tests for each specific thing those gems taught me.)

As an agile coach, I have a fundamental rule that I apply to all software projects, regardless of tools or technology: If something bad bites the project more than a couple of times, I look to prevent it occurring again, either by adding tests to help the project identify the problem situation sooner, or by tooling to make it easier to do the "right thing". Tests for quality, and regression tests for bugs, both come into that category; so does using rake, hoe and newgem.

As I mentioned before, I don't tend to use complexity metrics. I tend to think that any code that's free from smells will have naturally low complexity; so I prefer to focus on refactoring.

What value would you see in using reek, flay, and friends on widely used Ruby libraries, or on emerging code like Rubinius?

Kevin Well-factored code offers a couple of general benefits. First, it is easier to read and understand. Which in turn means that more people might feel they can contribute. And that, in turn, can be made safer if each project uses these tools to establish quality standards via regression tests suites. (As a community we could promote this by evolving an easy way to set these up for new projects; perhaps newgem and rails could install standard spec files that run reek and flay over the project's source -- there's code in reek's repository that could be used as a starter here!)

And second, well-factored code is naturally comprised of smaller, more cohesive and less coupled pieces. These are likely to be easier to reuse, which in turn leads to increases in both speed and quality of development. So the whole community benefits more from well-factored code.

No comments: