A blank page can be very intimidating, even for a Test-driven developer. Where do we start? Write a test, right? Not always.more...
Embedded Development is being influenced by the Renaissance. Many embedded tools and techniques are very advanced. But some areas lag the software development community at large. More and more functionality in embedded systems is moving into the software. Embedded software developers are feeling the pain their non-embedded brothers have encountered. To keep from getting bogged down, and having to reinvent effective techniques, embedded software developers can apply many of the lessons learned outside the embedded arena. Embedded developers can gain greatly by learning successful agile development practices
Many embedded software projects suffer from the problem of late projects and high defect levels; the dark ages for software development organizations. Some of these problems can be overcome by applying the design and development practices learned outside of embedded.
What's the problem? Everything is fine, isn't it?
Embedded software development is hard work and it does not seem to be getting any easier. Too often requirements are vague and constantly changing, schedules are fixed without concern for how much work there is to do, and everyone is getting burned out. Add on top of that the hardware is late and we're squeezing our needed integration and test time with every delay. You know we can't change the release date (at least not until we've past it). Maybe after four months of debug you find yourself saying "If we fix this one bug we can release. It's the last one. Next time we're going to do this right!" Then some time goes by and we get into the same rut. before you know it you're talking about doing it right the next time, again. I can see the overtime coming to get that "last" bug out.
We've all lived that project. The classic problems of vague requirements, unrealistic schedules and developer burnout have been the bane of software developers for years. For embedded development you can pile on another layer of problems like: concurrent hardware development, cross platform development, limited execution resources and real time constraints. Thankfully there has been some progress in the software development world on these classic software development problems. The software development community rediscovered iterative development and applied an interesting set of practices that support iterative development. This renaissance was first noted by the fervor caused by Extreme Programming. XP's provocative name raised the hair on many necks. A couple years later the renaissance got the less scary, corporate friendly name: Agile Development. Agile Embedded Development is one area where embedded teams can avoid some of the difficulties that come with both large and small development efforts.
Agile development practices come in two broad categories: planning and engineering practices.
Getting Software Done
Agile development is focused on reducing process waste and misplaced efforts by putting the bulk of the development teams effort into the software.
At the core of Agile is iterative development. In iterative and incremental development the team does not have to wait until everything is decided before starting development. There is an initial planning activity that establishes vision, goals, time-line, architecture and content. Like all realistic initial plans, there is considerable uncertainty. in Agile we admit the uncertainty and move on making the best decisions we can with the information.
Development is broken into a series of incremental deliverables that focus on features more than architecture. Architecture is obviously important, but people buy products for their features. The deliveries interval is usually 2 to 4 weeks. Even though there is a lot of uncertainty in some of the requirements development can begin on core functionality with requirements are concurrently elaborated.
Having frequent functionality deliverables give very important feedback to the team and management. Such as:
- Is the feature delivered correct?
- Did we discover additional features?
- Did we discover that some of the features are not needed?
- How close was the estimate?
- Is the project on track?
- Does the current architecture work?
Another key to improving project success is a reliance on automated testing. Manual tests just won't get run often enough because of the effort needed. Automated tests on the other hand get written once and run many times without an effort penalty. Test Driven Development is an excellent way to get a comprehensive automated test suite, and eliminate many side effect defects.
Agile Planning for Embedded Software
Using agile planning on an embedded project has additional challenges of non-embedded projects. These challenges include:
- Concurrent hardware software development
- Hardware dependent software
- Unclear hardware/software boundaries
- High deployment costs
These special problems that impact embedded developers are not a reason to avoid Agile. They are additional risks that the iterative model can help to contain. Good planning and feedback from iterations help to manage the risks found in typical embedded development efforts.
Embedded Test Driven Development
Test Driven Development is a key agile practice. In fact, it is one of the most profound practices of agile development. TDD offers a different way to program---a way that helps you keep track of where you are in the problem solving process and helps you avoid long debugging sessions. The TDD approach can provide a great advantage to embedded developers.
Test Driven Development is an technique for building software incrementally. No production code is written without first writing a failing unit test. Tests are small. Tests are automated. They work well with how humans work.
Instead of diving into the production code, leaving test for later; the TDD practitioner expresses the desired outcome in a test. The test fails. It may not even compile. Only then do they write the code, making the test pass.
Each step of the way, new automated unit tests are written followed immediately by code satisfying those tests. As the production code grows, so does a valuable suite of unit tests. With every code change the test suite runs, checking the new code's function but also checking all existing code for compatibility with the latest change. The tests provide assurance that the code, old and new, does what is expected.
Test Driven Development is not a testing technique, although you do end up with a lot of valuable automated tests. It is a way to solve programming problems. It helps software developers make good design decisions.
Test Driven Development is a powerful technique for building embedded software.
Let's compare TDD to the traditional way of programming, something I like to call Debug Later Programming. In DLP, code is designed and written prior to tests; when the code is "done" it is tested. We all know that mistakes are made during design and coding. The problem with debug later programming is that the feedback revealing those mistakes may take days, weeks or months. By then we have lost the context of the problem we were solving when the defect was introduced and the debugging starts. This inherently unpredictable activity can destroy the most carefully crafted plans. In comparison, with TDD we get immediate feedback of many of the mistakes made during design and coding. This immediate notification can help to avoid many of those unplanned and unpredictable debug sessions.
When we're writing code we follow a repeating cycle of small steps known as the TDD Microcycle. Each pass through the cycle provides feedback that our new and old code behaves as expected because it passes our tests. I've embellished Kent Beck's description of the TDD cycle in the following list.
- Add a small test
- Run all the tests and see the new one fail, maybe not even compile
- Make the small changes needed to pass the test
- Run all the tests and see the new one pass
- Refactor to remove duplication and improve expressiveness.
Each spin through the TDD cycle is designed to take only a few minutes. New tests and code are added incrementally with immediate feedback showing that the code just written actually does what it is supposed to do. We grow the system modules from simple roots to more complex behavior, learning more about the problem and the solution as we go. The tests and code capture our knowledge of the problem and our solution.
Running the tests with every change shows us if our recent change broke anything that used to work. In a sense, our code tells us when we break it! TDD can't work without automated tests. Tests have to be cheap to run and manual tests are expensive and error prone.
Embedded Test Driven Development is challenging. One of its big advantage is the that it provides a great way to test embedded code prior to having the embedded target hardware. We have experience in guiding development teams to adopting this profound technique.
Published: March 19, 2014
Here is a short interview with James about TDD and embedded software from the deliver:Agile conference last spring.more...
James participated on these social media platforms.more...
Do you have some time to do a simple programming problem in C or C++ for my research?more...
My long-time good friend (Uncle) Bob Martin and I have fun programming together firing tracer bullets for distributed water pressure measurement system.more...
Here are a couple reviews of our TDD for Embedded C training.more...