Siena Aguayo, a software engineer from Indiegogo talks about the philosophy of refactoring, and the steps needed to reach refactoring zen.
I’m Siena Aguayo, and I’m a software engineer at Indiegogo, a crowd-funding platform based in San Francisco. I worked on both our iOS and Android apps and now I’m a full-stack web developer.
Today I will cover refactoring.
Consider this scenario: you start refactoring and realize something looks bad, then you try to fix it and you found this other thing, and next thing you know, you’ve lost track of what you were doing.
The strategy that I’m presenting to you today is something that I have distilled down from the work of others. In particular, Sandi Metz, who is a very prominent figure in the Ruby community, and Martin Fowler, who wrote the classic book, “Refactoring”.
Chances are you are not perfect. You didn’t write your code perfectly the first time, and it’s not really fair to try and anticipate your future needs. Refactoring is to help you.
Get more development news like this
Four Steps to Refactoring
The first step is “Discover”. You have to identify the code you wish to change. Sometimes, this is very apparent to you, because the code is screaming for a refactor. Other times, you may not know where to start.
When that happens, you can follow your nose, through “code smells”. The term “Code Smell” came from Martin Fowler’s Refactoring book.
Code Smells Categorization
This hierarchical categorization was developed by a Finnish professor Mica Mantiles.
1) Bloaters: things that make your code large, they’re too big, with too many responsibilities.
Examples: long methods, large classes, primitive obsession, long parameter list, data clumps.
Primitive obsession is when a string or an integer has started to take on special meaning.
Long parameter list and data clumps: Data clumps are two or more pieces of data that always travel together around your code base, they’re never separated. This is a good signal that they should be put together in their own class.
2) Object-Orientation Abusers: these are failed applications of Object Oriented Programming.
Switch Statements. The caveat is that Switch Statements are fine as long as you’re switching on your own data in your own specific part of a layer.
Temporary Field - something you only use once.
Refused Bequest, is when you only implement part of an interface.
Alternative classes with different interfaces.
3) Change Preventers
These are things that make it really difficult to come in and start making some of these changes:
Example: Shotgun Surgery and Divergent Surgery.
Shotgun Surgery is when you make small changes in a lot of different places to achieve what you’re trying to do. Divergent change is the opposite of that where when you go to a very big file, and you’re working on a specific subset of things. This is an indication that different things with different responsibilities can be pulled out.
Example: Parallel Inheritance Hierarchies: The existence of a concept that is repeated across different classes.
Examples: Lazy Class, Data Class, Duplicated Code, Dead Code, Speculative Generality, Comments
Lazy Class: A class no longer justifying its existence in utility.
Duplicated Code: Self-explanatory.
Dead Code: Code no longer used.
Speculative Generality: When a method was created for general use, but ends up being specific.
Comments: Comments are not inherently bad, but they may mask code smell. If you have to explain something in a comment, that might be an indication that there’s some other code smell going on that you need to deal with.
Couplers are things that know too much about how other things work.
Examples: Feature Envy/Inappropriate Intimacy, Message Chains, Middle Man
Feature Envy: When a class knows too much about another class and it feels like it may belong in that class just because it is doing so much.
Message Chain: When you call a bunch of methods on a thing.
Middle Man: Something that has to go in between two objects.
To change, the smallest possible fix has to be done at a time, even smaller than you might be used to. This will be worth it and it will make your refactors smoother.
You should write tests for new code, but if you have legacy code that does not have tests, you should add test coverage when it becomes actually possible.
Repeating is a zen state you reach when refactoring. It is good to start feeling safe and bored when refactoring. You do not want to be in a danger zone where everything is crashing.
Applying the Strategy
I will apply this strategy to the Indiegogo Android app.
At Indiegogo, you give money to a campaign, and you get a perk. For example, if you give 20 dollars to a campaign you may get a T-shirt as a perk.
We will be refactoring a view dealing with perks. How a perk is displayed is based on different states in the campaign.
- Sold out: Some limited number available, but the perk is sold out.
- Ended: The campaign is over and you cannot buy the perks anymore.
We’re going to be remodeling the code that is in charge of all that, so you use a Model View ViewModel pattern.
The code we will refactor is part of a Model-View-ViewModel (MVVM).
Model: Contains the data that is going into your view - such as a text on a button.
View: Something that inherits from the Android Framework view, in this case, it will be the Button.
ViewModel: The class in charge of taking data from the model and assigning it to the view.
See Siena’s demonstration of refactoring from 14:08-23:31
To recap, we focused on old code that was in a generic helper class. We focused on reducing the complexity of the main conditional statement. Switching over to an MVVM style architecture, our conditional is much more manageable.
**_Q: Do you have any recommendations for how to ensure you have a good Unit test suite and just make sure that your tests are running consistently and testing the right things? **
Siena: You can use this refactoring strategy to move you towards that. Once you’ve separated something out enough that it doesn’t have any more dependencies on, for example, the Android Framework, then that’s a great time to stop and write Unit tests.