How can we measure the software quality? There are many perspectives on that issue of software development. Matthew Wilson has spotted some evaluations that we can follow to improve the quality of our code and bring us a wide perspective on what does matter when we are evaluating the software quality.
- Correctness:
Software correctness can be established using many techniques and tools, from static analysis, runtime checkers and formal verification. So we have enough tools to determine whether or not the software is correct. Also we can search for the proper algorithm, previously verified and tested to being used in our software.
- Robustness:
We must try to create small and efficient code, regarding correctness and documenting it, so can create hardened code based on common standards to allow reliable modifications while we are maintaining the code. Jon Postel‘s law says: "Be conservative in what you do; be liberal in what you accept from others". This also reduces the amount of bugs that you can find on your applications.
- Efficiency:
Efficient code has lower times and reduced cyclomatic complexity. For example we must prefer a construct that is O(1) instead of one that is O(n), if it is available, and we must prefer a construct that has a cyclomatic complexity of 78 rather than other with 129: "simplicity is divine".
- Portability:
The code must be portable. To create portable code we must simply omit the usage of language/platform specific features. There many standards that we can follow to create portable code and there also are compiler options to help us in that task. For example, GCC supports pedantic compiling flags to regard the strictness of some standards, try compiling your C code with: -Wall -Wextra -Wshadow -pedantic -std=c99, or your C++ code with: -Wall -Wextra -Wshadow -pedantic -std=c++98. Also there is a compiler with the portability issue in mind, the TENDRA compiler, it comes with a static analysis tool too.
- Expressiveness:
We must create legible code, this principle holds well known coding standards, naming conventions and similar scopes on how do we write the code, to let others read your code and understand it with a faster lookup to the documentation. Also documenting the code is a must. I’m sure that you can follow some standard on coding.
- Flexibility:
We must try to use abstraction in our code, instead of concrete system calls. For example we have dirent on UNIX and WIN32_FIND_DATA on Windows, both structures are completely different and most Win32 API calls aren’t based on any known standard, so we need to create or use some kind of abstraction to access the system calls, like the Façade design pattern, to allow us to omit direct calls and create more flexible code, and easy to maintain.
- Modularity:
It’s fully recommended that we must think on terms of OOP. For C programmers the book "Object-oriented programming with ANSI-C" possibly is a must read. Modularity bring us the robustness needed to operate on terms of cohesion, like any desired architecture, we want to implement cohesion instead of attachment, so we can create reusable code and modules.
- Discoverability:
legible and modular code bring us a good discoverability, so we can establish good basis on well known standard APIs, like POSIX, C++0x and many others. This regards the intuitive approach of building portable code, so we can guess about how is called the function or class that we need to use in our problem.
- Transparency:
In conjunction with modularity, transparency can bring us the proper cohesion between our software components. The proper abstraction layer between the application tiers allow us to create individual components that work together without attachment and with the proper cohesion. So the maintainability of the application becomes higher and easy.
And here comes some questions: How can we measure all those points?; How can we become more strict and bring more reliable applications?. We must follow some steps to reach optimal software development process. The first one is to define the application architecture, so we can handle common problems with the proper architectural patterns. Then we must fit those architectural patterns with the proper design patterns. One time defined the application architecture we must design our components following that architecture. If we are using Agile Methodologies, we need a well defined architecture too, so we can follow a common paradigm on how to solve the problem that the application is solving.
Using third party frameworks or third party libraries is not a bad practice, but we must be constantly following its roadmap, looking the direction that the library or framework is following, so we can handle properly an incoming and imminent update. How to handle that issue?, using abstractions and modularity, which also reduces the amount of bugs. Here some patterns plays an important role in our quality goals, for example the façade, factory and dependency inyection patterns would help us on that task.
conclusion
The more rigorous we are, we can get more reliable software. But sometimes we don’t have enough time or simply we don’t need to be too rigurous, for example we can not apply formal verification to enterprise applications, because usually those applications have millions of lines of code, instead possibly we can deliver just one module at time that has been verified formally. Instead we can use metrics analyzers, static analyzers and runtime checkers to bring us a better approach on how the code is working. You can learn code hardening techniques and debugging techniques to ensure your code too, which will bring you a better code if you get well taught on those topics.