Bugs caused by the way we write code and design our software

The first two are problems related to our computer system. We can fix these by paying attention to how we create software, both in the coding and design.

As Arlo Belshee points out in his talks on #BugsZero, the likely cause of a bug isn't always what we might think; he lists the causes in the following order:

  • Readability of code: Not paying care and attention, bad use of whitespace, poor naming, and long methods all contribute to the lack of readability
  • Context dependence: When code is dependent on the context it is running in and we introduce design flaws such as tight coupling, we make it overly complex and hard to test

The complexity of computer systems can mask the root cause of the bug, especially where multiple different pieces interact, with varying degrees of coupling. Of course, we try to prevent bugs by using a variety of quality assurance techniques. These traditionally involve testing after the code is written, but test-after strategies have little to no impact on how we design software.

Another bad smell when automating our tests is that we find our code hard to test; having untestable code is a reliable indicator that the code is not straightforward. To overcome this, rather than refactor the code, teams will use complicated and fragile testing strategies, such as the ones driven by the UI (Selenium, for example) or too many mocks.

Instead, if we're serious about reducing bugs, we have to get serious about making our code more readable and also reducing code complexity. This requires strategies, including clean coding standards, TDD and refactoring, emergent architecture, trunk-based development and continuous integration, and so on. 

This has caused a shift to the left as we discussed in Chapter 8, Tightening Feedback Loops in the Software Development Life Cycle for these activities. We don't just flip straight to writing code. Instead, we consider and incrementally implement strategies for their automation. Plus, if we're serious about continuous integration, we will learn how to avoid long-lived feature branches by becoming more trunk-based in our code committing strategy.

Test-Driven Development (TDD) helps fix an important aspect of this problem because code designed with TDD is often much more atomic and simple in nature. From this perspective, we can see that the test-before nature of TDD isn't really a testing strategy; instead, it's a design strategy which uses a test harness to make refactoring our code safer and more effective.

To reduce code complexity with a test-after testing strategy then we first need to be serious about good design principles and refactoring. Test strategies in themselves are not preventative; as the saying goes, testing only proves the presence of bugs, not the absence of them. 

As a baseline to all of the above, we have to write clean code proactively; all Agile teams should be familiar with Robert Uncle Bob Martin's book Clean Code. The boy scout rule that each time we touch the code, we leave it in a better state than when we found it, is imperative if we're to remain light and nimble without the burden of technical debt.

If we're not in a greenfield, entirely new development space, that is, we have some legacy code, we can also employ tools to tell us where cyclomatic complexity exists. Cyclomatic complexity indicates which code is most used and will, therefore, help us target areas for refactoring which will be most fruitful.

However, the greatest contribution we can make to preventing bugs is to write all code from that point onwards with the understanding that we have to write clean, simple, and readable code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.228.246