Code Reuse and Simplicity

Monday July 23, 2018
Software Development

The Myth of Code Reuse, a quick
video by Iain Lowe, became quite popular on Reddit in
/r/programming a few weeks ago. I think
the reason for it’s popularity is obvious. Lowe talks about the amount of time
developers invest in making code “reusable” with interfaces and abstractions,
only to watch that code be replaced a few years down the road. Many professional
developers have worked with legacy code bases that frequently use these types
of abstractions (designed for “future-proof” code), but found the code to be
more complicated than necessary and difficult to work with.

Developers have been taught that code reuse is an important part of good code,
so they tend to try to put it into every system they design. Unfortunately, this
also tends to add to the complexity of the system. Often, the “reusable” code is
never actually reused. In the end, the abstraction adds complexity to the system
without delivering any additional value.

Similarly, a blog post about the cost of using The Wrong
Abstraction was
recently on the front page of Hacker News. Like
the video, this blog post illustrates the difficulty of working with
abstractions that are a poor match for the codebase. Whether by poor design or
by a lack of refactoring as the code base evolves, bad abstraction patterns can
turn into a big code maintenance cost.

How can developers safe-guard against anti-patterns emerging in their code base
to create code that is acutally maintainable? I think there are 2 important
rules to follow:

Complexity is bad. Keep the code as simple as possible.

Write unit tests.

Complexity is Bad

It’s easy for software developers to get carried away with the design of their
code. They want to apply all the algorithms and design patterns they’ve learned.
They want to use the latest technologies and newest features. They take pride
in their work, and they don’t just want a solution that’s good enough. They want
the best solution.

This often leads to bloated, complex code that’s difficult to work with.
Interfaces and other abstractions pop up all over the code base, even when
they’re not needed at all. Sometimes, developers will go as far as developing
classes that support things like custom plugin or callback architectures because
they envision a future where it might be needed. This usually results in bad
code because it’s more complex than it needs to be. It’s easy for developers to
be conned into the trap of building cool stuff that wasn’t asked for. If a
plugin system is required (as part of the story, within scope) because there’s
specific planned work that will make use of it, then it obviously needs to be
written. Otherwise, don’t write it. If code is going to be reused (or even
maintained for several years), it needs to be easy for other developers to read
and understand. Every bit of added complexity makes code harder to maintain.
Complexity is bad. In almost every case, the best code is the simplest code.

Write Unit Tests

The counter-argument to the case I’m making for simplicity is, of course, the
need to prepare for the future. Some might say that the argument against
complexity is short-sighted, and that it’s a good trade-off to introduce some
complexity into the system to make life easier down the road. But I don’t think
that’s usually the case. The future is unpredictable, and you’ll inevitably
build something that is different than what you end up needing.

So, how do you protect your code base against an uncertain future? A
high-quality unit test suite is the best way to ensure a code base continues to
be maintainable. If developers can’t quickly introduce changes without the fear
of breaking existing functionality, the code base is no longer maintainable. The
best way to provide the security future developers need (whether it’s yourself
or others) is with a good unit test suite. Good unit tests are a kind of living
documentation of what the code is expected to do, and they evolve with the code,
providing a safety net for making changes. This safety net allows the
development team to make changes to the code base quickly, providing a hedge
against the uncertainty of the future.

TL;DR

Complexity is bad. Keep your code as simple as possible. Write high-quality unit
tests and keep them up-to-date to ensure the code base can evolve rapidly. Unit
tests are the best way to future-proof your code base.