Software development for medical devices is subject to specific regulatory requirements. IEC 62304 establishes requirements for the software lifecycle processes, including development, verification, and maintenance. It also classifies medical device software into Classes A, B, and C according to the potential harm that a software failure could cause to the patient. This classification influences the activities and documentation required throughout the software lifecycle.
.webp)
One of the requirements addressed by the standard is the decomposition of software into software items and software units. This is not merely a software design practice: how the software is decomposed also affects how it is verified and what evidence needs to be documented.
The standard defines a software unit as a “Software Item that is not subdivided into other items.” The definition itself is straightforward, but the real question is:
These are some of the first questions we face when deciding how to segregate the software. How should we divide it? What should be considered a software unit? How small is too small? And most importantly, how do we know if the boundaries we have chosen are actually appropriate? The standard leaves this decision to us. The real challenge is figuring out where those boundaries should be.
The consequences of poor segregation often become apparent once we start working with the resulting units. We may find ourselves with a function that handles hardware access, state-machine logic, and communication at the same time, or a module that requires an excessive number of mocks just to be tested in isolation, or a seemingly small function that actually depends on a large part of the application. As a result, tests can become tightly coupled to implementation details rather than focusing on the behavior the software is expected to provide.
These are not simply testing problems, they can be symptoms of how the software was originally structured. In other words, the way we structure the software can directly affect how easily it can be verified, and this is why software design and verification cannot be treated as completely separate activities. When designing the software architecture of a Class B or Class C medical device, we also need to consider how that architecture will eventually be verified. Software units should not be something we think about only when it is time to write the tests. Their boundaries should be considered from the beginning, while the architecture and responsibilities of the software are still being defined.
.webp)
How do we actually decide where to draw the boundaries of our software units?
There is no single correct answer, and the right level of granularity will depend on the architecture and the characteristics of each project. However, there are some practical criteria that can help guide the decision. A good starting point is to separate software according to functionality and responsibility; modules, services, hardware abstraction layers, and other components with a clearly defined purpose can provide natural boundaries for software units. Ideally, the functions within a unit should be focused on that responsibility rather than depending heavily on the implementation of other modules.
This leads to two important software design concepts: cohesion and coupling. Cohesion describes how closely related the responsibilities within a component are, while coupling describes the degree of interdependence between modules. In general, we want software units with high cohesion and low coupling. Reducing coupling can make a significant difference when it comes to unit testing because a unit that depends on many other modules, shared data, or internal implementation details can become difficult to isolate and verify. Some practical ways to reduce these dependencies can be avoiding unnecessary global data structures, preventing external access to internal data and functions, and defining clear interfaces between modules.
Another useful question is whether a proposed boundary actually isolates a well-defined responsibility. If testing a unit requires reproducing a large portion of the application around it, the boundary may be worth reconsidering.
Once the software unit boundaries have been established, another question arises: what should we actually test within a software unit?
It can be tempting to focus on the code itself, which functions are called, which branches are executed, or how the internal logic is implemented. However, a unit test should ultimately give us confidence in the behavior of the unit, rather than in the particular way that behavior is implemented.
Unit testing frameworks provide tools to write and execute tests for individual functions and components, often making it easy to check specific inputs, outputs, and execution paths. Yet, testing individual functions is not necessarily equivalent to verifying a software unit, since a software unit may comprise multiple functions depending on how the software has been decomposed. While testing individual functions is an important software development practice, verifying software units is necessary to address the corresponding regulatory requirements. It is important to understand that these are not necessarily the same thing: both are important, but they serve different purposes.
Defining that responsibility also helps establish the scope of unit testing and distinguish it from integration testing. A software unit may be part of a larger feature or contribute to a specific intended use at system level, but this does not mean that all of that functionality belongs in its unit tests. Unit tests should focus on the responsibility of the software unit itself, while the interactions between that unit and other components should be addressed through integration testing.
Ultimately, the way software is decomposed influences not only how it is designed, but also how it can be verified. The best software unit boundary is not necessarily the one that produces the most units or the smallest ones, but the one that gives each unit a clear responsibility and makes it possible to verify that responsibility independently. Thus, not only is software unit segregation about satisfying a regulatory requirement, but also an architectural decision that can directly influence how practical and effective software verification will be.
.webp)