When Do You Know If It Is Time To Refactor

We as developers and designers write a lot of code. Some of it is good and sticks around from project to project. While other pieces are hacks or quick implementations meant to solve an immediate need. These poorly constructed snippets shouldn’t follow us from project to project. But in reality, they often do.

How do we solve this problem? How do we know if it’s time to refactor and fix these hacks / quick implementations? When is it worth it to invest a few extra hours or days to create a better component, module, stylesheet, script, etc.?

Time constraints, budget issues, and general laziness aside, team members at Unrelated ask themselves three questions when considering whether or not they should refactor a portion of their code. If we have any hesitations answering the questions below we know it’s time to invest in ourselves and our products.

Questions we consider:

  1. Is my code DRY, enough?
  2. Does my code align with modern web standards?
  3. Can I learn a new method or technique?

Don’t Repeat Yourself

The first question we consider is whether or not our code is DRY. This common development term stands for Don’t Repeat Yourself. Did you copy and paste any aspects of our code from one file to another? Could we create a simple loop or component that would allow us to achieve the same outcome, but create fewer lines of code?

Consider these additional questions when trying to answer whether or not your code is DRY. An example we could study is the creation of buttons in Sass. We use buttons on marketing sites, in web interfaces, and just about everywhere else. It’s easy to let these elements get out of control. Take these button selectors for instance:

.button--primary {
  @include s-foxtrot;
  background: $blue-base;
  color: $white-base;
  font-family: $primary-sans-serif;
  font-weight: $font-weight-bold;
  border: none;
  display: inline-block;
  padding: $spacer-charlie $spacer-bravo;
  text-align: center;
  text-decoration: none;
  text-transform: uppercase;
  -webkit-transition: background-color 0.5s ease-in-out;
  -moz-transition: background-color 0.5s ease-in-out;
  -o-transition: background-color 0.5s ease-in-out;
  transition: background-color 0.5s ease-in-out;
}
.button--secondary {
  @include s-foxtrot;
  background: $black-base;
  color: $white-base;
  font-family: $primary-sans-serif;
  font-weight: $font-weight-bold;
  border: none;
  display: inline-block;
  padding: $spacer-charlie $spacer-bravo;
  text-align: center;
  text-decoration: none;
  text-transform: uppercase;
  -webkit-transition: background-color 0.5s ease-in-out;
  -moz-transition: background-color 0.5s ease-in-out;
  -o-transition: background-color 0.5s ease-in-out;
  transition: background-color 0.5s ease-in-out;
}

In this example, we can see that we merely duplicated our buttons by copying and pasting. The only difference between the two button selectors above is the background colors. This violates the DRY principle. We could easily re-write these styles to be more DRY.

One way we could refactor this component might be to create a common button selector and then specific button selectors for the button types.

For example:

// Button Selector
.button {
  border: none;
  color: $white-base;
  display: inline-block;
  font-family: $primary-sans-serif;
  font-size: 15px;
  font-weight: $font-weight-bold;
  padding: $spacer-charlie $spacer-bravo;
  text-align: center;
  text-decoration: none;
  text-transform: uppercase;
  -webkit-transition: background-color 0.5s ease-in-out;
  -moz-transition: background-color 0.5s ease-in-out;
  -o-transition: background-color 0.5s ease-in-out;
  transition: background-color 0.5s ease-in-out;
}
// Button Types
.button--primary {
  background: $blue-base;
}
.button--secondary {
  background: $black-base;
}

As you can see from this example, we abstracted the common elements from each button type and added them to a selector each button object can leverage. Additionally, we created button types or modifiers that we can add to a button object to provide the correct background-color value.

Refactoring this component makes sense in this case because it allows us to reduce the amount of code in our project and allows us to update one selector vs many when a change to the core button structure is required.

Align to modern web standards

Next, we examine best practices and whether web standards have changed. Since the web is changing every day, it’s a pretty easy way of determining that we need to refactor. A few things we have considered lately are accessibility, CSS vendor prefixes, and single-page application best practices.

