• Daniel (they/them)

Git for hardware: commit best practices



Overview

Revision control can be overwhelming when first starting out. Engineers used to releasing files once per PCBA revision can be daunted by commits. It’s very easy to finish working on a section of a schematic and instantly jump to another and keep the development momentum flowing.


Some engineers take a liking to frequent updates as it can be fun to stay organized. Others might begrudgingly follow a process, but feel it’s too much overhead or that it slows them down. Others might not realize they’re even working on disparate changes in sequence or when to segment and label their work.


There are no one-size-fits-all solutions to how to manage your changes. There are, however, guidelines that produce correct hardware with a minimal amount of overhead and can be modified to fit your team's process and your personal tastes.


Technical background

As a quick review, users make changes to a file or files and then commit those files to their local repo. When they are ready, they can push one or more commits to the repo. When they push the files to the repo, they can choose which branch to push their files. There are more complicated scenarios, but let’s start with the user being the only one creating changes, with no need to fetch the list of remote changes or pull the changes to their local filesystem.


Git commit best practices

Commit to the correct branch

There are many ways to break things in git. Parallel development branches can make a lot of sense, but if you start mixing changes between branches, the two branches become like twisted noodles, and changes intended for one branch get associated with the wrong development line.


There are many ways to fix things in git, and sometimes new bugs are interjected. Without backups, it’s possible to destroy large chunks of your work, even the entire repository. Git is a powerful toolbox, but this toolbox contains power tools that can rip through well-organized branches and cause hours of work to fix.


There are worse things than losing work. Lost work can be recreated with known, good rework. It’s expensive and time-consuming but is a fraction of the cost if misbranched commits make their way into a purchase order. ECAD tools are visually controlled, not file controlled. You might have to manually repair commits by opening up ECAD files and copying/pasting. If you do this with schematic components, nets, or tracks on a PCB, you stand a good chance of introducing a bug, removing a feature, or changing the intended functionality.


The easiest way to avoid one of the biggest causes of problems in git is to commit to the correct branch. We’ll cover branching more in future chapters, but the following advice will be moot if you have to undo and mix/match changes.


Commit related changes

Committing related changes can mean many different things in a hardware design, depending on the lifecycle. Hardware, unlike software, is massively parallel, and the design components are not broken down into small functions that live in small files. The way circuits are interconnected is similar to code, but different from software’s function prototypes inputs and return outputs.


It would be prohibitive to schedules and sanity if changes were scoped to one commit per component or per net, but sometimes it might make sense to make a commit for an individual IC, especially if it is a critical part of the design like a microcontroller, microprocessor, FPGA, or SOIC. It may even make sense to create a commit for an individual IC if it is complicated, has failed in the past, or just has a lot of pins.

  • “Label every net on main microcontroller U47”

  • “Connect all open nets on ADC U17”

Hardware often iterates with loose ends. There are many unfinished commits that are fine in hardware revision control that would be unthinkable in software revision control. Early stages of hardware design often have lots of empty blocks, diagramming loosely how the design interoperates. Many of these blocks might remain until much later in the design. It is not common for software developers to add that much pseudocode or empty code blocks, but it is very common for electrical engineers to leave non-functional stand-ins.

  • “Place a block diagram of relay control ckt”

  • “Add all PMU ICs, unconnected”

Sometimes it’s too much work to document every component going into a design, especially when they’re added in batches. Other times, a single commit is needed for a single new component.

  • “Add voltage regulators to library”

  • “Add ACT1746, Motor Driver, TI, DRV8243-Q1 to library”

It’s possible to affect dozens or hundreds of components in a commit. Sometimes changes are related because they all affect the same voltage rail. Sometimes it’s because all the serial data lines are changed. Changes aren’t always limited to a functional subcircuit. Often it makes sense to make a commit if you need to change all of one type of component.

  • “Add +3.3V to all powered chips”

  • “Connect all I2C nets”

  • “Change all load resistors to 1K”

  • “Change all capacitors from 0805 to 0603”

After the initial circuit design, changes should become narrower in focus and should highlight small changes like upgrades or fixes.

  • “Add isolated optofets to relay drivers”

  • “Fix current driver bandwidth”

One problem engineers face is wanting to fix bugs as they see them, bouncing from their task to correct the error, and then jumping back to their original task. It might be better for the engineer to create an issues.txt file or add issues to the git repo and add changes they want to make to the file as they go. That way, they can save the changes they want to make until they can file a separate commit message, or group it with a series of related changes.


Because hardware mistakes are expensive, It’s better for the engineer to correct the mistake in-situ rather than forgetting about it. Best practice, however, is to defer the changes and keep a log of the issues so the project changelog can be more coherent.


It can be hard to know what changes are related, and every engineer has their own style and mode of thought. It’s better that everyone strives for the goal of related changes and commits changes, rather than waiting for the perfect moment or grouping.


Write good commit messages

Good commit messages should tell you what changed and why. Stakeholders should be able to read the summary sentence and understand the scope of what changed and the reasons for the change.


Good commit messages should be short. The general rule of thumb for the subject line is to be no more than 50 characters, although some teams extend this to 72 characters, or 8 characters shy of an 80 column string. It may be frustrating at first to try and fit everything into the subject line, but this coaxes you into better summarizing the change.


Any additional information can be added below the subject line. For example, maybe you want to refer to all the part numbers that were added, or the reference designators of resistors that changed. Subtle, or difficult to understand changes should be given a sentence or two. The same is true of bugs or challenges that are being fixed.


Well-structured commit messages help out when it’s time to do a design review and merge all of the changes. If a commit message is too vague or hard to understand, it might delay and raise questions that can be addressed when the commit is made.


