A commitment to unit testing results in easier to maintain code in the long term

The benefits of religiously following a strict Test Driven Development (TDD) approach (always write tests first before code) is a hotly debated topic. No experienced developer would ignore the fact that testing is essential, but there are debatable pros and cons for whether writing unit tests before, during or after has the most benefit.

If your team has a commitment to unit testing there is valuable side benefit to your code quality that can arise by itself but is usually encouraged by more experienced development teams. Overly complex code that doesn’t have a clear or single responsibility, is tightly coupled with other code and/of is excessively long, is hard to test. Complex code is hard to understand and maintain, it’s usually also the source for more defects compared with simpler and easier to understand code. Effective teams always look for areas where they can become more efficient and make their lives easier, and aiming to develop simpler code to make it easier to test brings these additional benefits with it.

By adopting a mindset that code should be developed and structured in a simpler way so that it is easier to test, you instantly gain from the benefits of simpler code that’s easier to maintain. Win-win all round.

Write code for humans first, computers second

When first learning to develop software, it’s natural to focus on getting your code to work as your first priority. As development is an incremental process, between multiple changes and attempts to get your code working, it’s natural that your first working version of your code is probably, to put it bluntly, a mess.

As you progress in your career, you realize the importance of developing code that is easy to read and understand, because at the end of the day, any software developed or maintained by a team has to be read and understood by other developers, not just the original developer. If no-one else on your team and understand your code then it’s unmaintainable and that poses a long term risk; if it breaks who is going to be able to fix it?

I commonly see posts online by new developers who are first starting out questioning the point and importance of spending time to refactor code, ensuring code is clear and readable, in essence, all attributes of maintainable code. The first time you come across code that you struggle to understand and you’re faced with making updates or fixing an issue, you’ll quickly realize the importance of developing code that is easy for other humans to understand.

This is not a new concept, it’s summarized clearly by the famous quote from Donald Knuth:

“Programs are meant to be read by humans and only incidentally for computers to execute.”

Code comments should help you and other developers understand the code. Or “why project code standards can result in unwanted behaviors”

The purpose of commenting your code is to help you and other developers that come after you to understand the code. If a code comment doesn’t offer anything in addition to the code itself then it’s pointless and just adds noise.

Method and variable naming is also important, a descriptive method name shouldn’t need copious additional inline comments if it already describes what it does. Obviously you don’t want the extreme and have a method name that’s hundreds of characters long. Be sensible.

I often come back to these 2 Twitter posts that illustrate how inline comments that state the obvious are useless:

Another one making the same point:

There’s many reasons why developer’s comments add little value. The main reason is probably just not putting in the effort and time to write something that’s useful. There’s another reason though that leads to documentation that looks exactly like this:

/**
 * This method retrieves an Account.
 * @param accountId the account id
 * @return the Account
 */
public Account retrieveAccountById(String accountId){
    // code here
}

First, the name of the method, the parameter and the return type tell you a lot about the purpose of this method. So why would anyone take the time to write “This method retrieves an Account” that clearly just repeats what the code already says?

Have you ever seen statements like these:

  • Every Class must have JavaDoc that describes the Class
  • Every method must have JavaDoc that describes the method and it’s parameters

This is an example where standards encourage unexpected or unwanted behaviors. The intent of both of these statements is good. In most cases it is beneficial to have documentation that helps others understand what the code is and what is does. There is a point however that if you’ve named your method, it’s parameters and it’s return clear enough, there isn’t anything left to say in the documentation for the method.

“But the code standards say we must write JavaDocs, so I have to write something!”

Standards and rules are established for a purpose, but they have to add value. Be flexible. If something doesn’t add value, you shouldn’t follow it blindly. Do what makes sense.

Good code feels good, bad code feels bad : Trust your instincts

There’s been many attempts over the years to measure code quality with quantifiable metrics, such as cyclometric complexity (a measure of complexity caused by nested conditional branches). An experienced developer who has seen good code and bad code will have an instinctive feeling whether code is good or bad. Gut feel is valuable because it’s almost always right. If you look at code and it feels wrong, you’re probably right. Based on experience, you get a feeling that something’s not right. The difficult part is nailing down what’s not right, and then refactoring to make it better.

There’s been a number of best practice principals established over the past decades of software development that have stood the test of time, and are widely accepted. There are many other ideas and concepts that are topics for hot debate, but these few core ideas seem to be largely accepted by most developers.

In order to understand what it is we’re aiming for, I’ll take a stab at defining what I consider to be the core qualities of good quality code. Quality code is:

  • easy to read, and easy to understand
  • easily testable (with unit tests)
  • easy to fix, and easy to modify

You could expand this list with many other factors, but I think these distill the essence of high quality code. There’s a direct relationship between these qualities and the complexity of the code too. Remember cyclometric complexity measures the complexity of code based on number of branches – code that has a high number of nested branches with a high cyclometric complexity count would probably fail all the above qualities: deeply nested conditional code is usually difficult to read and understand, hard to test, and also usually hard to fix and modify.

As a broad generalized statement, simple code is usually high quality code, complex code is usually poor quality (based on the factors above).

The problem is, it’s not easy to write simple code, it takes time, effort and experience. It’s pretty easy to write complex code, but simple code usually takes considerably more time and effort. Without thought and attention, even as you work on simple code over time, there’s a danger that the code becomes more complicated the more changes are made to it. It takes effort to keep code simple, to keep it from deteriorating over time.

Best Practices for Writing Quality Code

There’s a number of established best practices in software development, many of which if followed help you towards writing code that achieves the 3 points above (easy to read, easy to test, easy to fix). Bob Martin in his book Agile Software Development, Principles, Patterns, and Practices, captures a number of these in the SOLID mnemonic:

  • Single Responsibility Principal
  • Open Closed Principal
  • Liskov Substitution Principal
  • Interface Segregation Principal
  • Dependency Inversion Principal

Other popular industry best practices include DRY (Don’t Repeat Yourself), and the YAGNI (You Ain’t Gonna Need It) idea also contribute and guide you towards developing simple, quality code.

Many of these concepts are deeply rooted in the first of the SOLID principals, the Single Responsibility Principal. If you have a Class that ‘does one thing and does it well’ (another common way this principal is expressed), or a method that does one thing, this code is typically easy to read, easy to test and easy to fix.

Many other best practices and guidelines tend to result indirectly in achieving the same desirable results. In many cases regardless of the approach or best practice followed, the ultimate result is code that does one thing and does it well. Code with this ultimate quality meets the criteria of easy to read, easy to test and easy to fix.