Real programmers don't use refactoring tools

10 August 2007

You know, it's funny. I can well remember the day I saw my first syntax-highlighted code in an IDE. It looked a little weird compared to the flat Notepad look of yester-editors, but, from that point on, non- syntax-highlighted code just became harder to read. It was as if we had been shown the z-axis from having lived in Flatland for so long.

And then came code completion lists. You'd be typing along and if you suddenly became stuck trying to remember a member name, you could wait half a second or so and a list would pop up showing the identifiers you could type in at that particular spot. And again, you were shown another dimension to your code, and there was a split from the past.

And now I see comments like this: "I have to laugh when I see what these tools do. Any coder worth his salt will be instinctively writing code as good as if not better than this tool produces." In case you hadn't guessed, the writer was talking about refactoring tools. In particular, the fake Yorkshireman macho-ness ("When I were a lad, we were lucky to 'ave _fastcall") that infuses the prose might have indicated to you that he was a C++ programmer, and you'd have been right.

This just seems nutty to me and it's not because I work for a company that puts out a refactoring tool. Yes, we all know what refactorings are, the great geek god Martin Fowler wrote a best-selling eponymous book on them. And we all know the names of the most popular ones: Extract Method, Inline Temp, Extract Interface, Move Method, Rename Identifier, and so on. Their names have become a mantra almost, a paean to the new agilism. But, surely -- he says in a baffled voice -- having an automated tool is better than doing them by hand? Aren't refactoring tools just another small step on the way to better visualization of our code and of writing it better and faster? What am I missing that this comment writer implicitly understands, or, rather, vice versa.

The writer of this comment also seems to imply that Code, Once Written, Is Never Changed. It must have been the eleventh commandment, but God ran out of ink when printing it on the stone tablet. But we all know from our own experience, without recourse to statistics, that most code is changed. No matter how good we are, how much we're worth our salt, no matter what language we use, we introduce bugs when we write code. It may be we change it immediately ("Duh, that should be a less than, not a greater than") or it may be later, after testing. And, sure, when we modify our code, or we modify someone else's (because we are lucky indeed if the only code we ever work on is our own), we make use of refactorings as we make fixes.

