Tests are an observability concern

The first thing I do when I encounter a strange code base is run the test suite. I do this because I can get an idea of:

  1. the exact use cases that the developers envisioned
  2. the actual usability of the APIs
  3. the overall health (read: test coverage) of the project
  4. and get an idea of how development is done

I’ve heard a lot about the benefits of test suites, even in the context of articles that are against TDD (test-driven development). For instance:

  1. They’re for regression testing
  2. They inform the design of the software
  3. They are a way of organizing the work into small chunks

And conversely, the negatives:

  1. They are code, and code is a liability
  2. They are slow to write and maintain or refactor
  3. You need to know what you are doing in order to write a test

I’m not here to argue with any of these points, and I think different points are valid for different people and different situations. What I am interested in exploring is the idea that tests are a part of your code’s observability stack:

Observability is a system property that defines the degree to which the system can generate actionable insights. It allows users to understand a system’s state from these external outputs and take (corrective) action.

Observability | Cloud Native Glossary

Tests are simple red/green line-item indicators for the functionality of your system. In this case, the “system” is your code and the “user” is the developer. When a test fails, it is an “actionable insight” allowing the user to understand the code’s state and take action (you could also argue that testing the public interfaces of code makes it an “external” output).

A system that simply performs its function with no affordances for observability is a black box that doesn’t lend itself to change. Without indicators of what it’s doing or how it functions, it’s nearly impossible to have any confidence in its performance or methodology.

Imagine running ./bin/server and being greeted with a blank screen. Is it running? What port is it listening on? What does it do? What is its API like?

A code base without a test suite is like a server with no logs. As someone tasked with the maintenance of the code base, developers are higher order “operators” of sorts, where the output of the operation is a functioning program.

Ultimately, this post is less about the importance of tests and more about the importance of observability. Being able to see into our systems is crucial in order to maintain them, and tests are a front-line mechanism for deriving actionable insights from the state of your code.

To me, legacy code is simply code without tests. […] Code without tests is bad code. It doesn’t matter how well written it is; it doesn’t matter how pretty or object-oriented or well-encapsulated it is. With tests, we can change the behavior of our code quickly and verifiably. Without them, we really don’t know if our code is getting better or worse.

Michael Feathers, Working Effectively with Legacy Code

Observability at any level may not be a priority for a two-person startup, but it is guaranteed to become a critical concern the more successful a project becomes. The more observable your system is, the easier it is to fix or change.

I’ll close out with an analogy that I’ve been toying with: a test suite is basically the nervous system of code. Making a change that triggers a regression in the unit test is like stretching in the morning and rediscovering that yesterday was leg day. Your nerves tell you swiftly where the affected area is, and you can move to correct the error. Without your nerves to tell you where and what is happening, you won’t be able to react appropriately to the state of your health. The same goes with a good unit test. Without tests, your code is numb to changes and prone to stumbling.

Leave a comment