Pillar 7 of the Dojo Five Modern Embedded development practices is Emergent, Intentional Architecture: Architecture matching the inherent problem to be solved, evolved and documented as the project evolves.
The firmware industry is plagued by bad software architecture, with giant functions of tangled spaghetti code superloops and global variables. The code is hard to understand, hard to test, and hard to maintain. It often has bugs, and efforts to improve it end up creating new bugs.
This results in poor quality and slow development. Worse, bad firmware architecture can kill. When the code is doing hard real-time management for things like automotive acceleration and braking control, the consequences of poor quality are much more severe than just a buggy product. [Read more about debugging]
Emergent, Intentional Architecture
Good architecture is both craft and science. There are a variety of patterns and techniques that encourage building code that is malleable, reliable, and maintainable. The architecture emerges as you apply these and learn more about the system. It breaks things down into small cohesive elements that express intention about what they do.
Malleability means the code can be changed as understanding, features, and requirements change. The code is architected with change in mind. Reliability means it works as expected, throughout its life as it evolves. Maintainability means it can be changed safely, not risking breaking it every time you make a change.
Design patterns are a way to apply known solutions to the design problems that arise, adapting them as necessary to the specifics of the system. They’re proven solutions that have been developed as best practices to follow, providing a common design language between developers.
Programming by intention means you write code as if the ideal interface exists that expresses the intention of what you want to do in functional steps, then implement the code that provides that interface. Sometimes that means using a pattern such as Adapter that provides the desired interface on top of other existing components. That adapts them to your desired usage, so that you can follow your intentions even if those components don’t conform exactly to them.
Techniques such as TDD (Test-Driven Development) and refactoring provide a feedback loop that drives forward progress. The tests resulting from TDD form a safety net for fast refactoring cycles. You can refactor fearlessly, getting instant feedback when something goes wrong. That allows you to evolve the architecture quickly. [Read more about Unit Testing]
These practices combine to help the architecture emerge and adapt over time. Just as requirements will change over time, the architecture needs to change to handle them. That’s the reason for avoiding Big Design Up Front (BDUF) approaches that try to create a predetermined blueprint for the entire project; such designs no longer match the inherent problem once it changes.
See 10 Pillars of Modern Embedded. Pillar 4: Effective Testing for more on testing and 10 Pillars of Modern Embedded: Pillar 6 Effective Requirements for more on requirements.
Documentation is important because many people will work on a system over its life. Documentation provides the information they need to work on it effectively. That helps minimize the risk of making incorrect changes.
There are a number of ways of documenting architecture. The code itself is one form, consisting of good naming, structure, and modularity. But by itself it isn’t enough.
Unit tests are an excellent form of documentation, because they’re executable. They show how to use the software components and the expected results.
Additional forms such as diagrams and explanations of how things fit together provide a roadmap of the system, accelerating understanding. Where other forms tend to get down into the fine details, these can take a higher level view, summarizing the design at different levels. That helps guide exploration of the code and tests. See Documenting System Architecture With AsciiDoctor for an example of a lightweight documentation tool.
It’s also important to document architectural decisions, so that people will understand them and the rationale behind things. Architecture Decision Records are a way to do this.
Need help with testing, documenting or designing your firmware architecture? We are the firmware experts companies trust to get their embedded projects done right. Consistently. Efficiently. Cost-Effectively. Every time.
recommended additional reading
Emergent Design: The Evolutionary Nature of Professional Software Development, by Scott Bain, shows how to apply patterns to architecture, using disciplined practices. These practices include TDD, refactoring, and programming by intention. It also provides an excellent example of documenting design through UML diagrams.
Essential Skills for the Agile Developer: A Guide to Better Programming and Design, by Alan Shalloway, Scott Bain, Ken Pugh, and Amir Kolsky, expands on the concepts presented in Emergent Design.