Version control, also known as source control, is a tool that tracks and manages changes to files in a software project. It has been widely practiced by software teams to speed up software development. Source control platforms, such as GitHub and Bitbucket, securely host software code in a single location, allowing developers to pull it down from anywhere with an internet connection and start developing new features. Firmware development also adopts the same practices. This blog post discusses the crucial role of this software tool in modern firmware development.

Concurrent Development
The Feature Branch Workflow, Gitflow Workflow, Fork Workflow, and Trunk-based Workflow are types of version control workflows commonly practiced in software development. All of them have a common methodology, having up to two main branches and multiple feature branches. The feature branches branch off the main branch and are merged back to the main branch when a feature is completed or when the acceptance criteria of that branch are met. By following this methodology, developers can work on different features concurrently. If there is a dependency between features, one can also branch off an active feature branch. The Gitflow workflow requires two main branches, main and develop. By using this particular workflow, the development and release of firmware can be executed in parallel. One team can continue developing new features while another team works on testing and releasing the firmware.
Compared to the Feature Branch Workflow and Gitflow Workflow, which often have long-lived feature branches, Trunk-based Workflow promotes branches with fewer commits and frequent updates to the main branch. This workflow is commonly practiced among DevOps teams. Although the Fork Workflow uses the same branching model as the other three workflows, it is fundamentally different. Instead of submitting changes directly to the original repository, developers make a copy of (called “forking”) it, and develop features on that copy. Once a feature is ready, it will be reviewed by the project maintainer before contributing back to the original repository. This workflow is often seen in open-sourced projects.
Firmware Releases
Once the main branch is ready for deployment, we can tag the latest commit on that branch with a version number. The naming of firmware versions can be standardized through versioning conventions such as Semantic Versioning. The Continuous Integration (CI) pipeline can be configured to run a release workflow, such as building a release target and creating a release on the source control platform. We can also generate the release on the platform manually by linking it to an existing tag. By doing so, we have a list of all firmware versions in a single location for future reference. The release artifacts generated by the CI pipeline can then be used by Over-The-Air (OTA) updates to update the devices in the field.
History and Progress of A Project
When we submit the changes to the source control platform, they are separated into commits. A commit consists of the author’s name, the time and date, and the description of the changes, also known as the commit message. Writing an informative commit message will help the code reviewer and future developers understand the changes. The commit messages can also be used as a reference when writing a release note. It is best practice to keep each commit with a single task. For example, if a commit message needs to use “and”, that means the commit could be separated into two commits.
By referring to the commit history, we know who is responsible for or has knowledge of an implementation. So, we can approach them to learn more about the feature, which can help us get up to speed on what’s going on before implementing a new feature on top of the existing one. To learn more about writing a commit, check out the conventional commits page.
Debugging
It takes time to resolve most of the bugs in the firmware. If a bug is discovered in released firmware, we can roll back to a previous stable version if a hotfix branch is not ready in time. On the other hand, if the development branch has a bug with no obvious root cause and blocks developers from adding more features, one option is to figure out the commit that introduced the bug and revert it for now. This can be achieved by using the bisect subcommand of Git. This subcommand uses a binary search on the commit history to figure out the culprit. With the result of the search, we can unblock others by reverting the commit in which the bug was first introduced. By excluding the commits that are not related, it helps narrow down the root cause of the bug.
Code Size Optimization
Modern firmware development faces a common challenge: accommodating multiple board revisions consumes a significant amount of flash space. As projects evolve and features expand, this space becomes increasingly valuable. Once the hardware design stabilizes, code supporting older board revisions can be removed, freeing up precious flash memory. The robust commit history maintained on source control platforms ensures that these changes can be safely reverted if needed, without fear of losing past modifications.
Code Quality Assurance
To bolster confidence in released firmware, developers can leverage several tools in conjunction with version control. A pre-commit hook, which is a script executed before each commit, can automatically check for coding style (among many other checks). By enabling a coding style hook, it offloads stylistic concerns from reviewers, allowing them to concentrate on the business logic during code reviews. The pre-commit tool, a multi-language package manager for pre-commit hooks, further enhances automation by enabling static code analyzers such as Cppcheck or Cpplint to run on every commit.
Furthermore, on the source control platform, a CI pipeline can be configured to build and test the firmware with every commit and upon merging changes into the main branch. Integrating a Hardware-In-The-Loop (HITL) test into the CI pipeline and having it run on a scheduled basis can verify firmware functionality. To learn more about the CI pipelines, check out our “Beat the Clock: CI Secrets for On-Time Embedded Systems” and “Setting up Automated Firmware Builds” blog posts.
Code Reusability
Submodules enable the inclusion of an external, version-controlled repository as a subdirectory within another. Unlike manually copying a module, which necessitates manual updates for new changes, submodules can all be updated simultaneously using the command git submodule update –init –recursive.
This feature is handy when developing reusable libraries or modules within a project. Instead of duplicating entire directories, submodules allow these components to be imported and shared across multiple projects.
Conclusion
The introduction of version control has improved the firmware development cycle by promoting concurrent development, releasing stable firmware, having a development history, ensuring firmware quality, and promoting the reusability of modules.
Dojo Five is dedicated to modernizing the embedded industry with modern practices and tools such as pre-commit and CI pipelines. If you are interested in bringing your project to the next level, you can schedule a call with our team. Or, feel free to check out EmbedOps, our CI build platform. It’s free to sign up.
We look forward to hearing from you! -D5


