How Continuous Integration Improves Medical Device Firmware

Resources

How Continuous Integration Improves Medical Device Firmware

Authors: Levi Puckett

Originally a practice at only the very largest software houses — like Google and Amazon — continuous integration (CI) has worked its way into most major projects these days. It has become a standard practice in the tech world everywhere other than small firmware teams.

Firmware is different because the tooling is usually bespoke for each project, and for a team with just one or two developers, downloading the binary and exercising the hardware is usually felt to be enough of a confidence test during rapid development cycles. The purpose of this blog is to persuade you that approaching CI as a small-team medical device developer is worth your while.

Writing firmware for medical devices complicates the development process. It’s not okay to take a fast-and-loose approach to development, because at the end of development, you’ll have to rigorously prove that the firmware does what it is supposed to do. CI will help medical device software developers show regulators that their code is of high quality, providing metrics in several dimensions to support that claim.

What is Continuous Integration?

In theory, continuous integration centers on the idea of frequently integrating new software features into the main software base — often daily. Many different people working on many different features means tying it all together can be challenging to manage.

In practice, CI is a set of tools that are run automatically to build and analyze your software in various ways, catching new errors early and keeping the overall code quality high. Even if you only leverage a few ideas from CI, you’ll notice yourself becoming more confident in the firmware you’re developing and delivering better firmware sooner.

A CI server is the remote machine responsible for executing the CI actions. Typically, these include items such as:

  • Watch your code repository for changes, then check out new commits
  • Build the software using any relevant build configurations
  • Run one or more over the software
  • Build and run tests

If all these steps succeed, then the CI server will archive build artifacts (binaries, test results, static analysis reports) somewhere to be retrieved by you or a test engineer for further inspection.

Getting Started with Continuous Integration

If you want to get just a taste of CI’s benefits, start with the automated build step. Building each commit using all the relevant build configurations reassures developers that they aren’t breaking one configuration by changing another. It also removes the error prone and time-consuming manual build process from your developer’s workflow.

Recently, I was involved with a project that was exploring alternative hardware configurations. Each of these involved compiling different drivers for the hardware. Having the CI server build all these configurations made it a breeze to swap in different firmware to test our alternative hardware.

Once your static analysis tools are configured in CI, every commit will be scanned, and problems will be brought to your attention sooner rather than later. The CI server will also build and run all your unit tests (which you are writing, right?), confirming that the changes introduced as the program grows haven’t broken the foundation that you’ve built. You’ll also see when new units are missing tests. Could this be tested with a unit test on the host PC? If so, there’s no reason not to write the test and have CI record that your module works the way you think it should.

Why Use Continuous Integration for Small Teams?

The principle benefit I’ve found from using some of these CI approaches on small projects is that my confidence in my own software goes up. I can clearly trace out changes and understand that everything I had working and tested still works. I know that the linters are happy with the code, and I’m not introducing any spectacularly unstable code into the code base. I know from running tests off the side of my desk that the changes work, but I’m also more certain that they integrate nicely into the existing program. This confidence in my software base allows me to move faster through development cycles and pass working builds over to a tester sooner.

Setting up CI removes the developers from being a bottle neck for downstream tasks. It allows testers, electrical engineers, and others to fetch a binary from the CI server. If all they want to do is test new features or use a binary to exercise some hardware tests, they don’t have to set up the build environment and figure out how to build their configuration from source.

CI builds are reproducible, and the instructions for building the software are stored right alongside the code in the repository. Usually, the build will happen inside of a container such as Docker. The Docker file will also be stored in the repository and dictates exactly which version of the compiler, linter, and other tools to use. This makes aligning the team on the build environment easier. The CI server will act as the single source of truth for the build process: if the CI pipeline fails, then the build is broken.

Finally, CI for small teams encourages positive behaviours by developers. Things that might have been dropped in a fast-paced environment lacking CI will be respected with it in place. Running two or more static analysis programs over new firmware might be skipped in a rush, but the CI server will do it for every build. Developers will be aware of that. Even when it’s only me on a project, I find myself writing better code knowing that the CI server will run all its stages for each commit. This encourages better programming practice from the start, side-stepping common issues for small software teams such as missing tests and unhappy linters.

There is a non-zero cost associated with setting up CI, especially if the project is legacy code, lacking unit tests and using the integrated development environment (IDE)’s opaque build configurations. It is not the right fit for every small firmware project. CI will not help you write your business logic any better – instead, it guides you into writing cleaner code through linting and testing, which makes debugging issues in your business logic easier.

CI can support a successful medical device software development strategy, so if you are just starting your next project, consider standing up a limited CI workflow to automate building, linting, and testing. Your code quality will improve as your CI system keeps you honest, and you can focus on the more interesting medical device problems that need solving.

Levi Puckett is a Software Engineer at Starfish Medical. With a degree in Electrical Engineering, he bridges the gap between electronics and high-quality embedded software. Levi works at all levels of medical device software, helping companies develop effective, innovative, and safe software for their new medical devices.