While at UberConf I had the great opportunity to attend a talk by Venkat Subramaniam titled “How to Approach Refactoring”. The following is a quick summary of his talk and what I took from it.
- Reduce code
- Avoid clever code – keep it simple!
- Make it small and cohesive – single responsibility
- Eliminate duplication – DRY
- Eliminate dependencies – rather than striving to reduce dependencies, strive to remove them
- Write self documenting code – make comments unnecessary
- Code should be understandable in seconds – it is not just about reducing the amount of code, but also about clearly expressing meaning.
- Avoid primitive obsession – focus on creating higher level abstractions
- Checkin frequently, take small steps – every commit should be only one change.
- Shorter feedback cycle
- Other developers are kept in the loop
- Avoid large painful merges
- Keep code at one level of abstraction – each method should do one thing, and delegate to other methods that each do one thing.
When not to Refactor
Never refactor while making any other code change. Take a note of the refactoring and complete it after the bug fix or functionality change has been committed. Never commit a refactoring at the same time as any other code change. If you do break a build, having small, single change commits, will make spotting the problem that much easier.
- When you spot a code smell – take a note of it
- Finish your change
- Never refactor alone. You should always have a second set of eyes on the problem. Pair programming is essential while refactoring. The second person will be their to help ensure we are making the code easier to understand, using good names, and helping to ensure we are not breaking any existing logic.
When to Refactor
- You can refactor before or after a bug fix or a functionality change
- If you think the change will improve the design of the code
- If you think the change will improve the readability of the code for other developers
While at UberConf I had the great opportunity to attend a talk by Venkat Subramaniam titled “How to Approach Refactoring”. Many times I have attended talks and read articles focusing on refactoring techniques, but rarely do these other sources discuss the behaviours that allow refactoring to work. Knowing all of the refactoring techniques without understanding the fundamentals that make refactoring beneficial, can result in making code worse!
There are many great refactoring books out there such as
However, during Venkat’s talk he recommended a book that is not about programming, but instead about non-fiction writing in general: On Writing Well by William Zinsser. Whether you are writing a newspaper article, a book, or writing code, many of the same principles for better writing still apply.
- Simplicity – Writing is a about communication. Often as developers we make code unnecessarily complex. We do this because for some developers it feels good and impresses others. A developer should never be proud of complexity in code, but instead focus on simplicity.
- Clarity – Make it clear. Don’t use cryptic naming, instead make it clear and easy to understand.
- Brevity – Make it short, but be careful not to take it too far. Reading a very long piece of text can make it take time to understand a piece of code, but it is also easy to make it too short. Use only as many words as it takes to make the code understandable and no more.
- Humanity – Write it for people. Remember that code is going to be read by other developers, so consider how others will interpret what you have written and keep that in mind when writing code to make it simple, clear, and concise.
While attending Joshua Kerievsky’s talk at SD WEST he went into a brief overview of a number of refactoring patterns. If you want to read about these patterns in detail (and you should!), you should get a copy of the book Refactoring to Patterns. However, to help peak your interest on the subject, here is a quick overview of a few of the patterns discussed at the talk.
The Three Basic Strategies used in Refactoring
- Extract method
- Extract class
- Move Method
Piecemeal Refactoring Pattern
- Divid and Conquer
- Split problem in half
- Work at solving smaller problems
Narrowed Change Pattern
- Extract methods to determine where the change needs to be
- So refactor 3 methods, not 50
- Change only that area, make the change isolated to only a few places
- Narrow the change down to a smaller number of change points
Gradual Cutover Refactoring
- Move from A to B gradually
- Start with 1,2,3 to 30
- So the idea is you can ship to production with 10 of 30 pieces of code switched over while in a releasable state the entire time
- This type of refactoring can be done along side other work without affecting the release schedule for other development work
- Write the refactored code along side of the existing code
- THEN decide later on how and when to finally cut over to the new code
- This is a very conservative safe approach to refactoring.
- It also gives management the ability to make the decision whether or not to cut over to the new code in a release
- So if it is not tested properly by the time of the release, just don’t switch over, no need to roleback changes or take any other steps
While at SD WEST I was lucky enough to attend a talk by Joshua Kerievsky on refactoring. The following is just a summary of my notes from the talk.
The 5 stages of a software company (They all make the same mistakes)
- V1.0 – Developers are happy. The team can develop software quickly. No legacy code to read or fix, no maintenance or scalability issues to deal with (yet). All work is on new features so managers see the value in all the work and are happy.
- V2.0 – Still pumping out new features just as fast as in V1.0. Starting to notice some technical debt. However, at this point it is small enough to not slow development down or be that big of a concern.
- V3.0 – More focus on just new features. Same focus as in V1.0 and V2.0. However, now the maintenance bugs are starting to flood in. Stability of the product is decreasing. Customers are getting frustrated. Management reacts by adding more support people and creates a team of maintenance developers.
- V4.0 – The really great talent starts leaving the company out of frustration. Management starts to look at out sourcing for support and maintenance tasks as a solution.
- V5.0 – Finally, managers call in experts to help fix the issue, which means a great deal of refactoring work. This is paying a great deal of interest on technical debt that could have been avoided if that debt was dealt with on a regular basis (like paying your credit card bill monthly, not once every year or two).
How to get Managers to Understand the Cost of Legacy Code
Explain it in terms of poorly written driving instructions:
- You spend a lot of time reading the instructions trying to understand them, this is a waste of time
- So you then try and ask other people if they understand the instructions, each having a different interpretation
- Now you try and drive there, how many times do you get lost, how much longer does the drive take?
- If only the instructions were clear and concise, imagine how much time could have been saved
Now imagine how hard it is for a new developer to understand an existing system that is in a similar state as these driving instructions. The root problem is many different styles from many different developers. As a result code gets more and more confusing over time. Continuous refactoring can help mitigate this problem. Refactoring is the ongoing process of making software simple and concise.
During the talks I have attended this week there have been some books that have been mentioned over and over again.
So far the number one recommended book, and this has been recommended by presenters who also have published excellent books on design and refactoring has been:
“Working Effectively With Legacy Code” by Michael Feathers
Also, other books mentioned repeatedly have been:
“Refactoring: Improving the Design of Existing Code” by Martin Fowler, Kent Beck, John Brant, and William Opdyke
“Refactoring to Patterns” by Joshua Kerievsky
The talk from Scott Meyers was excellent. He is a great speaker and really understands the root issues that lead to many quality issues in software. Mark has already posted some comments, so I will just touch on a few common issues Scott kept stressing time and time again.
“One of the basic principles of developing quality software is consistency, from the UI to the code and even the unit tests.“
Without consistency you will never be moving toward quality software. One of the ways to go about doing this is to provide developers with a set of guidelines and best practices. If developers are not given this guidance they will each develop code and unit tests as they feel the tests should be done, which is fine, but will never result in consistency. To be able to move towards consistency provide the development team with guidelines for: code styles, naming conventions, unit test layout, refactoring standards, etc.
The point he stressed was the most important of all, above everything else is “Interfaces must be easy to use correctly, but difficult to use incorrectly”. He was talking about not just user interfaces, but all the way down through the classes, the methods, the APIs, etc. If the interface is not clear then you have failed to fix the number one issue. If the user interface is confusing, users will leave, if the code is hard to understand, developers will be less inclined to refactor and instead will make the smallest change possible every time which will lead the project further and further from being quality software.
The afternoon talk from Scott Meyers was just as good as the morning talk. I have to say that his talk is one of the most useful presentations I have ever attended, including conferences I have been to in the past. Scott Meyers really knows a lot about software quality and explains it well in his talk. He stresses in the talk about how ALL bugs are an embarrassment, not just big issues but even minor issues that are considered “cosmetic” and often ignored. Minor issues are often the ones that do not prevent a user from performing the task they wish to do, but are enough to cause annoyance and frustration which does not create a good impression of the product.