My first week on a new team I broke production.

frightened

The feeling of getting a notification of my PR being reverted by the oncall engineer because it broke production was not fun. This happened because on my first week, I decided to try to improve some of the naming in our codebase; instead of everyone being ecstatic about the easier-to-work-with names, I ended up becoming scared to do any refactor like that again. Coming from a statically typed language background, I learned my mistakes the hard way when moving to coding in a dynamic language like Ruby. From this experience, I learned how refactoring can make you wish you never started touching it, how refactoring can make you scared to refactor again, but also how to do it correctly and the benefits to the team once it’s done.

For this article, I will be specifically focusing on advice for renaming in a dynamic language, where it is difficult to find the usages you are looking for. The advice will largely pertain to monolithic codebases but can be relevant to all forms of refactoring.

Here are the sections that will be covered

  1. Figuring out if it’s worth it
  2. Refactoring checklist
  3. My takeaways from this experience

Figuring out if its worth it

Sometimes the toughest part about refactoring is figuring out whether you should do it in the first place. In my case, I started on a tech spec and was finding it incredibly difficult to see what changes needed to be made without doing some basic naming changes first. I ended up splitting my tech spec into two parts. Part 1 was the refactor, and Part 2 was the behavioral changes. By doing this, my reviewers were able to just focus on the naming improvements without worrying about me modifying behavior. It also made the feature changes much easier since I was able to work with cleaner code. For more information on separating structural and behavioral changes, Kent Beck wrote an article about it here.

A few questions you may want to ask yourself to determine if the refactor is worth it:

  • Will you be using the refactored code often after updating it?
    • Positive indicators
      • Will be adding additional functionality that will use the refactored code.
    • Negative indicators
      • The code has not been touched for a long time and is not expected to be touched for awhile.
  • If a name is misleading, how misleading is it and could it cause errors down the road if misunderstood?
    • Positive indicators
      • The name has implicit assumptions. For example: the method name is is_active? but actually means is_active_or_upcoming?
    • Negative indicators
      • It is unclear whether the name change would be better.
  • If the refactor is performed incorrectly, who is impacted?
    • Positive indicators
      • Minimal impact to the customer if done incorrectly. Only errors on the backend logged.
    • Negative indicators
      • If done incorrectly, customers would not be able to perform basic functionality within the app.
  • How easy is it to confirm the refactor was done right, all cases were caught, and no issues should be run into?
    • Positive indicators
      • Method name was very distinct to begin with, and easy to find all cases
      • Test coverage is very good for the affected areas.
    • Negative indicators
      • Method name is hard to distinguish between other usages of the same method name that are not relevant.
      • Test coverage is poor for the affected areas.

You can generally use these indicators to determine whether a refactor is worth doing, and the value you place on each one. It’s best to consult with the rest of the team before doing the refactor, presenting your argument for why it should be done, but also opening up about the risk of performing it.

Let’s assume you and your team decide the refactor is worth it.

Tips/Checklist

The first thing that should be done if this refactor is large in scope and there is a concern of breaking old usages:

  • Create a second method with the new name, copy the code over, and call that method from the old one. Additionally, log a warning from the old method to indicate it’s still being called.

Once this is done, we can search for the old usages and update them to the new method without worry that a breakage will occur.

Text searching tips (Ruby specific)

Let’s assume that we are searching for usages of a method on the database model Apple. Some of the following tips may seem obvious but it’s a checklist so they should be ;). Pay careful attention to the . where appropriate.

actual

Disclaimer: The editor I work with is RubyMine but most tips should apply or can be slightly modified to work for your editor.

  1. Search for usages of apple.. This can be an example such as apple.method_name
  2. Do the above search with apple.method_name first to clear up most of them.
  3. After doing the above, regex search for apple(.*)method_name to catch cases where there is a new line in between apple and method_name.
  4. Search for usages of apples.. This can be an example such as farmer.apples.first.method_name
  5. Search for usages of Apple. This can be an example such as Apple.first.method_name.
  6. Depending on what you’re searching for, you may want to do a “word” or “exact” search that will only search for cases without additional text. For example, without word search, “apple” would be shown in “applepicker” but with word search “applepicker” would not show up.

Lastly, for any areas of the code you updated and are not sure about, feel free to take it as an opportunity to check test coverage to ensure what you changed is being tested. For Rspec, our own Kelly Sutton wrote an article about “counting the contexts” to sniff test the test coverage.

Although my initial attempts at refactoring ended up breaking production, I ended up pair programming with my manager to make the fixes needed and to learn the tips above. I shared with him how the initial attempts at refactoring made me scared to make changes again, but he reminded me that it’s a good quality to take the initiative to change things if they can be better, and that I shouldn’t be afraid of refactoring in the future. Since my mistakes, I have had the confidence to make multiple successful refactors in our codebase. It’s kinda like how if you got into a car accident and didn’t get back to driving soon after recovering, you may have trouble getting back to driving at all. In the words of Rocky Balboa, “It ain’t about how hard you hit, it’s about how hard you can get hit, and keep moving forward.”

LRM_EXPORT_62713208897292_20190528_225601872-copy-2