Building custom laboratory software from the ground up requires robust planning and meticulous attention to detail. In the process of doing this for our clients and ourselves on our forthcoming product, we have developed our own software development best practices to ensure the implementation meets all the requirements. We’ve found this to be a magic mix — user and regulatory requirements built within sustainable business practices.
When building software, we use all the most common practices, such as version control (we prefer Git like the rest of the planet), and have talked before about the importance of software testing and continuous integration/continuous deployment (CI/CD). Here’s a look at some of the processes we’ve invested in that enable us to provide high-quality software while using a development environment that has numerous advantages for developers.
Collaborating to develop software requirements
As a software engineering-led company, we believe the computer science principle “garbage in, garbage out” applies directly to laboratory software requirements. Say, for example, little effort and understanding are put into translating lab requirements into development tasks. The resulting software will not be of good quality because it will not meet end-user needs.
For this reason, we spend a lot of time upfront with our lab clients — listening, establishing common terminology, and ensuring we fully comprehend their needs before we start building. We use our own business analysts and field application scientists, who have experience in both the laboratory field and software, to help translate these requirements into development tasks.
If you’d like to learn more about our unique approach, read this article in The Journal of Molecular Diagnostics. In it, Eban Tomlinson and his co-authors describe how we successfully collaborated with a molecular pathology lab to build a custom software solution.
Working on branches off the main line
As noted above, we keep our code repository in Git. Within the repository, we maintain what’s known as the main line. This contains the latest (tested) code for the project.
When our developers work on new features or updates to existing features, they make a branch of the code so that they can work on that task in isolation. We test all code thoroughly before merging it so that we don’t inadvertently introduce errors and can maintain the integrity of the main line.
Testing changes before they are merged
When a developer has completed their changes on a branch, they make a pull request. This signals our infrastructure to deploy a temporary virtual environment where the changes can be tested in isolation. Once the changes have been successfully tested, the infrastructure discards the temporary environment and the branch is merged with the main line.
Using IaC ensures that the creation of test environments is repeatable, isolated (for easy debugging), and scalable, enabling us to test branches of code without concern for configuration drift or missing dependencies.
A main benefit of this practice for our developers is that it significantly shortens the time between a code change and merging into the main line. Traditionally, when a pull request is made, the developer may need to wait until the code is merged to the main line, built, and deployed before full testing can be completed.
As you can imagine, this process can sometimes take days. If a revision request comes back to the original developer after three days, it can be challenging for the developer to revisit and load all the mental context required to effectively revise the code. Using IaC, a developer can test code changes immediately in a production-like environment and fix anything necessary without losing their train of thought.
Using a framework to ensure changes are auditable
We also leverage the GitOps framework (which combines IaC with merge requests and CI/CD) to facilitate repeatable, auditable change. Using this framework, each software environment (Development, Test, Staging, and Production) is independent of each other, ensuring data and executional isolation. This means that any bad code doesn’t affect anyone else using the main line.
In a regulated lab environment, traceability and auditability of data are vitally important, as we have mentioned previously. Once a software product has been implemented in a lab (put into production), the lab’s compliance and regulatory requirements come into play. That’s why we build software using tools that facilitate auditability in both the software itself and the final implementation.
Benefits of these best practices
These best practices let us build software in such a way that data in a lab implementation is traceable and auditable, fixes can be made easily when something’s wrong — we’re able to refactor code without incurring significant risk — and testing can be automated. Just as important, these practices eliminate a lot of drudge work for our developers and make their processes as fluid and seamless as possible.