Published on: 23-04-2025
Author: Karthick Shanmuga Rao
Modular architecture is crucial to improve scalability, manageability and long-term efficiency of a LabVIEW application. A particular experience that we have had is the challenge of managing dependencies and keeping them modular in large projects where there are large number of libraries (~100) and several developers involved in building the solution.
Without dependency management, very quickly a project can lose sight of its modular architecture definition and in the long-term both developer and owner lose faith in their own application.
The Practical Push to Seriously Consider Dependency Management and Monitoring
What invariably takes place is that developers unknowingly create unexpected dependencies through wrong reuse of VIs or Classes that violates separation of concerns. During the course of developing a module, its dependencies can change as and when the team works on features. This leads to a tangled and messy ball of coding yarn that needs code dependency management to avoid it altogether.
Note: A module refers to any top-level library that has specific responsibilities defined by the Architecture. It can call multiple low level reusable libraries which would be its dependencies.
In such cases, we will also be unable to build PPLs due to circular dependencies being created that are hard to identify. The LabVIEW Solution builder generally looks at dependencies and builds the lowest module first and works its way up to build all the required libraries. With circular dependencies, this order is broken, and the solution builder ceases to build as it cannot find the lowest module.
It is important that we track dependencies and ensure that actual dependencies align with expected dependencies as per the Architecture Definition. We don’t want to only avoid circular dependencies but bring down unnecessary dependencies to zero. This is important when we look to reuse a module in the next project, without taking any unnecessary code into the new project to maintain the integrity of the architecture.
And finally, you want to get to a point where you can visually represent the dependencies in the code base and ensure the software is developed exactly how it was first intended by the Architecture definition or even better.
Why is Dependency Management/Monitoring Important?
- Dependency tracking and management is essential in ensuring how modular the architecture can remain over the long term.
- To ensure reusability, clean, clear-cut modules with singular responsibilities.
- We ensure that development adheres to the proposed design – even accidentally, a developer cannot override the design. Any need to override the HLD will require a purpose-driven action.
- Track and achieve low coupling metrics and high stability of modules.
- Modular deployment is an important goal – we cannot develop, build, test, deploy modules with unnecessary dependencies. Modules can also be developed in parallel, independently if they are modular and loosely coupled.
- Modules can be easily isolated and tested to find problems and complete debugging.
In a nutshell, we ensure a robust application that both developer and customer can rely on.
Automating Checks to Manage Dependencies
Defining Boundaries
The first step to automating checks would be to define the boundaries of any library. To do this, define a process to maintain the list of expected dependencies of each library and assign module owners who can review and approve changes to it. Expected dependencies essentially match your Architecture.
(Example setup: Add an ExpectedDependencyTracker.txt file in repository for each LV library)
Reading Actual Dependencies in Code Base
Next, define an approach to programmatically read the actual dependencies of each library from the code base and export the report to a text file.
Finding Circular Dependencies
Finding circular paths in LabVIEW may require developing a complex logic. So, we looked for reusables and open-source libraries to find circular paths and visualize the dependencies of the modules.
We developed a Python script using open-source libraries like networkx, and built a directional graph with each library name as a node in it. This graph data structure and the networkx functions helped us to easily find circular paths.
Also, the Plotly library was useful to visualize the dependencies of modules if needed. The python script along with the actual dependencies report in previous step can help developers to identify and rectify circular dependencies.
Ensuring Adherence to Defined Boundaries
- Once you have defined an approach to programmatically read actual dependencies, you could implement a CI pipeline check that could find any new dependency that may be created for any of the modules.
- In GitHub repository settings, Enable the Branch protection for the main branch to avoid merging of Pull requests for which the CI pipeline fails. This would block the PRs not adhering to the defined boundaries.
- If the CI Pipeline passes, the Code Reviewer reviews and approves the respective Pull request.
- If the CI Pipeline fails,
- The developer can either remove the unnecessary dependencies created by him and then request for review again to re-run the CI Pipeline. (Or)
- The developer can align with the module owner about the need for the new dependency addition to a module. In which case the module owner edits the expected dependencies in dependency tracking files in the repository.
Late Adoption
If you are implementing dependency management to an existing project as opposed to including it from the start of your development, you can expect the velocity of the team to be reduced temporarily. This is because they may face several CI Pipeline failures from the existing codebase, and they will have to fix them to merge their Pull Requests to the main branch. As the team continues the process, it will reach a stage where all modules have only expected dependencies.
Your Library Becomes Your Happy Place
At the end of the day, a clean library where everything remains where it should be, allows modular deployment, reuse, longevity and peace of mind. With each module functioning independently, and integrated seamlessly, you have an application that is robust and can be maintained with ease.