Continuing our journey through Fowler’s refactoring book for this week, we covered a lot of ground which mostly concentrates on the role of testing (Chapter 5) and selected refactoring that seemed most relevant. They did a nice job attacking some of the common problem areas I have (including complex conditionals, too-complicated functions and data structures).
I’ve been practicing trying to implement some of the practices of testing in a current project. Figuring out the actual words for writing tests was new, but the hardest part. The hard part is figuring out what about your code you want to test. This chapter in the book had some helpful advice, talking about how you want to think about what is likely to break, how the code handles errors. For example, if you expect a data file, what happens if the computer can’t find the data file? What if the data file is empty. Or as Fowler puts it: “Notice how I’m playing the part of an enemy to code. I’m actively thinking about how I can break it.”
On a similar note, the book talked about a distinction I’ve been considering — that automated tests you write to go with your code are distinct from the tests you run by looking at what your code produces from a user perspective. You can’t leave either one of them out. One suggestion Fowler made that I plan to take seriously, though, is to let one type of testing inform another. That is, when you or someone else finds a bug by clicking around your website, before you fix it, write an automated test that captures that issue. Then, you’ll have a better sense of when it’s resolved. And even better, as you change other aspects of the code, you will be aware if that same issue breaks again (otherwise know as a regression).
Chapter 6 introduces the list of refactorings, of which there are many, and Chapter 13 sums up the book and explains that the real work is using these concepts in practice, and knowing how to stop when you need to, and take one’s time being thoughtful and careful about the code we write. Both helpful, but not the meatiest substance, in terms of things to write about.
Now, we get to the refactoring themselves — very much the heart of this book. We only read 11 of them, but I think we tapped into some of the ones I believe I will find most useful. When reading through them, a lot of them seemed like common sense. But I know part of that is because it all sounds good when doing a focused reading, but having steps to follow, and words for different refactorings in the heat of the moment and deadline stress of coding, will be key in being successful at this new level of programming I’m trying to reach.
A few of them had to do with taking bits of code, often methods, out of larger functions. The book seemed to focus on how this could help clarity, which I agree with. In the description of “Extract Method”, for example, I would have also pointed out this helps eliminate duplicate code (and supports a principle called “DRY”, or “Don’t Repeat Yourself”) as well, and I didn’t see that mentioned. Other refactorings addressed principles of better naming, including going back and changing to a more precise name, or combining these concepts and moving code into a separate area if you were unable to give it a precise name. I’m seeing more and more that if you work carefully, what you call things can make your code more readable, and even ultimately, serve as a way of commenting your code without actually using comments.
In conjunction with the naming ideas, creating temporary variables within a function can clarify what a coding expression is doing. In work I’ve been doing on parsing a webpage with some complicated text functions, I think better naming of temp variables, to serve as a reminder of what I’m trying to do is a really good idea.
Other refactorings focused on clarifying complex conditional logic, which has been a major struggle for me in some recent bigger projects. Again, it’s related to better naming and organization of code. Ruby has a more flexible syntax for writing conditions than I’m used to that can make things more readable, which I definitely want to try out. Breaking out methods for what happens in each of the conditional branches can make things easier to follow. And exiting from a method earlier by returning out of a conditional can not only not execute for no reason, but signal if you expect something to be a rare case or a more prominent one (that’s called a guard clause, I have now learned).
That’s just a quick overview, but in all the refactorings, Fowler helpfully outlines an organized process for getting from the old code to the new code. I really can use that as opposed to my “Oh no, fix it now!” mentality. What’s especially nice about the steps he lays out is you often write the new bit of code, redirect old code to the new code, and only delete what you want to get rid of as the very last step. It’s like you’re protecting yourself with a safety net in case you mess up. And, smartly, if you don’t control all the code using your code, it’s best to leave the old bit around, but warn against using it (deprecation) so that you don’t break what other people are doing.
Lots and lots of good advice in this bit. Very exciting stuff, which I’m enjoying applying to my work. This study of the refactoring book has been a great way to start answering a question I’ve long had: What should an intermediate coder be studying? This is a new level, and an awesome way to start.