Introduction
Software quality is a term that gets thrown around a lot in different conversations. Everyone seems to want it, but what does it really mean? In this article we'll describe what it means to us, why we think it is important, and what we do to achieve it.
Definition
Our definition of software quality is based on four core attributes. We want the systems we build to be correct, approachable, malleable, and predictable.
- Correct: The software should behave as intended for all of its use-cases, without obvious defects or bugs.
- Approachable: Developers should be able quickly understand what is going on and how to contribute without feeling fear or anxiety.
- Predictable: There are no inconsistencies in the system, limitations should be known and documented, and we can predict how the system will behave under specific conditions.
- Malleable: Modifying the system due to changing requirements — or unexpected faults — should be a smooth experience.
We use the acronym CAMP to make it easier to remember.
Traits of quality
These are some of the traits we look for in a software project:
- The code is well-structured and optimized for readability and modifiability (e.g. low coupling and high cohesion, proper source code file organization, code adheres to style guides, descriptive variable and module names, untangled complexities).
- Comprehensive tests that prevent errors, verify functionality, act as a complement to documentation, and give developers the confidence to make modifications.
- All available environments (e.g. local, testing, production, and staging) and corresponding release mechanisms are properly documented and easy to use.
- Everything from tests to code formatting to deployment is automated and leaves very little room for omission or manual error.
- The right amount of documentation (e.g. APIs, information on getting started, clues as to why decisions have been made, limitations, available environments, architecture).
- Dependencies are clearly specified including versions.
- The overall architecture is well-thought-out.
- The system works in an efficient manner.
- Security wasn't an afterthought.
Why it's important
Building quality software reduces long-term costs, improves developer efficiency, builds confidence, and has a direct impact on reliability.
Quality is also responsible for one of the most important positive feedback loops in any software related organization:
- Building quality software boosts morale.
- Teams with high morale build better software.
When it’s important
As with almost everything we do, it comes back to timing and balance. While, at the end of the day, our job is to deliver features, we must also consider the long-term implications of our actions and decisions.
We do not build ivory towers. Instead, we ensure that the project evolves in an sustainable and efficient manner. Technical debt doesn’t have to be addressed immediately, especially if it doesn’t have a direct impact on current goals. It is sometimes okay, or even desirable, for code to be repeated in different parts of the project. Timing and balance.
Our approach to achieving quality
We approach software quality with the mindset that it is largely systemic and the result of good practices and a sensible culture:
- Code that has been scrutinized by a second set of eyes is almost without exception higher quality. Make code reviews or pair programming a part of your routine.
- Make sure the code base has sufficient test coverage and that the code has been written in a way that makes it easy to test. A test-first approach puts you on the right path.
- Automate everything that can be automated. Linting, testing, type checking, image building, deployments, backup and restoration, API documentation generation, and so on. Automated flows reduce the risk of manual error.
- While you may know the reason for every decision, have a good understanding of all the little peculiarities in the code, and can build the software on a plethora of different architectures, odds are that the next person joining the team will not. Write documentation.
- Security isn’t an afterthought. Make it part of the process. Think about potential security threats and vulnerabilities from the beginning, assess risks, adhere to established security practices, minimize the attack surfaces, use secure defaults, and regularly update and patch the system.
- Be proactive when designing solutions. Being agile doesn’t mean that you’re not spending time on the architecture, doing wire frames, or discussing solutions with your team before you start coding.
- Standards and style guides should be enforced during development. Not adhering to a standard should never have to be brought up during a code review.
- There’s always a sense of urgency in our projects, but that doesn’t mean that there’s no time to take care of the code properly. Make sure to plan for refactoring, performance profiling, writing documentation, commenting code, and having discussions about tech debt and general improvements.
- Team members must feel secure and confident in expressing their opinions and taking risks without fear of embarrassment, rejection, or punishment.
- If (when) something goes wrong, it is not the fault of the team. Or any one individual. It is a shortcoming of the process. We fix the problem, update the process so that it does not happen again, and move on.
- Implement robust error handling and logging to ensure system stability and to facilitate troubleshooting.
- Make sure the requirements are clearly communicated and understood by the relevant parties in the team.
- Everyone on the team should have a shared understanding of how version control is used in the project.
- Regularly update the skills and knowledge of the team to stay current with best practices and technologies.