Software moved from the desktop to just about everything we touch. From smart thermostats, to infusion pumps, to cars, software is pervasive and growing. The so-called “things” from the Internet of Things (IoT) increasingly carry more logic. With it comes a larger risk of failure. Many of these devices are used in safety-critical areas such as medical and automotive arenas where they have a potential for bodily harm.
But, there is hope. Software CAN and MUST be treated as an engineering practice. Coding standards, which are part and parcel of good software engineering practice, move us from the “build, fail, fix” cycle to a “design, build, deliver” cycle with high quality, safety, and security.
As it turns out, these same standards also provide benefits in the areas of cybersecurity, doing double duty.
Software Development to Software Engineering
Software’s impact on the real world is often discounted. One of my main themes that I discuss continuously as an evangelist at Parasoft, is that software development really should be engineering.
We frequently call software developers by the title, “software engineers,” but that’s not necessarily the proper term for how they’re working today. Evolving to a good software engineering practice results in costs going down and quality going up. A key part of this is adopting standards—in particular, coding standards.
The age of the connected car, IoT, and perpetually-connected devices is here. Software is creeping into products, devices, and other places we never thought of. We now must think hard about the software in these products and the ramifications of it.
The Cost of Poor Quality
Building good software is different from building something like a car. If I’m trying to build a high-quality car, I must spend more on materials and more time to build it. It turns out that in software, you don’t spend more to build high-quality software. You spend more to build poor quality software.
We must understand that in software, most of the defects come from the programmer, who puts them in the product. If we can stop introducing defects as we develop the software, we can have much better software, at a lower price.
It’s important to realize how quality impacts software development costs. Capers Jones, a researcher, has been following this for decades and performs a survey every year on software costs. The numbers don’t change much year to year. The data shows that the typical cost of software from requirements to coding to maintenance goes up with each phase.
Fixing defects post-release costs about $16,000 (possibly much more) based on research into real companies doing real software, not a theoretical model. If we look at late cycle quality and security efforts such as penetration testing, the security issues found here are at the expensive end of the cycle. It’s probably 15 times as expensive as finding vulnerabilities through testing versus early security audits.
An outdated and provably false approach is to improve the quality of your software by testing it at the end of the lifecycle, just before release. In the manufacturing world they understand, but for some reason we think we can test quality (and security) “into” software.
The reasons why I say that software development is almost never engineering are these common characteristics of current software development:
- What most developers are doing isn’t repeatable. If I give the same task to two different people, the results won’t be the same.
- Lack of well-exercised best practices. Software developers look at coding standards as a word that has little meaning. Standards are thought of as a set of rules that the team lead insists that are followed, rather than understanding that the coding standard is the embodiment of knowledge, practice, and experience. An electrical engineer knows that standards are the way to build a safe product the first time. A software developer thinks that standards are a constraint that is slowing them down… a “false positive”.
- Developer training is unknown and inconsistent. Software development education isn’t standardized like it is with engineering disciplines. Standards and established practices are often not part of the curriculum, rather the emphasis is on programming languages.
Coding Standards Improve Safety and Security
The goal of software coding standards is to instill proven programming practices that lead to safe, reliable, testable, and maintainable code. Typically, this means avoiding known unsafe coding practices, or code that can cause unpredictable behavior. This becomes critical in programming languages like C and C++ where the potential to write insecure or unsafe code is high.
However, I think the industry has lost its way with these programming standards. In the last decade, the tools (such as static analysis tools) have shifted from detecting potentially problematic code that’s unsafe or a known language weakness into a focus on looking for defects as a form of early testing, also known as shifting left.
Although looking for defects is important, building sound software is a more productive activity. What we should be doing is building and enforcing standards to avoid the situation where defects are introduced in the first place—shifting even farther left.
To back this up, consider the research done by the Software Engineering Institute (SEI). SEI found, unsurprisingly, that security and reliability go hand-in-hand and that security of software can be predicted by the number and types of quality defects found. In addition, critical defects are often coding mistakes that can be prevented through inspections and tools such as static analysis.
Role of Static Analysis
Research shows that inadequate defect removal is the main cause of poor-quality software. Programmers are about 35 percent efficient in finding defects in their own software. Later in the development cycle, the most defects we can hope to remove after all the design reviews, peer reviews, unit tests, and functional tests, is about 75 percent.
Static analysis, when used properly in a preventative mode, can increase defect removal to about 85 percent. However, the focus of static analysis usage for most organizations today is on detection and the quick fix.
Further benefits are possible when static analysis tools are used to prevent known poor programming practices and language features in the first place. This is where coding standards come into play as guidelines and programming language subsets that prevent common defects like buffer overflows or missing initialization from being written into code.
An Ounce of Prevention
Consider an example where during testing a rather complex buffer overflow error is detected, perhaps with a dynamic application security tool (DAST). This is a stroke of luck since your testing just happened to execute the code path that contains the error. Once detected and debugged, it needs to be retested. Static analysis, using flow analysis, might have also found this error, but it depends on the complexity of the application.
Runtime error detection is precise, but it only checks the lines of code that you execute. So, it’s only as good as your test code coverage. Consider if a coding standard had prohibited the code that enabled this error in the first place.
Coding standards like MISRA C don’t describe how to detect uninitialized memory, for example, but rather guide programmers to write code that won’t lead to such an error in the first place.
Take for example, civil engineering and building bridges. We wouldn’t take the approach of building a bridge and then test it by driving bigger and bigger trucks over it until it collapsed, measure the weight of last successful truck, and build it again to withstand that new weight. This approach would be foolish, yet it’s not unlike the way we approach software development.
Once a software team adopts a coding standard and static analysis is applied properly, they can detect errors early and prevent them. In other words, instead of finding a defect early, which is good, the team is changing the way they write code, which is better!