What can firmware engineers learn from software engineers? Plenty!
I am surprised at how similar firmware development is practiced today as it was 25 years ago when we first started using C for embedded development. C was a big step forward from assembler, but there are many more lessons learned that firmware engineers can and should learn from. Yeah, some of the tools are better, but many practices are stuck in the 70s and 80s.
Embedded software development does add some additional challenges on top of the challenges faced by non-embedded developers. But all the principles and practices that skilled software developers use are applicable to embedded development. BTW: It's not just the embedded software developers that are not up on these state of the art practices, but many non-embedded software developers as well.
The people I know and have met doing firmware are by and large a very skilled group, working to solve difficult problems. Unfortunately, for whatever reason, many have not kept up with developments in the software world. I think it has to do with an imaginary barrier erected by firmware engineers.
Embedded and non-embedded developers speak different languages, but solve similar problems. Keeping embedded code independent from a hardware device is essentially the same as keeping application code independent of the UI or database. The underlying principles are the same.
Here are a few things that I think embedded developers should pay more attention to. Some of these principles and practices can be used right out of the box, while others might need a little adjustment to deal with the embedded challenges. If you want to substitute the word firmware for software below, go ahead, I don't really distinguish between the two.
Dependency Management
Dependencies between modules must be managed. Dependency from software to hardware is a special case that must be actively managed by the embedded software developer. If you don't manage the dependency, it will manage you.
In practice this means that only a limited subset of the software modules should have knowledge of the underlying hardware (and operating system). As the hardware evolves, and it always does, the investment in the hardware independent code can be preserved.
See my ah ha! moment.
Robert Martin has written extensively on the SOLID design principles. Embedded developers should get to know them and apply them to their designs.
- S-Singled Responsibility Principle
- O-Open Closed Principles
- L-Liskov Substitution Principle
- I-Interface Segregation Principle
- D-Dependency Inversion Principle
These principles lead to designs that better stand the test of time. The SOLID principles encourage creating cohesive and independent modules. They are build on object oriented ideas, but can be applied to C. We have to stop the function-call data-structure free-for-all that is all too common in embedded C code.
C++ and OO languages
Why can't you use C++ and OO? Because they are too slow, or too big. What are the facts? C++ is a big, and mysterious language, but you don't have to use all of it. Take a look at Why are you still using C?
C++ makes up for some of the problems that C does not help much with like:
- Encapsulation and information hiding
- Programming to interfaces
- Substitutable objects
- Ad-hoc initialization
C++ can be used effectively for embedded development. Well you do need a C++ compiler, and the headroom. Maybe that is not possible in your world, or maybe it is the cost of doing business. Start by learning:
- classes - these are structs with member functions as well as member data.
- constructors - these make it possible to get initialization right, all the time.
- destructors - if you learn constructors, you must also learn destructors to keep the universe in balance.
- inheritance - use this mainly for defining interfaces that contain only pure virtual functions. Interfaces provide important dependency breaks and flexibility points. These are usually unjustly discouraged in embedded. There should be no mystery or prejudice here; virtual functions are function pointers under the hood. The alternative to effective use of interfaces is complex conditional logic, something that embedded C programs usually have too much of.
If embedded developers used those parts of C++ they could build more flexible design and not incur a high cost.
Test Driven Development
This might be the biggest game changer. I am glad to see other posts mention it too. TDD can help prevent defects now and in the future. To see why TDD might help take a look at The Physics of TDD.
Embedded does present some unique challenges for TDD. For example, TDD requires an extremely fast incremental edit/compile/link/run cycle. For many embedded developers this means careful Dependency Management and running unit test first on the target. See more about adapting TDD for Embedded.
With TDD, you are creating code that is thoroughly tested. The comprehensive automated test coverage acts as a safety net, allowing code to be changed safely as requirements change. Unintended consequences are immediately discovered.
Also, having the tests that you get almost for free, allow you to fearlessly refactor your code...
Continuous Refactoring
Code is written once, but read many times. Then it is changed and tweaked, leading to designs that degrade over time. If developers do not continually refactor to keep the code clean, it turns into a mess. Maybe some of you are dealing with that mess. TDD's automated tests enable low-cost and low-risk refactoring.
Continuous Integration
Automate your build process. Have it run with every workspace checkin. This is a challenge with the heterogenous tools sets often needed to get the compiled code into the target, but it is still the right goal.
The sooner the embedded developer knows that a change is somehow incompatible with some other work, the faster it can be repaired and the less time will be spend in painful merges.
Incremental Delivery
Find ways to split the work so that large painful integrations can be avoided, and design ideas can be tried early. Avoid splitting along architectural lines, focus on delivering slices of visible functionality.
Collaboration
Embedded developers! get out of there cubes and work together. How can you get better when you only see your own code? How can you improve when you are the expert on technology XXX, have mastered it and don't get an opportunity to work in different areas.
There is lots to learn out there. Are you responsible for being all you can