Refactoring: Improving the Design of Existing Code 184
kabz writes "Refactoring (as I'll refer to the book from here on in) is a heavy and beautifully produced 418 page hardback book. The author is a UK-based independent consultant who has worked on many large systems and has written several other books including UML-Distilled. Refactoring is a self-help book in the tradition of Code Complete by Steve McConnell. It defines its audience clearly as working programmers and provides a set of problems, a practical and easily followed set of remedies and a rationale for applying those techniques." Read below for the rest of Johnathan's review.
Code refactoring is the process of restructuring code in a controlled way to improve the structure and clarity of code, whilst maintaining the meaning of the code being restructured. Many maintenance problems stem from poorly written code that has become overly complex, where objects are overly familiar with each other, and where solutions implemented expeditiously contribute to the software being hard to understand and hard to add features to.Refactoring: Improving the Design of Existing Code | |
author | Martin Fowler with Kent Beck, John Brant, William Opdyke and Don Roberts. |
pages | 431 |
publisher | Addison-Wesley |
rating | 9/10 |
reviewer | Jonathan Watmough |
ISBN | 0201485672 |
summary | expands and formalizes the idea of applying explicit refactorings |
Typically refactorings are applied over a testable or local scope, with existing behavior being preserved. Refactoring as defined in this book is not about fixing bad designs, but instead should be applied at lower levels.
Testing a la Extreme Programming is emphasized as a control for ensuring that program meaning is not changed by refactoring. It is not over emphasized, and this is not a book about testing, but it is often mentioned and stays in the background through the book.
The refactorings presented in the book are not intended as a comprehensive solution for all problems, but they do offer a means to regain control of software that has been implemented poorly, or where maintenance has been shown to simply replace old bugs with newer ones.
The book is divided into two main sections, introductory material that introduces and discusses refactorings, and a lengthy taxonomy of refactorings that includes both examples and further discussion. The introductory material consists of a long worked example through simple Java code that implements printing a statement for a video store. Despite the simplicity of the code, Fowler shows in clear detail where improvements can be made, and how those improvements make the code both impressively easy to understand, and easy to maintain and add features.
Several key refactorings are demonstrated in the opening chapter including Extract Method, Move Method and Replace Conditional with Polymorphism. This is a book about programming in the object oriented paradigm, so as you might expect, the first two refactorings refer to extracting and moving object methods either into new methods, or between objects. The third example provides a means to replace special cased behavior in a single object type by deriving a sub type of the object and moving type specific code to the sub types. This is a fundamental technique in object oriented programming, and is discussed here in practical terms.
Now that several actual refactorings have been introduced, Fowler provides a solid and well thought-out discussion of the why's, when's and when not's of refactoring. For example, code can decay as features are added, and programmers special-case, or bodge additional functionality into existing objects. Fowler argues that the bitrot and decay makes software more unreliable, leads to bugs and can accelerate as the problem gets worse. Faced with these problems, refactoring should be used to improve local design and clean up and improve code, leading to better software, that is easier to maintain, easier to debug, and easier to improve with new features as requirements change.
However, there is a caveat, in that since software functionality should remain unchanged during refactoring, the process of refactoring consumes resources, but provides no easily measurable value. Fowler confronts this issue in a section that discusses how to communicate with managers, that you are performing refactoring work. He denies being subversive, but his conclusion is that refactoring should essentially be folded in with normal work as it improves the overall result.
This is a bit like goofing off on the basis that you'll think better after 20 minutes of fooseball. I'd definitely subscribe to that theory, but many others may not.
Kent Beck guests in Chapter Three for a review of the issues in typical software that suggest a refactoring may be needed. This chapter is entitled Bad Smells in Code, and most of the smells presented will be familiar to any reasonably experienced programmer, and they will be a great learning experience for less experienced programmers. I got the same feeling reading this chapter as I did when I first read Code Complete. Here was someone writing down names and describing problems that I had a vague unease about, but was too inexperienced to really articulate or do something about. Typically the refactorings address the same kind of issues that a code review with a skilled experienced programmer would address. Long parameter lists, too long methods, objects delving about in each others private variables, case statements, related code spread across different objects etc. None of these problems are debilitating in themselves, but added up, they lead to software that can be prone to error and difficult to maintain.
Most of the remaining substance of the book, 209 pages, is given over to a taxonomy of refactorings. These 72 refactorings are covered in detail with comprehensive simple examples presented in Java. Each refactorings is given a clear name, a number and a line or two of descriptive text. The motivation for the refactoring is then discussed, often including caveats and cautions. The mechanics of implementing the refactoring are then listed, with 1 or more (and often more) examples of implementing the refactoring. Refactorings range from the very simple to more complex examples such as Convert Procedural Design to Objects.
Due to the difficulties of reproducing large and complex sections of code, Fowler sticks with relatively simple examples. These seem to grate on him more than the reader, and he can come across as somewhat embarrassed when we look at the employee, programmer, manager pay example for the tenth time. I certainly didn't have a problem with it though.
This is a very well written and fun to read book. I personally feel that much of the material is implied by from Code Complete, but Fowler does a fantastic job of expanding and formalizing the idea of applying explicit refactorings. Much like Code Complete gave a motivation for keeping code well commented and laid out, this book presents the case for care and feeding of how to structure software. To fight bitrot and technical debt, poorly structured and unclear code should be targeted and refactored to improve structure and clarity. This gives a very real payback in terms of less required maintenance, and ease in adding features later on down the line.
Despite the fact that all the examples are in Java, the ideas are easily transferable to C++ or any procedural object oriented language. I highly recommend this book.
You can purchase Refactoring: Improving the Design of Existing Code from amazon.com. Slashdot welcomes readers' book reviews -- to see your own review here, read the book review guidelines, then visit the submission page.
Awful description (Score:5, Insightful)
This is a bit like goofing off on the basis that you'll think better after 20 minutes of fooseball. I'd definitely subscribe to that theory, but many others may not.
It's not at all like that. If you really need an analogy to understand this very simple concept, it's like seeing that your desk is overflowing with paperwork and spending some time filing everything properly. A little time invested can make it a lot quicker to find what you are looking for and help you deal with one thing at a time.
Another Excellent Book, The Pragmatic Programmer: (Score:4, Insightful)
This is yet another book that I wish I had read years ago. Working a few years in industry really makes you realize how much you can learn from other people. But alas, the problem of youth is that you always think you're the exception.
Re:who doesn't know about Refactoring? (Score:3, Insightful)
This review comes as a pleasant reminder that you don't have to chuck your old code away and rewrite it all in the latest, coolest tech-fashion. Keep the old stuff working, refactor it so it can still be maintained, enjoy producing applications that your customer wants and that work well for your customer.
for example, I had a meeting with some of my american colleagues early this week where they wanted to "throw away all our old SQL access code and rewrite it all using LINQ". Please, lets not.
Refactoring sucks (Score:4, Insightful)
And if anyone whines about how old code needs to be rewritten, point them at this
http://www.joelonsoftware.com/articles/fog0000000027.html [joelonsoftware.com]
Not quite the same (Score:4, Insightful)
You'll find that being a star in _any_ discipline, Jazz included, isn't just a matter of being given a crash course in music notation. There are many who can learn Jazz, but there are few which are stars at it.
And there are a ton of people who just aren't any good at Jazz, no matter how much you teach them. There are those who are born tone deaf, or lack the coordination, or those who just aren't interested in a musical career, or those who find that learning uber-boring, or a dozen other cases which you just can't turn into _stars_ in any kind of music.
I'll further go and say that in Jazz (or any other kind of music) it's a relatively low pay career and with very few oportunity to become a super-star. So I'd wager that any music school gets an already filtered set of candidates. It's already those who love that genre, are sure that that's what they want to do, etc. It's already the people passionate and motivated.
In programming, we get a ton of drooling burger-flippers who think they can just get a quick training in Java and earn the big bucks. Not because they like it, not because it fits their personality type, not because they showed any kind of aptitude or inclination, just because they think they can fake their way to the big bucks. And it shows.
If you want a more apt comparison, compare them to the gang wanting to be a rock star in high school and college. The prospect of glamour and big bucks is there, and every other high-school boy wants to be in a rock band. The problem is that most suck, and will _never_ be even an acceptable player with any instrument. And aren't particularly motivated to train hard either. How many actually become a _star_? I'll say that's lost in the decimals.
So basically to sum up the comparison to Jazz:
1. Yes, you can teach Jazz. You can teach CS too. That's why we have that kind of colleges, you know.
2. You can't just take any guy off the street and turn him into a _star_ musician. If you went and took random people laid off by McDonald and tried to turn them into musicians, very few would even make an acceptable musician, and _extremely_ few will ever be considerable a _star_. Same as programming.
But, of course, that won't stop idiot PHBs from trying.
Re:Code refactoring is the process of... (Score:3, Insightful)
It decreased the size of the program code, made the code more modular, faster, made possible to write good unit tests for it and decrease bug count per LOC, all in all rewarding the developer and user with a more stable, smaller, faster and more maintainable codebase.
Re:Code refactoring is the process of... (Score:2, Insightful)
(1) Your requirements have changed.
(2) You are dealing with a piece of buggy code that is a nightmare to debug and test.
Re:Refactoring sucks (Score:5, Insightful)
But that's not what refactoring really is. Refactoring is not about making code "purdy", it's about making it so it's easier to add those layers of bugfixes that all successful production systems have. What you're referring to is a bunch of lazy, vain chuckleheads who don't want to read code. Refactoring is noticing that an enhancement or bugfix that requires you to make a similar change in umpteen places in the code and working to make it so the next enhancement/fix to it doesn't take so much time in the future. Refactoring is noticing that venerable function with all the layers of complexity isn't even used anymore by anything (and KNOWING that, not just assuming it) and giving it a dignified retirement. It's not going through software like the Code Gestapo and looking for everything that doesn't conform to "The Right Thing As I See It."
Re:Refactoring sucks (Score:5, Insightful)
Good programmers can deal with ugly code, but if your code base takes two months to make a developer marginally productive and breaks in unpredictable ways from simple code changes, then you probably need to refactor. If you're spending tons of man hours treading water because the code is such an unbelievable mess that its nearly impossible to make a solid change without breaking something, then you're going to get better productivity if you bite the bullet and come up with a plan for refactoring.
Also, I always have to laugh at the whole "well a great developer can work with messy code..." Unless you're working in a small, tight team, its completely impossible to make sure that you're working with a good developer, much less a great one. A lot of developers out there are poor or average, and companies, despite their best efforts to avoid doing so, do hire these people.
Re:Refactoring sucks (Score:5, Insightful)
Obviously refactoring needs to be carefully considered on a case by case basis. How ugly will hacking in the new feature make the code? How likely is it that other new features will also be added to this code down the road? How many other programmers have to deal with this portion of the code? How much time do you have to make the change? How much work is it to refactor the code and do it the right way, versus just hack in the feature? These and many other questions need to be answered before one determines whether refactoring is a worthwhile effort.
As somebody who works primarily on server-software that is in a continuous state of changing and adding features, where stability is critical and expected lifetime is measured in decades, I personally have found that refactoring is almost always worthwhile in the end. I think the biggest problem with refactoring is not whether there is a need, but whether there is competence to get it done. Often times programmers will refactor something to work differently to their liking, but in reality will not have actually improved the flexibility of maintainability of the code at all.
Re:Love refactoring but primary problem is legal (Score:4, Insightful)
Refactoring code is like paying off debt. When you add new code without thinking about the design, the internal quality goes down. This makes the code harder (read "more expensive", "slower", etc) to work with in the future. And since most of the cost of software is in maintenance rather than the initial development, that's a bigger issue than you think.
Carrying a little bit of technical debt is inevitable, and a good thing. But if you allow too much to build up (by not refactoring debt-heavy areas of code), the interest payments can become crippling. Your code sucks, you can't understand it, and making changes takes far more work than they should. That directly impacts the bottom line.
Re:Change, we love it! (Score:4, Insightful)
The other forms of documentation are meaningful names. If you want to document a sub-expression, which does not have a name, give it a name, by assigning it into a variable before using it. If a piece of code does not have a name, then give it a name by putting it in a separate function.
Variable and function names are much less likely to go out of sync with the meaning of the code than comments are, are more concise and less redundant (so they avoid violating the DRY principle).
Remember, bad/wrong documentation is worse than no documentation.
Re:A problem with 'refactoring' (Score:4, Insightful)
Amen. I'm getting to the point these days where I'm slowly becoming wary of anything that appears, to me, to be a good idea. In the mid-90's or so, I came across this new paradigm called "Object Oriented Programming". At the time, I was maintaining monolithic Cobol systems, and the appeal of OO was intuitive. I saw right away how it could be used to simplify code reuse, or even make reusable code that had not previously been reusable.
These days, OO means every function is encapsulated in a class, and every class has an ISomething interface, a SomethingImpl implementation (which has no data members) and an AbstractSomething base class (that's not extended by anything else), and a SomethingFactory that creates instances of SomethingImpl's and hands back a pointer to an ISomething... and if you ever float the concept of implementing ISomething again (you know, taking advantage of all that framework?), you'll be scolded for violating the purity of the OO design.
XP was another thing that, when I first saw it, looked like a great idea. It seemed to me that XP was essentially a description of what productive programmers were already doing while they were pretending that Gantt charts were meaningful and that project managers were useful.
Now XP is called "Agile" and companies hire "Agile Mentors" to show us how to do precisely the meaningless project management exercises that XP was meant to replace... When I see how much paperwork I have to fill out to be "agile", I long for the days of Gantt charts and Microsoft project.
Refactoring, too. When I first saw Fowler's book, it was like a lightning bolt. The intro talks about how he spent a few days at a client site "cleaning up" a bit of code to simplify his next task. I had had that same experience too many times to count - I had been tasked with, say, implementing lot tracking in an inventory system. It was obvious (to me) that if I first made some modifications to the existing system, I could insert the desired feature without disturbing everything else - but I could never seem to explain that to a nonprogramming manager (in the end, I always ended up doing it and pretending I hadn't because I didn't want to be responsible for breaking the entire thing). And here was a book dedicated to the technique!
Yet, nowadays, refactoring seems to mean going through working code and wrapping every function in a SomethingImpl class, giving it an ISomething interface, letting it extend an AbstractSomething base, and creating a SomethingFactory to create SomethingImpls...
Re:Awful description (Score:5, Insightful)
This is a bit like goofing off on the basis that you'll think better after 20 minutes of fooseball. I'd definitely subscribe to that theory, but many others may not.
The analogy I usually draw is to cooking.
Everybody's thrown a big dinner party and then left the dishes for a few days. Or even worse, had a roommate who did. Turns out this sucks, because if you just want to make yourself a little breakfast, then it's harder to cook. You have to scrape out a pan and chip off a plate just to get started. And cooking is twice as much work because you have to shift the mess around just to get at the counter, and then shift it again to get at the stove.
Then when you're done, you sure aren't going to wash up properly; the sink's filled with dishes already. So you just toss your dishes on the top of the pile, saying you'll get to it "later". Of course, that ever-growing mound of fuzzy dishes is the real-world analogy of half the code bases I've seen. And the giant rewrites that inevitably follow are like tearing down your kitchen and building a new one because that's the cheapest way out of the mess.
Instead, good chefs work clean. They clean as they go. Always. Not because they're uptight freaks, but because if they don't, the mess slows them down and makes them sloppier, eventually resulting in a giant clusterfuck, with all their colleagues yelling at them.
Refactoring is just a way for programmers to clean as they go. I picked up the habit years ago, and now would never go back, as it's the fastest way I've found to get good work done.
Re:MakeWork (Score:2, Insightful)
Another scenario (this time it's personal experience): A co-worker and I were asked if we could design an in house resource management system. Management wanted to keep this very low budget, meaning we had to use the tools available (VERY LIMITED) and nothing which would take more resources than a couple of co-op students knocking the idea about in their spare time (i.e. no resources at all). Although both of us have coding experience, neither of us had used the language available to us, nor do we do any sort of programming in our daily jobs.
We started discussed a couple of different approaches and we started knocking out bits of code that were really only meant for testing ideas about the language and putting together a rough sketch of the program. Unfortunately, one of the bosses saw a nearly-almost-halfway functional version out of this, showed it to his boss, who got excited, and then handed the code off to another employee to finish (actual resource allocation now that upper management approved). He unfortunately was a sloppy programmer and did a mediocre job at finishing the project. The system that resulted from that is still in use today, although over the last few months has started to show aberrant behaviour, reacting slowly, and any time a new feature is added it gets worse. "Tutorial" code we originally would have scrapped became a "functional" system, and it was made worse by a sloppy programmer who didn't understand the need for commenting (He actually deleted most of my comments out, because they "distracted" him).
In either of these situations, does it not sound like refactoring may do this code some good?
You forgot... (Score:3, Insightful)
The only way you can make sure while refactoring an application that at the end of the process it will still behave the same way as it did before the refactoring is to write unit tests. Never (heavily) refactor untested code.