Back during the pandemic, the embedded development wearables team I was on made several key changes to meet our deadline within the year. The team size doubled so quickly level-setting on the foundations of how we would get things done mattered. Those decisions fell into these buckets:
- language and style (amongst a bunch of C, modern C++, and generalists),
- unit test requirement by default,
- design docs before you write code (and associated design review),
- and use an early version of Pigweed to expedite developers’ day to day.
Taking clear stands on this early, enabled us to meet launch day expectations. So let’s break down how those worked starting from the bottom of the list.
Pigweed is a platform that enables developers to quickly integrate modules and build. It’s Ninja-based and while that might not mean much to someone using Cmake, Make or other system, here’s an example of how it speed me up:
If I needed to add a new file, it was easy to add it for compilation and allow it to get used elsewhere. Any changes to the file upon save could trigger an auto rebuild and execution of host unit tests as well as multiple builds (target or otherwise). This enables me to mentally not have to remember to run “this and that command” except to commit the changes for review, confidently.
While I am not evangelizing for Pigweed, I am pointing out the number of steps needed to be performed by each engineer should be clear and automated as much as possible to enable best possible outcomes.
So what else is part of that but cannot be easily automated? Designs and design reviews. When I hear push back from teams that they cannot handle adding this to their process, I mention a few things designs and their associated reviews can do:
- Having a well-throughout and vetted plan can ensure success to match against the implementation.
- A self-standing design means another person can implement it and you can move onto other cool stuff.
- If code reviews help prevent deploying bugs, then design reviews avoid poor designs.
- Enables a baseline documentation to easily keep up to date and provide engineers a way to step in and keep up.
- A place to put considerations, open questions, whys, and early tests to check against. A clear parking lot to the related and location to re-evaluate for porting or refactoring in the future.
The largest concern is usually time. None of this means writing a novel, but rather get out how we arrived at this solution and what’s the plan to implementing it. Start at around a 1 page target (ignore title pages, and other overhead). If you need more to explain yourself, do it. If it still is not clear to a colleague, explain a bit more.
Once that new design is implemented, deciding on the unit test methodology is not important but doing it is if you are running a lean operation. Whether googleTest or cpputest, or test-driven or after the fact, on-target or host, you are not stuck once you decide so just start with whatever gets the team going fastest. Over time, you may change your mind. Not sure where to start or the value? Start with this article on Memfault.
Whew this is getting longer than I expected. So last but not least: language and style. Ultimately, it won’t matter because there’s always a new shiny gimmick or revolution happening and great code now is not great code in the future so being ok with change is part and parcel of embedded (yes, even embedded).
Yes, understanding your requirements is essential. A clear document around what tools (compilers, debuggers, scripting languages, etc…) version you are using to meet them is also essential. C99 requires different code writing from C20 much less C++25. Our team embraced the fact we were going to do modern C++ (14 forward ) with a smattering of C++20. We encouraged the team to take classes and read books. More importantly, we used the group time to teach some key basics like:
- Namespaces
constexpr
- typecasting
- code style
Most of the rest felt C-enough for us as we climbed this curve.
That last item, Google C++ style readability was actually the more challenging part of the adaptation. In short, using an existing one works well to get going, but more importantly, having it automatically managed in your clang-tidy and clang-format efforts will make code reviews less contentious and faster.
In summary, there are a lot of decisions to make at the start of an embedded development project. It is more important that you jointly make those decisions such that they enable as much of the team as possible and have ramp up plans for those who need a bit more training. Yes, the first 2 months were slow but we meet the timeline for Google Pixelbuds A-series and starting the next project, Pixelbuds Pro we were able to leverage these practices to hit the ground running quickly and reuse many parts quickly.