Fig. 18 A Guide for Developers

How To Help Prevent Technical Debt

Don't kick the can down the road. Start with an organized codebase.

Previously, at The Scenery…

Last time, Ryan Buttrey walked through some helpful tips on how to work with technical debt. His article focused on a world where technical debt has already invaded and taken over. I want to help prevent that world from happening.

So, let’s travel back in time to a place where a project was just born; before technical debt has gotten its grubby fingers into an innocent project’s code.

Remember, Technical Debt is always present to some degree. The trick is to mitigate it in a certain way that allows for quick fixes where necessary and allows a team or individual to understand quirks and possible hangups without slowing down productivity. It’s a fine balance.

Let’s prevent some technical debt.

Low-hanging Fruit: Generic Names, Simple Styles

When starting at the very beginning of a project, one simple way to stave off possible technical debt is by naming classes generically. Obviously CSS styles should be repeatable, but they also need to be repeatable.

Building components with a forward thinking mindset can make them easier to extend when the time is right. Distill overlapping styles, then add to them when you find either a “one-off” situation, or when a future design refresh calls for a widely-used variant of the original element.

Example:

.header {
  font-weight: 700;
  text-align: center;
}

.header--xlarge {
  font-size: 36px;
}

The original .header style only manages text placement and weight. Extending that class with a category modifier allows it to be further focused, and doesn’t cause a bunch of needless overrides in the cascade.

Technical Debt in the form of difficult-to-troubleshoot gibberish

CSS pre-processors are a wonderful, wonderful tool. However, the nesting features can make troubleshooting funky styles more difficult than it needs to be. Sure, you can track down offending lines if sourcemaps are enabled, but not all environments support sourcemaps. In addition, nesting can cause accidental specificity (complete with super long selector chains) which may (most likely will) result in a refactor down the road.

How do we use a pre-processor’s features while still keeping nesting to a minimum and keeping things readable? Well we use nesting in one of three situations:

  1. Nesting behaviors is a-okay 👌. It’s totally cool to nest :hover or :focus behavior alongside links or form elements. Those pseudo-selectors should be coupled with their parent element.
  2. Nesting other pseudo-selectors is fine too. Keeping :last-child nearby also makes sense.
  3. If two or more classes only work together at the same time, pair them.

For that last point, let’s examine a .button class. This class only sets a display mode and padding, and maybe font styles for good measure. Helper classes, such as .button--normal or .button--ghost only set background colors and behaviors, but don’t work by themselves. Keeping them nested together in a pre-processor makes sense. It’s not required, but we build this way it if we need to bundle styles together.

So as a refresher:

Nesting Behaviors 👌

.main-nav-link {
  color: black;

  &:hover {
    color: blue;
  }
}

Nesting Pseudo-Selectors 👌

.list-item {
  &:not(:last-child) {
    margin-bottom: .5em;
  }
}

Nesting Cooperative Classes 👌

.button {
  display: inline-block;
  padding: .15em .3em;

  &.button--ghost {
    background-color: transparent;
    border: 1px solid blue;
    border-radius: 3px;
    color: blue;
  }
}

Using nesting only when necessary (and honestly as little as possible) can make code easy to read for troubleshooting purposes, as well as when working in a collaborative environment.

Random, but please stop using “magic numbers.” 💩

I have written about this before, but it’s worth a second mention.

.nav.active {
  z-index: 72;
}

.modal {
  width: calc(12% - 2.3em); // GOD WHY?
}

Let’s go with this instead:

.nav.active {
  z-index: 72; // Needs to be above hero (70) and below modal (75)
}

.modal {
  width: calc(12% - 2.3em); // To fit within grid system
}

If a certain calculation was done, explain the methodology in a comment. Otherwise, values appears to be arbitrary. Changing these values could cause regressions, but the “why” would take longer to track down. A short explanation can save a lot of time and prepare the developer known buggy behavior if a change needs to take place.

Create a living style guide

Before, Ryan mentioned that a good way to get a handle on a codebase is to spend time reading it. As long as everything is commented and organized in a sane fashion, this should be a somewhat simple task. Creating a living style guide would make things even easier for you, your team, your future you, and your future team.

KSS (Knyle Style Sheets), Fabricator and Fractal are great examples of style guide creation tools. These tools generate organized HTML views listing all available styles and their paired example usage markup. Also, they can be added to your build tools (such as Gulp or Grunt) to be generated on the fly. Update your stylesheets and your style guide is instantly updated. Bam.

This is a simple way to provide organized documentation over the low bar standard of inline comments.

Embrace working alongside a CSS framework

For MVPs, getting a working design out the door as quickly as possible is everything. A lot of teams use Bootstrap or Foundation or [insert framework here] to help speed up the development process. Sometimes, however, only a small portion of the framework is actually used broadly. Grid systems are easy to lean on, but future styles may actually start to fight the chosen framework. New additions to the design language may ignore the framework altogether.

Instead, extend the framework. Build helper classes for new design elements while keeping the same coding standard the framework has started for you. What do we get with framework extension?

  1. The stylesheet is speaking one language with similar class names and structure
  2. Documentation is built-in (provided you don’t stray from the framework’s coding standards and add comments where necessary)
  3. Overrides are kept to a minimum (configuration occurs at the top of the cascade)

When it comes to speed, frameworks are hard to beat and provide a well-documented and tested foundation.

Bundle styles with components

This one may feel strange. Using a component library such as React (or even with Web Components), inline style usage is common. Their specificity doesn’t pollute global styles, and it’s really easy to make changes—the styles can be within the component JavaScript so file organization is more flat.

Unfortunately, inline styles make it difficult to support pseudo selectors (:hover) or media queries. They just don’t happen.

Just kidding… They can happen.

Radium is a tool that allows for support of pseudo selectors AND media queries! Heck, it even allows for preprocessor-like nesting! It’s definitely worth a look for React projects.

This may seem like a more extreme option, but it is an option. It also depends on your project structure and coding standards (obviously).

Prevention is the best medicine

Again, technical debt cannot be completely stopped, but it can be managed before it becomes an actual wall for project momentum. These tips aren’t orders, but they are worth considering. We use these methods at The Scenery, and they are constantly being reconsidered based on the age of a clients’ project and the technology being used. In the future, we’ll have more tips to share as we come across more scenarios.

Written by Andy Rossi Published on October 06, 2016

Read More