by J. David Giese on November 30, 2015
People almost never see the underlying code that drives the software they use. Their interactions with the software are limited to the text, graphics, and controls that make up the user interface, or the UI, of the software. Because people’s experiences are limited to the UI, it is common for people to over-emphasize its role when commissioning custom software development.
We have seen this over-emphasis on the UI lead to a variety of problems and misunderstandings with our clients, usually centered on two issues:
The first misunderstanding can lead to mis-matched expectations concerning the size of a project or the amount of progress made towards its completion. The second misunderstanding is more subtle, but can lead to large and expensive problems that often only appear towards the end of a software project. The main reason for this misunderstanding is:
The UI is visible and tangible, and it is possible for non-technical people to gage whether it is built well. Poorly written code, i.e. bad code quality, is usually hidden from non-technical people. Furthermore, a high quality UI does not necessarily mean the underlying code is written well.
Poor code quality can be an extremely expensive problem to fix. This article describes what code quality is, why its important, and how to handle issues related to it. We hope this article will help managers, business-owners, or entrepreneurs who are interested in building custom software avoid some common problems related to code quality.
Poor code quality doesn’t usually become apparent until one of the following occurs:
Most of these issues doesn’t arise until the project is far along, and a large amount of poorly written code has accumulated.
It is often cheaper to completely discard poorly written code than it is to try and improve it.
A significant number of the projects we have worked on are re-writes of existing software that had exceptionally bad code quality.
Not everyone agrees on how best to structure a program, and there is a large spectrum between terribly written code and high-quality code. However, there are many agreed-upon coding best practices that, when followed, will improve the software that uses them.
Steve McConnell in Code Complete (a well-known if slightly outdated programming book) said:
Managing complexity is the most important technical topic in software development.
We completely agree. Even the smallest projects, which may appear simple on the surface, involve many different systems that must work together in complicated ways. Software is extremely complicated and can stretch the limits of what the human mind has the capacity to keep track of at once. Most programming best-practices stem back to managing complexity well, and answer the question: how do we keep the code as simple as possible, while performing the task that it needs to perform?
In the next few sections, we discuss some specific aspects of code quality.
When a piece of software is small, manually verifying its functionality is feasible. As it grows in size and complexity, the number of verification steps quickly becomes too large to perform consistently. As a result, developers will often skip verification steps after making changes to the code, and often times this is when new bugs creep in. During the initial development, these bugs are less common, however once the software is being used, it becomes a much larger problem.
Fortunately, there are many ways to automate the testing process, so that computers will test a variety of aspects of the software to ensure it is working properly.
We believe that automated testing is a critical component of quality code, and it is often the single biggest technique that can help reduce the number of bugs in a software system.
Automated tests take time to write, and when the software is updated, the tests must be updated as well. For this reason, tests add additional up-front work when building software. Unless the project is very small (or there are other good reasons, as discussed further along in the article), this extra up-front cost will be paid back several times over during the lifetime of the code.
Another advantage of automated tests, besides providing quality assurance, is that it acts as implicit, up-to-date documentation about how the software is supposed to function. Automated tests also make it easier for new developers to work on the code, because they will be warned by the automated tests if they accidentally break something (the tests act as a safety net).
Unfortunately, because tests don’t directly contribute functionality to the software, it is easy to skimp on writing them (or even skip them completely). The problem is further exacerbated because the issues caused by a lack of tests aren’t visible for a while. Agencies under tight deadlines (or who have over-promised results) will often use this technique.
For all but the most short-lived software projects, the code being written for it will be read many times more than it is written.
High-quality code is written so that it is as easy as possible for other developers to read it.
Difficult to read code is problematic for a lot of reasons. Identifying and correcting bugs takes longer, adding new functionality is more difficult and error-prone, and new developers will take much longer getting started.
There are many factors that affect codes readability.
the software is upgraded, and some old features are no longer used, but the code is never removed)
All of these factors occur naturally over time. For example, a button in the UI that was named the
blueButton in the code has its color changed from blue to red, but the name isn’t updated in the code. Another common example is: a client requests specific functionality for one employee using the software, so the developer goes in and hard-codes the employee’s id number in several places to make this happen. Now, a year from now, nobody will know what this magical number means or what its purpose is.
Poorly written code often has a lot of duplication.
Imagine your web application sends emails from fifteen different places in the code. The sloppy way to handle this is to copy and paste the code that sends emails to each location, and then make little modifications where necessary.
This code duplication is bad.
What happens if you change email servers, or want to add analytics to see how many people open the emails? Now, the code must be updated in fifteen locations! It is all too easy, especially for a developer that is new to the project, to forget a spot. Whats worse, the place they forget will likely be in a rarely used feature (because if its not it will be identified quickly), and the newly introduced bug won’t show its head for several weeks.
High quality code avoids duplication by combining shared functionality into reusable pieces of code that can invoked whenever it is needed. This makes the code easier to read, and repairs or extensions only need to be applied in one place.
In programming lingo, an “Exception” is a rare-event that should be handled differently in the code.
For example, imagine you commission software that must send emails when a rare business events occur. The software works well for over a year, then you stop receiving emails completely. After a few months of missing emails, you realize that the custom software you had built is failing silently! It turns out that your IT department changed a setting on the firewall a few months back, and the custom software has been failing because of this without warning.
The custom software probably should handle this “exceptional” situation gracefully—perhaps by waiting fifteen minutes and trying to send the email again. Then, if it fails a second time, it should display a message to all the administrators.
There are many types of exceptional situations that can occur during software use. Many of them never occur during development, because the software is only being used by a single developer. Examples include:
Poor quality code often does not handle exceptional situations well.
That being said, it is usually unnecessary to handle every exceptional case that can occurs in an application. Ideally, the trade offs involved and the likelihood of the various exceptions occurring will be discussed up front, instead of being revealed by strange and hard to find bugs after the software has been released.
Writing high-quality code takes more time up front than poorly written code. Sometimes, it can make business sense to “get something out the door”. This is often the case for early stage startups; getting a buggy, error-prone version of software into the hands of early users can be more valuable than having high-quality code, and then realizing that nobody wants to use your software anyway.
The accumulation of poor quality code is often referred to as technical debt. Technical debt is similar to taking out a loan to get the software up and running quickly, but just as with a real loan, you will pay interest in the form of more expensive maintenance and future development. The more bad code you have, the more interest you will have to pay. Eventually, you can build up so much technical debt that it is better to abandon the software and rebuild a new version from scratch.
Sometimes it is also possible to “pay down the technical debt” by investing time cleaning up the code. This process, often referred to as refactoring, involves making changes to the code to make it easier to read and work with, without actually changing the core functionality.
Technical debt isn’t a bad thing in-and-of-itself! It can be a viable strategy for a startup to bootstrap itself on technical debt. However, if you own or work for a more established company it will likely make more sense to avoid accumulating technical debt, and have the software written properly from the beginning.
It can be very difficult for a non-technical person to monitor the code quality of a project.
Imagine that you are having your house custom built. You walk into the half-finished house and look around. Maybe you can identify some suspicious construction, however you will naturally be very unsure of yourself. If the general manager assures you this is normal, where do you go from there? Also, what about deeper problems that you may not be able to identify?
Here are a few strategies that you can use to help ensure you have the level of code quality you need for your project:
Software is complicated. Although the user interface is a very important and visible component of software, there is a lot going on under the surface that is less visible. Even though it is hidden from sight, it is important that the level of code quality throughout the project meets your business needs, as poor code quality can be an extremely expensive problem to fix.
Hopefully this article helped you understand some of the common issues related to code quality, and provided some advice on how to ensure you are receiving the level of code quality you expected.
We send out tips about once a month.
Articles about DICOM, AI, image processing, medical regulations, and other topics of interest to professionals in the medical imaging software industry.
You may view previous articles here.
The Innolitics team, and experts we collaborate with, write all of our articles.