Note: The question was heavily rewritten after the posts it received.
I work on ana team that develops embedded software team responsible for multiplevarious in-house developed devices. Each device runs several “bare metal” applications—there is no operating system like Linux; insteadhosts multiple "bare metal" applications—all compiled into self-contained binaries—and we also maintain a set of common static libraries (e.g., binary images are flashed to the devicesfor networking, with each image statically linking all required codecryptography, etc. Only one application can run at a given time) used by the applications.
Current Setup and Issues
Monolithic Repository per Device
Each device has
All the code lives inside a single per-device repository that contains:where every commit goes into a main branch.
- Code for all the device’s applications.
- Static libraries providing common features (e.g., networking, cryptography) that are shared between the applications.
Shared Code Challenges
Breaking Changes:
When one developer makes a change in a library shared between applications to add a new feature, the update is committed directly to the repository’s main branch. While the change may work for the developer’s specific application, it can inadvertently break up other applications that depend on the same library.
Duplicated Fixes:
A bug fix in a library in one device repository must then be manually replicated across other device repositories, increasing maintenance overhead.
Proposed New Approach
Separate Repositories:
Move each library and each application to its own dedicated Git repository.
Version Control for Dependencies:
In each application repository, specify the exact version (using tags or commit hashes) for each required library. This is similar to dependency management strategies (such as Zephyr’s west) where an application “pulls” the precise version of each library it depends on.
Advantages
Isolation of Changes:
A breaking change in a library will not force an immediate upgrade on all applications—each application remains pinned to the version that has been thoroughly tested.
Streamlined Bug Fixes:
A reported bug can be addressed by updating the pinned version for a single application, enabling the QA team to validate the change independently. This provides stability while development on the common libraries continues.
Reduced Duplication:
Fixes in common libraries do not need to be manually replicated across multiple device repositories.
New Requirement – Inter-Dependent Libraries
The challenge grows when we introduce inter-dependencies among libraries. For example,This setup has led to a “Hardware Abstraction Layer” (HAL) library is utilizedmajor problem:
Team members that are working on one application are breaking others application and work by almost every other librarycommitting changes to the common libraries.
The same goes for a “OS Abstraction Layer”Developer of "App B that abstracts away OS multithreading things (We can't use STL threading related classes like thread, mutex, condition_variable, etc since they are generally not supported inuses Lib A" has no way to choose a fixed version of the embedded world)"Lib A" for his release.
How should we effectively manage versioning and dependency control when libraries themselves depend onIf developer of "App A that uses Lib A" needs to make a required breaking change it has to wait for the other libraries? Specifically:developers to be ready before doing it's commit.
- What strategies or tools can resolve conflicts or manage cascading changes across a hierarchy of inter-dependent libraries?
- Are there approaches from similar embedded or constrained environments that may better serve our needs in terms of modularity, testing, and maintenance?
Additional Context and Considerations
Embedded Development Constraints:
Since our applications are “bare metal,” all dependency resolutions must be handled at build time with no runtime dependency management.
Impact on Workflow:
How do we strike a balance between the stability required for intensive QA testing (pinning tested versions) and the flexibility needed to integrate improvements or necessary fixes in common libraries, especially as those libraries are used across multiple applications?
Request for Community Input
I’m looking for guidance on:The codebase currently does not support automated testing. Making the code testable requires heavy refactoring that in the current situation we can't easily do because of the above It's something we are aiming to do after resolving or making this issue less problematic.
- Best practices for managing versioned dependencies in a modularized codebase for embedded “bare metal” systems.
- Tools, methodologies, or dependency management systems that have been effective in similar scenarios with inter-library dependencies.
- Potential pitfalls or alternative strategies that might be considered, given the dual priorities of ongoing development and ensuring the stability needed for QA testing and bug resolution.
Any insights, experiences, or referencesWhat strategy can we adopt to similar cases would be greatly appreciated!resolve the above problem?