When a customer comes to us with an idea, we start with an exploration. We then record the requirements in a System Requirement Specification (SRS). This document contains functional and technical requirements. This creates a concrete picture of what we are going to build and what it has to meet.
The SRS is the reference point throughout the entire process. Does something change along the way? Then we coordinate this with the customer and update the document. This is how we ensure that expectations and reality stay together.
Based on the SRS, we get to work in refinement sessions. During these sessions, our team translates the requirements into concrete ‘user stories’. Each story describes a piece of functionality. Together, they form the scope of the project. By thinking out and dividing everything in advance, we create an overview. We use tools like Atlassian Jira for this. This makes it clear to everyone what is to be built, by whom and when.
Nevertheless, we can only really perfect a project if we can make the right adjustments in between. That is why we work agile, so that we can switch flexibly during development.
Writing software is one thing. But to ensure quality, we use several elements. During implementation, we keep control using a fixed build environment (toolchain), packaged in docker containers. This way, everyone works with exactly the same tools and the results always remain reproducible – even years later when someone else is working on it. In addition, the implementation of code is always checked by another developer from the team via Pull Requests. This contributes to traceability.
For our CI/CD pipeline, we use Jenkins. It automatically checks every change that is pushed to Gitea. Think of checks for code style (linters), structure (SonarQube according to the MISRA C standard), documentation (Sphinx) and compilation. Jenkins only accepts the merge code when everything is correct.
Monitoring quality means continuously checking whether everything still works as intended. That is why testing is an integral part of the development process. We use three testing methods that complement and reinforce each other.
Unit tests (GTest) we use for logic that is independent of hardware. Think of functions that perform calculations or make internal decisions. We write these tests directly with the code, so that we can automatically check whether everything is still correct with every change. GTest is a globally used testing framework by Google and we run it fully automatically via Jenkins.
Unit tests on target (Unity) we deploy for code that depends on hardware, such as reading sensors or driving microcontrollers. This type of test runs the software on the actual hardware on which the product runs. Unity is a lightweight framework that helps us test these components reliably and repeatably as well.
Nightly builders perform a full set of functional and regression tests every night. These tests mimic realistic usage scenarios, including communication with servers or other systems. It is like a digital final check: does the product still meet the agreed functionality? Errors are immediately visible in our dashboards and via Slack notifications so that the team can react quickly.
This combination of testing gives us and our customers confidence. We detect errors early, see the impact of changes immediately and significantly reduce the likelihood of problems after delivery. This is how we ensure stable, maintainable software that does what it is supposed to do.
Interested in the possibilities of embedded systems for your organisation? We would be happy to meet with you to discuss these possibilities. Get in touch for an exploratory meeting.
We look forward to hearing from you!