Think of it like this. Since code will get changed (there's a bug; the requirements change; you prefer to code in an organic fashion as if you were playing with Play-Doh), why not take advantage of a tool that helps you do it? Certainly you can do it all by hand and have members of the opposite sex swoon as they watch you manipulate the code with the keyboard, but to be honest who gives a damn.

I think of the way I write code. I'm an experimentalist developer: someone who develops with TDD (Test-Driven Development). Despite years of programming, I always approach a programming task as a blank sheet of paper. Since I do TDD my blank sheet of paper always starts off with something I'd like to be true (my first test). And then I write the code that satisfies the test, and then I write something else I'd like to be true (another test), and I write more code that makes it so. And so on so forth.

During this continual piecemeal advance towards software of value, my code is completely malleable. Just because I wrote it one way for one test, doesn't mean that it's going to stay that way for ever. I refactor ad nauseam and utterly rely on my refactoring tool to do that.

The refactorings I use most of all are these:

1. Rename Identifier. This one is the most important refactorings in my ever so humble opinion (it's my blog so what I say goes!). When I'm coding, I never seem to get the names of things right the first time. Indeed, I've given up trying to, since it just interrupts the flow of getting an algorithm down. And the names of things are perhaps the most important thing about our code. We can't do anything about the syntax of our language, but, by gum, we can about our identifiers. Name them badly and we obfuscate our code quickly. We've all heard the (apocryphal?) stories about programmers coming across some code where the previous programmer in an effort to be funny used names of flowers or mountains to name local variables.

Compilers don't give a fig about what you name your identifiers. It's your human audience that does, and you can guarantee you'll have one: the hapless maintenance programmer that follows in your footsteps, which, sometimes, is you. You are writing for your human reader first, and your compiler second. Beware if you think it's the other way round, because you'll appear on The Daily WTF. I've learned that the real name for an identifier only becomes obvious once you use it. Sometimes if you find it hard to name an identifier it indicates that your code is not expressing the problem domain properly. So, being able to rename an identifier, without worrying about search-and- replace destruction, is a boon beyond compare.

2. Extract Method. I'm an advocate of small, low- cyclomatic-complexity methods, and ones that have a single responsibility. Ditto for classes, by the way. But constructing a set of interacting methods as a complete thought exercise and getting them all down in source code is a talent that has always eluded me. Call me a stream-of-consciousness programmer by all means, but that generally means slamming it all down and then sorting out the mess afterwards. This is where Extract Method come into its own. Hey, that code over there is essentially the same as this over here, let's extract it into a common method that can be called (reused!) from both locations.

And an automated Extract Method is great: it works out the parameters, constructs the right method signature, inserts calling code at the location where the code was extracted, and in less time it takes to think about the possibility. Toss in a few Rename Identifier refactorings and your code becomes more readable and simpler to understand.

Mind you, me, I can't wait for a tool that will find duplicate code for me. Oooh, at that point the combination may topple Rename Identifier from the top of the list.

3. Create Method Stub. This isn't really a refactoring per se (it makes temporarily non-compilable code into compilable code), but it's so handy for TDD enthusiasts. You're writing a test that defines how you'd like something to read and to work and you find you've used a method name that doesn't exist yet. Hit the refactoring key, and boom, the tool adds a stub method to the right class. Invaluable.

4. Decompose Parameter. This one is rapidly moving up the list ever since I started playing with an early beta version of it from Refactor! Pro. The set up for understanding why this is so good is a little long-winded, but the payoff has all the more impact for it.

When we use a framework like the .NET Framework we find ourselves writing a lot of event handler code so that we can get called back from the framework when something happens. Oftentimes, we'll get a nice handy object passed in as part of the call containing property values we can use in our handler code. The unfortunate thing is these event handlers tend not to be reusable, because the caller has to construct a new handy object to pass in some values. All in all a pain. Especially so when, in general, we are given a particularly rich or heavy object and we only use a couple of its properties. Far better to extract out the specific code into another method, pass in the property values (and not the rich, heavy object), and reuse this new method, including calling it from our event handler.

Enter Decompose Parameter: it works out what's actually being used inside the method's implementation and constructs a method that has those properties as parameters. It then patches up the calling code and you're done. So, instead of a non-reusable event handler that needs an EventArgs but just uses the EventArgs' Graphics property, and hence that requires an EventArgs object to be set up before calling, you get a method that expects a Graphics object instead, and can be used from other painting methods, for example.

5. Move Type to File. One of Refactor! Pro's invaluable refactorings and every time we demo it, people in the audience "get it" immediately. The way we write code these days is to have one class per source file, the latter named after the former. Makes it easy to find the code for classes, and great for when you have multiple programmers working on the same team. But writing code in that way is a pain in the neck. You want a new class, but have to stop what you're doing in order to add a new file to the project, name it accordingly, switch to it, add the new class, switch back to where you were, etc. It's a complete drag to productivity. For the past couple of years, I just continue writing code to build a class model in the same one source file. When I'm ready to extract the classes, I just invoke this refactoring on one class after the other. Boom, boom, boom. Much easier.

6. Extract Interface. Another great refactoring. You've written a class believing it to be the only one you'll need, and, wham, you discover another exemplar that's slightly different. I much prefer interface inheritance to implementation inheritance, and so I'd be doing an Extract Interface so quick it would make your head spin. Mock me, baby.

7. Inline Temp. A quick simple refactoring, nevertheless very useful. It changes code that declares a temporary variable, sets it, and then uses the variable in an expression. This refactoring just gets rid of the temporary variable and moves the initialization into the expression. Sometimes this can make the code a little clearer. Sometimes it won't and you'll need the opposite refactoring; Introduce Temporary Variable. I do tend to use both as I polish code.

8. Use String.Format. Another refactoring that audiences at demos instinctively "get" and that I use all the time. Face it, writing a String.Format call is great for localization efforts, but it's just a real pain to write. It's much easier to write an expression that consists of a bunch of string concatenations, but unfortunately that's hopeless from a localization viewpoint. So, do the easy thing and let the refactoring tool do the tedious work. I love just writing a string concatenation expression, getting it exactly the way I want it, and then convert it to a String.Format call as if I'd done it that way all along.

I'll stop there. My intent here is not to list out all the refactorings I use, only those I use most of all. In doing so, I wanted to indicate why I think a refactoring tool is deserving of being in your coding environment.

Sure, the refactorings I've listed can all be done by hand. Indeed if I were stern and hard-core I'd say do them by hand a couple of times so that you get a real feeling for what each refactoring means and how it works. And I'd laugh my evil taking-over-the-world laugh while you did so. Afterwards you'd just use the automated tool and be thankful that you can save time.

Because in the end, we're not all great programmers who write code correctly in the first pass. We're all programmers writing code to enhance/fix/extend existing code (and even green field projects turn into a marathon of altering code). We're all maintenance programmers to a smaller or larger extent. We're all programmers who have to live with other people's code, and recognize that our code after a few months could be someone else's. And because we're all that and more we deserve the best coding tools we can get. And that includes automated refactoring tools.

Free DevExpress Products - Get Your Copy Today

The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the DevExpress Support Center at your convenience. We'll be happy to follow-up.
No Comments

Please login or register to post comments.