Cover image taken from an illustration by Dirk-Jan Hoek
I recently learned about a logical fallacy called The Texas Sharpshooter fallacy. The first thought that crossed my mind when I first read about it was, "What does Texas have to do with any of this?". I still don't have a convincing answer for that one.
More importantly, the second thought that crossed my mind was how The Texas Sharpshooter fallacy clearly expresses one of the ways I think about Test-Driven Development (TDD). This blog post describes how TDD may reduce your likelihood of writing tests like a Texas Sharpshooter.
So what is this Texas Sharpshooter fallacy anyway?
The Texas Sharpshooter Fallacy
The Texas Sharpshooter Fallacy occurs when you find patterns to fit something you've already assumed to be true or correct. The name comes from an analogy where a shooter fires shots at a barn door and then paints targets around the bullet holes to create the false impression of being an excellent marksman.
Test-Driven Development (TDD)
TDD is a software development approach that encourages you to write tests before writing the code that makes the tests pass. It revolves around a red-green-refactor cycle that comprises the following steps:
- Write a test that fails (red)
- Write the minimal amount of code that makes the test pass (green)
- Refactor the code to meet quality standards while ensuring that tests still pass.
TDD has several benefits. One often touted benefit of TDD is that it helps you make better design decisions. Since TDD demands that you write tests first before writing the corresponding code, it forces you to always start by thinking through the design and expected behavior of the code you're going to write. This upfront focus on design and behavior often results in better software design and architecture relative to a test-after approach where you dive straight into writing code before writing corresponding tests.
What does the Texas Sharpshooter Fallacy have to do with TDD?
The positive influence that TDD can have on software design is generally well-known. A less popular discussion is the direct positive influence that TDD could have on your tests. Remember that tests should verify that your code behaves the way it is supposed to, as defined by software requirements. Sometimes, you may end up writing code that seems acceptable to you without realizing that there is some difference between what your code does and what it is supposed to do. When you write code before writing tests, you are more likely to write tests that verify what the code does instead of what it is supposed to do. Writing code before tests makes it too easy for you to mold your tests around flawed code without realizing it. In many cases, you'll end up with tests that merely confirm flawed behavior.
TDD forces you to start by writing a failing test based on the expected behavior of the code you'll eventually write. Note that the code that the failing test seeks to verify does not exist at this point, so you are primarily focused on the expected behavior, and there is nothing around which to (incorrectly) mold your tests. In other words, your tests written as part of the TDD cycle are much less likely to be biased by code you've already written because the code does not exist yet.
In the context of the Texas Sharpshooter analogy, your code represents the shots you've fired at the barn door, and your tests are the targets that those shots are meant to hit. Writing your code before writing your tests is like firing shots at the barn door before painting the targets. When you write your code first (i.e., fire shots) and then write the tests afterward (i.e, paint targets), it is too easy to start writing tests that are influenced ONLY by the current behavior of the code you've just written, even when that behavior is not what you intended or what the software requirements demand. If you are not careful, writing tests after writing your code will turn you into a "Texas Sharpshooter". And it's going to feel great. After all, your shots will almost always hit the target when you shoot first and paint targets around the bullet holes afterward.
Many software developers (including me) tend to write tests after writing implementation code but do not always realize that they are sharpshooters painting targets around problematic code. TDD encourages you to write tests BEFORE writing the code that makes the tests pass. Therefore, if you want to avoid being a "sharpshooter", try TDD.