Let’s take a look at our button example again. Notice anything that we could fix?

// Button Selector.button {border: none;color: $white-base;display: inline-block;font-family: $primary-sans-serif;font-weight: $font-weight-bold;font-size: 15px;padding: $spacer-charlie $spacer-bravo;text-align: center;text-decoration: none;text-transform: uppercase;-webkit-transition: background-color 0.5s ease-in-out;-moz-transition: background-color 0.5s ease-in-out;-o-transition: background-color 0.5s ease-in-out;transition: background-color 0.5s ease-in-out;}

What about that transition property? According to caniuse.com, the CSS3 transition property is fully supported by modern web browsers. This means we can say goodbye to those nasty vendor prefixes.

// Button Selector
.button {
  border: none;
  color: $white-base;
  display: inline-block;
  font-family: $primary-sans-serif;
  font-weight: $font-weight-bold;
  font-size: 15px;
  padding: $spacer-charlie $spacer-bravo;
  text-align: center;
  text-decoration: none;
  text-transform: uppercase;
  -webkit-transition: background-color 0.5s ease-in-out;
  -moz-transition: background-color 0.5s ease-in-out;
  -o-transition: background-color 0.5s ease-in-out;
  transition: background-color 0.5s ease-in-out;
}

Answering just that question helped us to remove three lines of code that were unnecessary.

Learn Something New

Lastly, one of the biggest influencers in our decision-making process to refactor or not is if we can learn something new. As professionals, it’s our job to perform at the highest level and this means continuing to learn and providing added value to our own and client’s projects.

We could easily refactor our button example one more time to take advantage of a few features Sass provides us: Loops and maps. These features will allow us to keep our code DRY, align to modern web standards and learn something new in the process. Let’s take a look.

// Button Map
$buttons: (
  'primary': (
    background-color: $orange-base,
    border-color: darken($orange-base, 15%)
  ),
  'secondary': (
    background-color: $red-base,
    border-color: darken($red-base, 20%)
  )
);

First we create a button map. If you haven’t created a map in Sass before it’s worth a look. Maps create key, value pairings that can be used to generate styles. They look and function very similar to javascript arrays. In the example above, we create a map called $buttons. This map stores a couple of button types, primary and secondary. Lastly, each button type contains two key, value pairs. These pairings will help us create our button styles.

// Button Selector
.button {
  border: none;
  color: $white-base;
  display: inline-block;
  font-family: $primary-sans-serif;
  font-size: 15px;
  font-weight: $font-weight-bold;
  padding: $spacer-charlie $spacer-bravo;
  text-align: center;
  text-decoration: none;
  text-transform: uppercase;
  transition: background-color 0.5s ease-in-out;
}
// Button Loop
@each $button, $styles in $buttons {
  $bg-color: map-get($styles, background-color);
  $border:   map-get($styles, border-color);
  // Buttons
  .button--#{$button} {
    background-color: $bg-color;
    border-color: $border;
  }
}

Next, we replace our button types with a @each loop. This loop references the button type and key, value pairings – $styles – we added to our buttons map. We pull out the styles and assign them to variables with the map-get() function and then simply reference those in our dynamically created selectors below.

Easy. Right?

While the previous method would have worked just fine, this new implementation allows us to extend our buttons without having to create any new selectors. We simply add a new button type to our buttons map and let the button loop take care of generating our newly-created selector. We also learned something new about using a few Sass methods.

Could we take this further and learn something else? Sure. Maybe we need to explore ways to turn our button into a mixin that takes various options to control the background-color and padding. This would allow us to create various button types in various sizes. But, we’ll save that for another post.

When deciding whether to refactor your code consider a few things: Could your code be more DRY? Does it align to modern web standards? And can you learn something new by refactoring it? If you find yourself answering these questions positively, then it’s time to put your refactoring hat on and get to work.