This post originated from an RSS feed registered with .NET Buzz
by Sam Gentile.
Original Post: On Refactoring
Feed Title: Sam Gentile's Blog
Feed URL: http://samgentile.com/blog/Rss.aspx
Feed Description: .NET and Software Development from an experienced perspective - .NET/CLR, Rotor, Interop, MC+/C++, COM+, ES, Mac OS X, Extreme Programming and More!
Two posts caught my eye today, one by Paul called Refactoring is Not Free, and one by Jonathan with the same title. They both largely obscure the issue and come out with blanket statements that Refactoring is Not Free, that don't make parse and don't make sense. The central issue is that both of these posts, and especially Fran's comments, and a lot of people who think Refactoring is unnecessarily expensive, are looking mostly at refactoring of entire systems/sub-systems rather that small continual refactoring of code as it is written. The example Paul cites, for instance, “refactor a large portion of a badly performing application. The application had no supporting unit tests, cruddy code and Swiss cheese specifications (its developers only had a sense of what the application had to do). It is in this example where refactoring became costly,“ This is a messy, crappy system that has no unit tests. Of course, its expensive in this case because any redesign after the fact is expensive but so is maintaining any system that is in this state anyway!!! It's a business decision what to do with a system likethat. Business is going to pay one way or another. They can pay for development to stop and write unit tests and refactor or they can continue to pay over time with a maintenance nightmare or they can scrap a system all together and start over. None of this has a thing to do with Refactoring as I understand it. This is all about business decisions on what to do with unmaintanable systems. Of course Refactroing without unit tests after the fact is costly. Jonathan's post is also about “large“ re-factorings. This is NOT what is meant by Refactoring and is a Red Herring argument.
Let's go back to the definition of Refactoring shall we?
“Refactoring is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior. Its heart is a series of small behavior preserving transformations. Each transformation (called a 'refactoring') does little, but a sequence of transformations can produce a significant restructuring. Since each refactoring is small, it's less likely to go wrong. The system is also kept fully working after each small refactoring, reducing the chances that a system can get seriously broken during the restructuring.“
See the difference? Refactoring is small preserving transformations, not system redesigns (although Refactoring can be applied to large system changes). Refactoring done this way for me is not only free, but it pays dividends for my customer, because my system gets easier to implement as I add refactored code. I produce business value at every point. Refactoring is part of Red-Green-Refactor that Jim best describes . I, and thousands of other developers use this TDD cycle hundreds of times a day and doesn't cost me or my company anything. I think -write a test creating a design- implement the code, retest and then always refactor. Since my level of changes averages about 3 lines of code in this cycle, refactoring is free. I, or in a pair, refactor a small body of code in less than a minute on average and less than 5 minutes worst case. The important thing is that my code is kept clean, gets an instant code review and is made maintanable and agile.
A system that doesn't do this is full of Code Smells and Code Rots. Both of these lead to the kind of messy, unimaginable systems that Paul writes about and that we all have experienced. We have all done these kinds of things in past systems but we won't anymore. The more you don't refactor and put “TODOs“ (yah right, you really come back to them?) in your code, commented out code, code that longer works, etc, the more your code will rot and you and your development team will lose control of it, and then “Refactoring“ OR maintaining a large system will indeed cost. We don't allow TO-DO's, commented-out code, dead code or code that no one can understand in our systems. Someone or some other pair on the team will take it out. The other issue is that since our code is well Refactored, when a change comes (and it always does) we can make that change anywhere in the code, anywhere in our cycle for virtually the same cost because:
The code is clean and adaptable to change quickly
The unit tests are a safety net
The changes are small so with the unit test are very likely to work
Which leads me to Fran's comment which states, “1) think for 10 minutes and write code based on the conclusions made in those 10 minutes. After an hour you realize you started wrong because you didn't think of X and you have to refactor the code you wrote 2) think for 20 minutes and write code based on the conclusions made in those 20 minutes. After an hour you're done. I definitely will opt for 2). Every second you have to spend on refactoring is a signal you made a mistake earlier on, because if you would have made it better in the first place, the refactoring wouldn't be necessary. “
Now, I know Frans is a good, perhaps great coder but I don't buy his number 2. Its essentially an argument for BDUF. BDUF can can certainly work with a large effort and lots of hours but I still challenge #2. The 20 minutes is ridiculous in that if I wait ten more minutes I will think of X. Well, you can wait an hour or a week and still may never think of X or you might.Its also saying that if you don't get it right the first time its a mistake the developer made. Horsepucky. No one gets it all right the first time. BDUF makes a huge gamble that all the business and development people can think of all the X's up front in the “requirements“ stage. In my 22 years of software development, I have very rarely seen that this is the case, Why? Many the aspects of design of software and the problem are completely unknown until one begins to write the test or the code. Code speaks to you. It tells you which way to go. Often we need to learn what its saying, upgrade our original understandings. Thats why Red-Green-Refactor works so well. You think a bit, write a test, write some code, and then refactor it based on the new learnings that you have made while doing it. The gamble here is that we start design right away at a whiteboard and then tests applying YAGNI and Simple Design not having everything up front. But my experience is that you can't possibly know everything up front. Business, as I am finding on our current Agile effort, doesn't even begin to know enough up front until they start writing their FIT Tests!! It wasn't until they started “writing tests“ or FIT examples that they found out how much they didn't know. The FIT examples helped open a large needed discussion between Business and Development. You know what? We had to Refactor dozens of times during the week because as Business learned all this and caused changes, we were able to change instantly, because all our code was all Refactored and every single class had unit tests. It cost virtually nothing.