Driving Towards Quality Software
As professionals we strive to develop high quality software. The question we must ask ourselves is “What qualities must software possess to be considered high quality?” The answer to this question fundamentally influences our software design and engineering practices. The design and engineering practices we adopt (or don’t adopt) provide the structure for us to succeed (or fail) at delivering quality software.
So, “What are these qualities?” Take a moment and answer for yourself. Think of the software that you consider high quality. Why is the quality high? Think about the software you develop. What are you happy about? What are your pain points?
Boiling down high quality software to a few succinct qualities is dangerous in that it can be misinterpreted. In addition, this topic walks the fine line of software engineering best practices…which can be a religious debate. However, it is important to narrow our focus, or reduce the clutter, so we can make decisions based upon what is important.
In my opinion, high quality software has the following three qualities: desirable, testable, and maintainable.
High Quality Software Is Desirable
Creating software that is desirable to the end user requires an understanding of the customer’s needs, their work flows, and their environment. This information can be captured into a set of requirements (user stories, use cases … etc). These requirements are then crafted into deliverable software. Eliciting good requirements is a difficult task.
Most of the time customers don’t know exactly what they want. They typically have a good idea that later evolves into something more concrete. Many times, this evolution occurs after they see a mock up, a prototype, or an early production version. This makes getting working software in front of customers often and early an important part of the process.
This can be scary to a development team. What if a customer changes their requirements? What if they don’t like what you have built? Try not to take this feedback personally. Requirements changes will happen. It is better to know as early as possible. In fact, having the customer participate in this evolution is really about getting customer buy-in. At the end of the project, the customer should not be surprised by the quality / features (or lack) of the software.
But what about schedule, costs, resources? Developing software is a business and reworks impact the schedule and costs. However, there is also a cost to delivering software that is not reworked. Involving the customer in the discussion is extremely important. Remember, it is about getting buy-in. Customers need to be aware of the cost of altering the requirements.
Informing the customer about the development process before beginning the project is extremely important. It provides the opportunity to discuss their role and how important clear, concise and prioritized requirements are to the software quality. It is also the time to discuss how / when they will have opportunities to review working software and the process / impact of modifying requirements.
Having desirable software does not mean that the software is feature complete. It does mean that completed features meet customer’s expectations and the customer is happy with the roadmap for future features. Having a backlog of prioritized features helps define that roadmap.
The ‘desirable’ quality drives customer communication. This is key to creating desirable software. Take any steps necessary to maximize this communication. As a result, the crafted software will represent the voice of the customer and the customer will be satisfied.
High Quality Software Is Testable
Testing is the process of exercising software for the purpose of discovering defects. Defects can be requirements that are not satisfied or they can be quality related (hard crashes, typos…etc). The earlier a defect is discovered (and fixed) the less it costs the business. Discovering a defect in deployed production code is the worst case scenario. Creating testable software is the process of adopting engineering and design practices that promote early defect detection.
Who is responsible for testing software? Many organizations have a quality group that performs final tests on software. However, the developers play an important role in detecting defects early. All team members have a responsibility to test the software to ensure it meets the highest quality standards.
At some level all software is testable. You can always ‘black box’ test software by firing it up and poking at it from the outside. This type of testing plays an important role in ensuring the software meets the customer’s acceptance criterion for each requirement. Black box testing is often time intensive and manual. Generally, black box testing is not done often enough to effectively discover defects early and often.
Adopting and embracing automated tests significantly increases the ability to detect defects early and often. Running automated tests as part of a continuous integration system is a common practice to ensure early detection. The automated tests should be fast to run. Usually, they are at the ‘unit test’ (testing methods on a class) level or maybe lower level functional tests (interaction between a two classes). There can be times where higher level automated functional tests replace some of the black box testing. Generally, higher level test take longer to execute. Keep your continuous integration environment free of these long running tests. Executing long running higher level tests at project mile-stones can be effective.
The biggest objection to embracing automated tests is the notion that it extends the development cycle and pushes out the release date. This is generally a short sighted view. The development cycle may be a bit extended. Especially when the team is just beginning to adopt automated tests. It is important to write tests for all non-trivial functions. Chasing 100% code coverage can be quite exhausting and a waste of time. Effectively choosing what to test will minimize the testing code that needs to be written.
The time savings payback of automated tests comes in two places. First, there be less time spent doing ‘black box’ testing. Quality assurance engineers who are aware of the automated tests, will be able to focus their efforts in other areas.
Optimally, continuous communication between the quality assurance engineers and the developers will maximize this effect.
Secondly, when software needs to be modified, automated tests will be key to ensure the changes don’t effect other parts of the system. Testable code roughly translates into software components that are loosely coupled. Loosely coupled software is less fragile and easier to modify.
Usually software is deployed in a staging or testing area. Another effective testing tactic is to create an automated test bench for the staging area. The automated test bench allows working software to be tested in an accelerated fashion. A little time spent creating an automated test bed environment can have big dividends.
The ‘testable’ quality drives the early detection of defects. A major by-product of this is the adoption of design / architecture choices that promote testable code.
High Quality Software Is Maintainable
Maintaining code involves repairing defects and adding feature enhancements. Most of us will spend more time maintaining code than creating code for new products. Since we generally will have to maintain the code we write, we should strive to write maintainable code. What makes code ‘maintainable’?
This is why creating software is a craft. How does one learn a craft? Practice, practice, and practice. Formal education can only go so far. The following are a few tried and true principles that can help create maintainable software.
The most important principle to keep code maintainable is to reduce complexity (the KISS principle). Avoid over-designing the software by trying to anticipate future needs. Liberally apply the YAGNI and DRY to keep your code clean and simple. Use OOP to create code that decoupled (this will be driven by the testable quality).
As architects and developers we generally face decisions on the best way to implement a feature. Most of the time, the problems we are facing have been solved before. Recurring problems are typically extracted into design patterns. It is our responsibility to be familiar with patterns for exactly this purpose. Patterns are there for us to lean on and apply known solutions to problems.
There are a number of ways to improve our ability to write maintainable code. The most effective way is any form of collaboration with other developers. This includes code reviews, design discussions, pair programming, blogging, and participating in OSS. Reading code is another great way to raise the level of your craft. Download the code for an interesting OSS project. In essence always be learning. Even the most experienced developers learn new things.
Summary
Each of the three qualities discussed (desirable, testable, maintainable) represents one side of the quality triangle. The area of the triangle represents the amount of quality. The highest quality software is represented by an equilateral triangle. If any one leg of the triangle is shortened, software quality diminishes.
There are a lot of dynamics in a software project. Establishing a commitment to delivering quality software ultimately requires you to answer the question, “What qualities must software possess to be considered high quality?”. Hopefully, my answer to this question is not too far off base. Please feel free to share your thoughts.