Judging from my knowledge of the history of firmware engineering tools, practices etc. It has consistently lagged behind the software engineering field by several years. For example, as far as I can tell there is still a fair amount of debate in the firmware world as to whether C++ is actually worth using for our applications, and some C++ compilers are noticeably absent (microchip?!?). I imagine that in large part this is due to the differences in requirements between firmware and software. Again, judging from history, it seems its only a matter of time before the properly vetted tools and techniques make it into the firmware world.
What methods, tools, best practices etc that modern software engineers use regularly, could firmware engineers also leverage to improve their craft?
Specifically I'm thinking along the following axes (but don't let them limit you):
I'd also love to see embedded shops answer or comment on the answers to provide feedback about theoretical feasibility or, better yet, personal experiences.
UPDATE
I'm especially interested in jumping ahead of the curve a little bit. So relatively new stuff that has been vetted reasonably well (works well for most people), like C++, TDD, etc. What do you use all the time and love?
UPDATE 2
I'm getting a lot of good general programming advice in the answers so far, which is great, but I'm really looking for more unconventional approaches that have proved successful for people. I'm trying to tease out the Agile practitioners, the TDDers, and the rest of you who have tried stuff and seen it pay off in spades or fail horribly. As a software engineer has there been a tool or practice that you've adopted in the past several years that has had a remarkably positive or negative impact?
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.
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.
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.
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:
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:
If embedded developers used those parts of C++ they could build more flexible design and not incur a high cost.
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...
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.
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.
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.
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