This will be my last article on Refactoring and Refactoring to Patterns. In this article, I will reflect on what I have learned, the exercises that I performed throughout the term, the books that I read and where refactoring, patterns and design fit into the software engineer's toolkit.
What I have learned
I came into this independent study with a vague understanding what refactoring was. Before the independent study, I was haphazardly and clumsily refactoring my code already: I love the idea of clean, elegant code. This term I learned about how to turn that ad-hoc, unprofessional method of refactoring into a well measured, thought out series of steps and guidelines that will set me on the path to good code.
First, in Martin Fowler's Refactoring, I learned about code smells and how to identify them. While I had heard about code smells, I was not ever able to identify them. Fowler gives a detailed set of instructions on how to identify a set of code smells that are relevant to refactoring. I was able to identify when I was to refactor a section of code rather than doing so in almost a completely random manner.
Fowler then (among other things) gave me a catalog of refactorings that would resolve the issues of these code smells. The catalog brought concrete practices to the abstract idea of refactoring. Putting these specific refactorings to practice and being able to write in my commit message "Extract Method Refactoring" was very satisfying. This, similar to patterns, allowed me to follow best practice examples as well as communicate what I was doing in an ubiquitous language.
Extending on Fowler's book, Jay Fields' Refactoring: Ruby Edition provided insight into how to do refactorings in a dynamic language such as Ruby. He also included several nice rubyisms that increased readability of the code in that specific domain (the Ruby language).
Finally, Josh Kerievsky's book extended the idea of refactoring further into using refactoring as a tool to reach patterns. This was a huge connecting piece in my understanding of where refactoring fits in the process of writing code (I'll get to that later). Even though he was introducing me to an abstract idea, his catalog of examples provided some concreteness to the otherwise abstract idea.
Exercises I performed
In the first 6 weeks of this term, I was reading either Refactoring or Refactoring: Ruby Edition. For these books, I was able to pick up a random open source project and refactoring that project. The projects that I refactored include Android, Thingamablog, Redmine and OJMoq. This was very good practice for myself and I learned much from these exercises.
In the last 4 weeks, however, I was unable to think of a way to practice the larger refactorings seen in Refactoring to Patterns. These refactorings are much more opportunistic and you must be familiar with the code before being able to perform those large refactors. So, I posted less and created a thought experiment on JUnit.
If I were to suggest exercises to someone, I would definitely suggest practicing the basic, small refactorings that are presented in Refactoring. Any code, whether you wrote it or not, is a good candidate to apply these principles. For the large projects, I would say that we should all revel in potential opportunities. If you see an opportunity in your code base to perform one of these large refactorings, create a branch and go for it. If, in the end, it isn't more elegant then you can scrap it.
Books I read
- Refactoring: Improving the Design of Existing Code by Martin Fowler
- Refactoring: Ruby Edition by Jay Fields
- Refactoring to Patterns by Joshua Kerievsky
I would recommend each of these books to people. Obviously, they should start with the canonical resource, Fowler's Refactoring. And, as you would expect, Jay Fields' book is best made for people working with Ruby code. Kerievsky's book is a great one for developers looking at the big picture and connecting the dots.
Where Refactoring, Patterns and Design Fit
Last summer I took a class sponsored by my employer where we studied and discussed design patterns. It was very informative and engaging and I came out in love with patterns. However, it quickly came into conflict with test driven development, continuous design and other extreme programming practices that I've come to embrace.
One of the practices of extreme programming is refactoring. I've studied refactoring extensively this term and I think that it is an essential part of development. There is the Red-Green-Refactoring methodology of TDD and applying refactoring in that manner is very useful. However, if we apply the philosophy of TDD and "Do whatever works to pass the test" style of development, is there any room for patterns and architectural design?
At first, I would have said, "No." Though I was uncomfortable with that answer, there didn't seem to be a way to fit large design and patterns into the TDD method. After studying refactoring, however, I've learned that architectural design and patterns are very much a part of the TDD method and are embedded in the Refactor step. Instead of focusing on the immediate area during the refactor step, we should be looking at the entire application. Was it painful to add this feature? Refactor. Can the interface for the object be improved? Refactor. Does this object have feature envy? Refactor.
This big leap came from Refactoring to Patterns (and a little from Refactoring). Being able to refactor to patterns is the way that developers can create flexible, maintainable, bulletproof architectures. Instead of doing BUFD (Big Up Front Design), the developers can dive right in and start satisfying features on Day 1. This, however, does not give license to developers to never think about design. Design needs to occur continuously. Good developers will know how to create good design. They will be both opportunistic and rigorous in their design practices; most of design, however, will happen during the refactoring stage.
If we apply this idea that design occurs during the Refactor step, then I would postulate that the Refactor step is by far the most important step in TDD. More time and focus should be applied in this step than the other two combined. Refactoring is the way that you're going to make sure that your system is flexible and maintainable for the future. Refactoring is the step that you make sure that other developers can read your code. Refactoring is the step that reduces pain and increases velocity. Refactoring is the step where you introduce ubiquitous pattern languages into your system.
So, where does refactoring fit into the developer's tool belt? For extreme programming practitioners, it may be the most important part of development! Of course, you'll need to be familiar with conventions of the language, how to most effectively expressed what you are doing, design pattern languages, the code you are working with, etc. Refactoring may be the most difficult and interesting part of software development.