It’s never too late to pick up some healthy Software Craftsmanship habits, but sometimes we might need a little push to do so.
It is not too uncommon to hear of IT companies still not relying on consolidated techniques such as TDD, Unit Testing, and maintaining high Code Quality standards.
Understanding the benefits of adopting a technique is crucial to make us feel more comfortable and at ease when practicing it, so let’s get right into it.
Unit Testing, And Why It Matters
Unit Testing is a software-level testing technique that aims at testing the smallest, logically isolated component of an application.
Unit Testing applied systematically and thoroughly can bring several advantages to a project and significantly improve the development experience:
- Gets rid of the fear of refactoring our code;
- Can act as self-verifying documentation for the project;
- Reduces the required amount of manual testing;
- Provides fast feedback about the code’s behavior circumventing deploy time;
- Improves design by revealing architectural flaws when the code turns out to be hard to test;
- Inherently increases code quality by forcing developers to put more focus on their code;
- Promotes Inversion of Control/Dependency Injection as these make it easier to mock external dependencies and develop true unit tests;
- Anticipates the discovery time for bugs from the production or black box testing phase, to the development phase;
- Makes debugging easier as you can exercise any piece of code by running its matching unit test;
- Helps to establish a sense of discipline and rigor when building software.
Unit Testing Fallacies
Be very careful about the following common unit testing fallacies:
- Over-testing: exercising multiple times the same code via different unit tests will unavoidably happen, don’t worry too much about it but at the same time make sure you’re not being too zealous and focused on insignificant details when writing unit tests.
- Coverage-slavery: don’t become a code coverage slave! Focus on meaningful unit tests rather than just testing for coverage. Also, 100% coverage is not needed.
- Sunk cost fallacy (for tests): don’t be afraid to throw out tests when they don’t make sense anymore or if you later find out they were badly written in the first place.
- Sunk cost fallacy (for code): don’t be afraid to throw out your code, as long as you keep your tests you will have an easy way to write it again from scratch and verify that it works as expected.
On a final note, do not be tricked into thinking that unit testing will raise the cost or amount of work you have to deal with when developing software: it is exactly the opposite.
The cost of unit testing will be outweighed by the overall reduction of costs resulting from: the early discovery of bugs during the development process; the refactoring sessions you would have otherwise not felt comfortable doing without proper unit testing in place, which also improved the maintainability of your code making it easier to overcome future requirement changes; reduced training time for newcomers, which can use unit tests as a way to approach the project without causing harm or feeling overly stressed.
The trade-offs of TDD
Test-Driven Development is a software development process relying on unit tests as a starting point to set off multiple code refactoring cycles until all tests are passing, the code quality is acceptable, and the code is easily maintainable.
Many debates sparked over the years about the validity of TDD, to the point where quite a few articles can be found trying to answer the question regarding TDD being “dead or alive” nowadays.
As you can imagine, TDD is neither dead nor alive, but it certainly is not a practice to be blindly applied to any project. Let’s check out some pros & cons to understand if it’s worth bringing this practice into our Ways of Working.
- Is a way to achieve confidence over one’s code;
- Is particularly worth it when wanting to verify intermediate results between refactoring cycles;
- Enforces dependencies layering in order to easily mock interactions with external systems, which in turn also promotes loose coupling;
- Guarantees very accurate red/green test signals: making sure we always write a failing test before obtaining a green test signal gets rid of a test wrongfully passing just because it was badly written in the first place (NOTE: every test should fail at least once).
- Might not be easy to apply under pressure or when tackling hard tasks with unclear design and/or not particularly restrictive requirements;
- Doesn’t fit short-lived projects where maintainability is not a primary concern. You might end up over-complicating your architecture for the sake of TDD rather than for an actual need to write easily maintainable code and loosely coupled components;
- Inherently adds overhead to the development process.
Long story short, there are good reasons both to use and not use TDD for any given project. The harsh truth is that you most likely need to try TDD out, figure out how it works for you, and adapt it to your needs as seen fit.
The benefits of Code Quality
Software Quality can be classified into two main categories:
- External Quality: evaluated by the end-user through a “black-box” approach, without any insight on the source code of the software;
- Internal Quality (Code Quality): evaluated by the development team through a “white-box” approach.
External and internal quality are tightly linked to each other, therefore we can say that high levels of internal quality correlate with ease of providing satisfactory levels of external quality.
Maintaining high Code Quality standards is arguably easy nowadays since many available static code analysis tools provide real-time feedback regarding the quality of our code and tips on how to improve it.
The Seven Axes of Code Quality
Some of these static code analysis tools base their analysis upon the so-called “Seven Axes of Code Quality” (informally known as “The Seven Deadly Sins of Software Development”):
- Coding Standards: adopt common coding standards and best practices.
- Code Duplications: apply the DRY (Don’t Repeat Yourself) principle as much as possible.
- Bugs/Potential bugs: identify and refactor code that has a high chance of being badly written.
- Test coverage: unit test your software to an acceptable extent.
- Cognitive Complexity: eliminate unnecessary complexity and equally distribute complexity among your application components.
- Architecture/Design: make sure components don’t have too many dependencies.
- Comments: don’t overdo it with the comments, focus on the code, and remove any and all commented lines of code.
10 Benefits of Keeping High Code Quality Standards
Caring about your codebase and keeping a good level of code quality:
- Makes code easier to maintain and adapt to changes;
- Makes it easier to prevent oversights (eg. bugs);
- Makes code easier to understand;
- Enforces the DRY principle;
- Makes it easy to have a standardized approach, even when multiple developers contribute to the same source code;
- Promotes code refactoring in order to lower cognitive complexity;
- Enforces an adequate amount of code testing;
- Highlights potential performance issues;
- Promotes a clean design and architecture;
- Teaches developers about the best practices of the language.
Unit Testing, TDD, and Code Quality are not new concepts by any means, but they’re not fully integrated within the Ways of Working of all IT companies, sometimes for good reason! It is worth knowing the benefits they provide to understand if it would prove beneficial to try picking them up or they’d end up just adding unnecessary overhead to our development process.
If you liked what you just read, please remember to follow me on Medium and Twitter for more articles like this. I’d really appreciate it ;-)