If you are using a text editor or command line, always add a blank line after the summary, with the longer explanation happening at line 3. Most git clients will take care of this for you. Adding the blank line between the summary and full description helps make the entire commit message more readable.



No matter how you style your commits and messages, the goal is to design good hardware. Good commits that are made of short and related changes will help the project flow better and make incorporating changes much easier.


Writing good commit messages helps everyone, including yourself, to know exactly what each commit does and why. When it comes time to merge all of the changes into a release, you can be more confident of commits that follow these guidelines.


Commit Often, perfect later

It’s better to get the hardware changes into the system, even if they’re a little bit wrong. It’s okay to copy the first 10k resistor you place and use it as a stand-in for every resistor on a page in order to capture the topology. It’s also okay to take the time to edit the resistor value each time you copy it, knowing you might have to come back and select actual components, rather than just changing the value of an ideal resistor. If the engineer places generic components and then replaces them back-to-back then it’s okay to wait until the circuit is complete to place a commit. Nothing, however, should stop the engineer from committing a partially wrong circuit and blocking the partially right subcircuit.


Git is all about change control, not change prevention. It’s important to break the changes down into small enough chunks and iterate later. Some engineers might leave themselves a note like “Change Resistor values to match Reference Design”. Other engineers might make an issues file or create an issue with git. Keeping the momentum going by allowing yourself to commit half-finished work trains you to look for good opportunities to segment the work. Some wrong change is better than no good change.


A single engineer might create several changes per day, or dozens, depending on how they segregate their work. If you only have a single commit at the end of the day or week, you run the risk of overwriting the correct parts of a design and being unable to revert back. It becomes incredibly hard to conceptualize and distinguish all the different changes. On the other end of the spectrum, making hundreds of commits a day generates too much work to review. Obviously, there’s no right answer to how often and how few changes to commit, but it’s better to err on the side of too many rather than too few. If you’re not sure, have a discussion with your team about commit frequency and style. Soliciting feedback about your commits from the people you work with creates a healthy team.


Another good rule of thumb to use is to commit whenever you may need to rewind the current state. If you’re about to tear up a section of a schematic or PCB layout, it might be a good time to commit. If you’re swapping out components, it’s always good to have a “before” commit so you can roll back your “after”. Save your current file, make a meaningful commit, and move on to the new task.


Test your design before you commit

There are many ways of testing your hardware design. Some of them come baked into the EDA tool like the (Design Rule Checker) DRC or error log files for exporting changes between the schematic and PCB.


In PCBA designs, it’s often impossible to fix all the DRC errors until very late in the design. It’s okay to check in files with errors as long as the errors are tracked. We recommend adding your log files to the project to track the errors. Hardware bugs are expensive. If you run your DRC before every commit, you can not only track errors, you can see when errors are introduced. Making DRC checks as part of your Continuous Integration / Continuous Deployment (CI/CD) process gives everyone viewing the change an idea of what is fixed, and what remains to be done.


If you are close to the end of the design, or at a stable release, it’s a no-brainer to run the DRC every commit. If your files pass the DRC, you make changes, and the files pass the DRC again, you have a lot of confidence that the changes are good. If they fail, you’ve caught a bug in your implementation. It’s best to automate the process, but even if it’s manual, it’s an extremely high-value, low-effort action that can reduce countless problems.


There are other ways of testing the circuits. Some EDAs allow you to run simulations on circuits using SPICE or other simulators. This is an interesting tradeoff because it often requires making a rudimentary twin of the schematic. It can be expensive to keep the two in lockstep, and the process of modifying the simulation schematic can introduce bugs and false confidence. Just because the simulations pass, doesn’t mean the actual circuitry matches the simulation 1:1. A logic simulation might guarantee that the 1s and 0s are happening in the right order and functionally correct, but might not take into account timing, transmission line errors, noise, or discrepancies in voltage levels.


Many design teams relegate simulation to a fraction of the design process to create the circuits, but not for verification after the design is set. It’s expensive and often not as reliable as desired. Simulations will be the future of CI/CD, but for the moment, the schematic tools aren’t as productive as they could be. Try to simulate when you can, but don’t be afraid to defer simulations when it takes more time than it saves, or when the models aren’t accurate enough for meaningful tests.


Specialty simulations like high-speed digital interference, EMI radiation, and antenna simulations should be run as soon as they can be modeled and tracked. These are all reasons PCBAs need revisions and it is easier to fix any problems right after you’ve created them, rather than later in the project when you have to rip up a lot of layout.


If you have the test and it can be run in a short amount of time, it should be run before making commits. Longer simulations can be run periodically, or before a design review. There is no sense in holding up your design and commits if the simulation takes several minutes to run.


Use the imperative mood

It’s very common for new git users to use the past tense when making commit messages like “Added bias resistors to transistor network”, but the preferred convention is to use the imperative mood like “Add resistors to transistor network”. The reason for this is to switch from the mentality that something changed in the past to what changes will applying this commit do to your design.


The handy rule of thumb is to think of every commit summary finishing this sentence:

  • Imperative: If applied, this commit will

  • Replace OpAmp with Higher Bandwidth ACT1378

  • Connect DAC to amplifier circuit

  • Add voltage translator to I2C nets

  • Fix noise issue on dithering ckt

Bad:

  • Fixed bug with Y

  • Changing behavior of X


There’s nothing to say that your organization can’t stick with the past tense, but using the imperative mood will make it easier to work on a team when you have to merge many different changes to merge from a development branch into a main or release branch. It makes more sense to apply a fix than to “apply a fixed”. Changing the language of your commits will help you conceptualize the changes and how they fit into the merge workflow.

If you’re looking for more examples, all the commit examples in this article are written in the imperative mood.