What does it mean that embedded software is good? From the product owner’s perspective, it usually means that it is optimally adjusted to the client’s needs, as bug-free as possible, and delivered on time. But is this a proper approach?
Accordance with the client’s needs, laid out as functional and non-functional requirements, is arguably the most important characterization of any software. Both the product owner and developer want the system to include all the specified functionalities and operate to the best of its abilities – without lags nor security issues.
Software reliability is another feature usually listed as “a must” in the context of good software. The fewer bugs it has, the more efficient it is. Program errors are a significant problem in software development, as their reproduction and correction consume a lot of time, thus impacting the budget and the general image of the product. Nonetheless, minimizing the number of errors is only one piece of the whole “code quality puzzle,” and not necessarily the most important one at that.
Finally, the product should be delivered on time. If it is not, there is a risk of a competing solution entering the market or the client losing interest in further cooperation, in which case, all our effort is lost. This is why it is so important to estimate the project time precisely even before starting the project, which means gathering as much data as possible during the preliminary talks and analyzing it thoroughly. Then it is just a matter of meeting the deadlines.
Balancing these three elements out is in theory the golden mean to creating good software, but as simple as it sounds, it is not an easy task. Here, I would like to tell you about some things you can do to succeed.
Before you start creating software, it should be defined what software quality and functionalities are expected by the client or the project’s business owner. Only then can you assess potential risks and resources, such as the workload, the team size, and the time required.
Sometimes, the proposed project duration does not meet the client’s expectations, while the budget does not allow for team expansion. In such a case, the only thing you can do is to decide if sacrificing certain functionalities will allow you to fulfill the remaining requirements. The road to a compromise here is certainly a bumpy one, but such estimates are necessary if the project is to be successful.
Another thing is that product and project risks need to be defined, but also periodically analyzed throughout the project. Minimizing the risks means a bigger chance of delivering the software without delays.
If within the initial stages of the project, you analyze which functionalities are crucial and which are secondary, you can optimize the production time. It will allow you to save some time if necessary, or on the contrary, it might indicate the need for postponing the deadline if you prioritize the proper quality level of the application, or if your client is not willing to sacrifice any more functionalities.
Work on software should begin with creating a precise and detailed characteristic of its functionalities. Only then will it be possible to optimize its development. The basis for a good description of a system should be the selection of functional and non-functional requirements. Following the requirements, the software architecture should be defined, as the next step. If these two initial stages are omitted or not treated with due diligence, the consequences for the project can be devastating.
Why is that? Simply, because requirements and architecture are that important in software development. Unclear and incomplete information will give developers and testers room for interpretation and interpretation may lead to good as well as bad decisions, and most often results in deviating from the system’s intended shape.
If the description lacks non-functional requirements it may lead to inconsistencies in many areas, such as usability, reliability, efficiency, accessibility, support, scalability, implementation methods, or security. Therefore, having some requirements is still better than not having them at all.
To check if all pre-defined functionalities have been implemented and work as intended it is crucial to perform functional and non-functional tests, as part of the high-level system and clearance testing. Functional tests verify if all the required elements are present and if they operate properly, also from the usability standpoint. Non-functional tests, on the other hand, allow verification of time relations, resource usage, integrity, and security.
This leads to one more requirement one should never forget – software should be testable! Nobody wants to spend more time on software testing than development itself. That is why the stages of software architecture and system design should take into account the mechanisms making tests possible and easy to perform. With the mechanism for checking software functionality in place, we can focus on reducing the number of defects of other types.
To find all the other faults it will be most helpful to utilize mid and low-level tests, especially those of the best price-to-quality factor. These are static code analysis, assertion testing, and code review.
Static analysis allows us to meet the coding standards assumed for the project. Additionally, it makes it possible to supervise all logic rules, track data flow, and find dead code. Thus, it improves the modularity, integrity, and reusability of the software.
Nonetheless, static analysis is not enough and should be used along with assertion and code exception handling. This way the software will be more stable, more accessible, and less sensitive to errors.
In more complex projects, where multiple developers create one application, a code review is worth implementing. It should be focused on the architecture and how the system operates, instead of the syntax and naming conventions, which can be checked much more efficiently by machines. This approach allows you to control the correctness and completeness of the code implementation, in comparison to the architecture and requirements.
The most complex or demanding projects benefit most from the dynamic analysis, which means unit tests or the Test Driven Development approach. In the TDD unit tests are created first and the code is written later. At further stages, they are created by turns with expanding the given functionalities. Although this solution complements the software testing process perfectly, it is more expensive than the methods mentioned previously. As it is utilized within the initial project stages, it is necessary to account for the time necessary for writing and maintaining tests, as well as use developers experienced in this methodology. Still, it increases modularity, reusability, testability, and maturity of the application, while reducing the number of errors detected in high-level testing.
The next stage of mid-level software testing involves integration tests of software-software and software-hardware types. In smaller projects, it is usually omitted or combined with functional or developer tests during implementation. In the bigger projects, though, it is treated as a separate stage to provide additional control over the system’s accessibility, resilience to errors, cooperation, and co-existence with other applications at the same platform.
Even a basic analysis of the graphs shows that choosing different test types for a project results in differences in code quality. Because the number of bugs in software is just one of its quality metrics, testing is necessary for much more than debugging and functional verification, but to increase the overall quality, which is clearly a more complex issue. So, now we can update the previously defined characteristics of good software.
What will make your embedded software good are several things. To begin with, scrupulously and clearly defined requirements of the system and the software. Secondly, a cohesive software architecture laying out the most important aspects of the solutions used for the software, a prerequisite for development and testing stages.
Based on the application quality features the right levels, types, and numbers of tests need to be chosen. This is how metrics describing the quality of the developed code are created, providing a way of verifying if the software meets the proper standards.
Finally, we should never forget one thing – in every software development process, the most important piece of the puzzle is people! Without their engagement, even the best solutions fail. That is why putting together a well-adjusted team of developers and testers is half the battle.
We are a platinum ISTQB partner and we hire over 100 testers certified at the Advanced and Foundation levels. If you need good embedded software, drop us a line and check our page on embedded software and safety-critical systems.