Working with code

Software Entropy

As you explore code, you’ll notice its shortcomings. Messy code is a natural side effect of change; don’t blame developers for the untidiness. This drift toward disarray is known as software entropy.

Technical Debt

Technical debt is a major cause of software entropy. Technical debt is future work that’s owed to fix shortcomings in existing code.

Like financial debt, technical debt has principal and interest. The principal is the original shortcoming that needs to be fixed. Interest is paid as code evolves without addressing the underlying shortcoming—increasingly complex workarounds are implemented.

Technical decisions that you disagree with are not technical debt. Neither is code that you don’t like.

Technical debt matrix. Source: https://martinfowler.com/bliki/TechnicalDebtQuadrant.html

RECKLESS

PRUDENT

DELIBERATE

“We don’t have time for design.”
(When teams are under pressure
to just deliver)
“Let’s ship now and deal with consequences.”
(good)

INADVERTENT

“What’s layering?”
(Unknown unknowns)
“Now we know how we should’ve done it.”
(Natural outcome of growing experience.)

Addressing Technical Debt

The following is a good framework for discussing technical debt:

  • State the situation factually.

  • Describe the risk and cost of the debt.

  • Propose a solution.

  • Discuss alternatives (including not acting).

  • Weigh the trade-offs.

Changing Code

Use the Legacy Code Change Algorithm:

  1. Identify change points.

  2. Find test points. (Entry points into the code that you want to modify)

  3. Break dependencies. (Means changing code structure so that it’s easier to test.)

  4. Write tests.

  5. Make changes and refactor.

Think of the first four steps as clearing space and building a fence around a field before planting seeds in step 5.

A wide variety of techniques exist to break dependencies, including the following:

  • Pulling apart a large, complex method into multiple smaller methods so separate pieces of functionality can be tested independently

  • Introducing an interface (or other indirection) to give tests a way to supply a simple implementation of a complex object— incomplete, but sufficient for testing

  • Injecting explicit control points that permit you to simulate aspects of execution that are hard to control, such as passage of time

Changing large existing codebases is a skill refined over years — decades, even.

The following tips will get you started.

Leave Code Cleaner Than You Found It: Try to keep the code-cleanup commits separate from your behavior-changing commits. Separating commits makes it easier to revert code changes without losing code-cleanup commits. Smaller commits also make changes easier to review.

Code smell: is a term for code that isn’t necessarily buggy but uses patterns known to cause problems: Many linters and code quality tools will detect this problem, as well as other code smells like long methods or classes, duplicate code, excessive branching or looping, or having too many parameters. More subtle anti-patterns are harder to identify and correct without tooling and experience.

Make Incremental Changes: keep your refactoring changes small. Make separate pull requests for each of the steps in the code change algorithm

Be Pragmatic About Refactoring: It is not always wise to refactor. There are deadlines and competing priorities. Refactoring takes time. Your team might decide to ignore refactoring opportunities to ship new features. Such decisions add to the team’s technical debt. The cost of the refactor might also exceed its value. Old, deprecated code that’s being replaced doesn’t need to be refactored, nor does code that’s low risk or rarely touched.

Use an IDE: Integrated development environments (IDEs) carry a stigma among l33t coders; they see getting “help” from an editor as a weakness and fetishize Vim or Emacs—“an elegant weapon for a more civilized age.” This is non-sense. Take advantage of the tools that are available to you. If your lan- guage has a good IDE, use it.

Use Version Control System Best Practices: write good commit messages:

  1. Separate subject from body with a blank line.

  2. Limit the subject line to 50 characters.

  3. Capitalize the subject line.

  4. Do not end the subject line with a period.

  5. Use the imperative mood in the subject line.

  6. Wrap the body at 72 characters.

  7. Use the body to explain what and why versus how.

You can refer to this resource for more fun regarding changing code and refactoring: https://refactoring.guru/

Avoiding Pitfalls

If you want to rewrite code or diverge from standards, your improvement must be an order of magnitude better. Small gains aren’t enough—the cost is too high. Most engineers underestimate the value of convention and overestimate the value of ignoring it.

Use Boring (stable and old and practiced) Technology.

Don’t Go Rogue: Don’t ignore your company’s (or industry’s) standards because you don’t like them.

Don’t Fork Without Committing Upstream: Maintaining an internal company fork is particularly pernicious. Developers will tell each other that they’ll contribute the changes back “later.” This rarely happens. Minor tweaks that are not contributed upstream compound over time. Eventually, you’re running an entirely different piece of software. Features and bug fixes become increasingly difficult to merge upstream. The team discovers that it has implicitly signed up to maintain an entire project.

Resist the Temptation to Rewrite: Single most stupid idea between software engineers. Do not start from zero, throwing away existing codebase. Code is ugly because it solved bugs and evolved.

Note

Sources:

  1. The Missing README: A Guide for the New Software Engineer © 2021 by Chris Riccomini and Dmitriy Ryaboy, Chapter 3.