TDD and design

Earlier I wrote about the long distance I went with mac driver design (link). The current design is sketched below with examples of functions, and responsibilities (green). The clouds are C files. The design has 95% unit test line coverage. Tests have proven their power. I refactored the code using a local repository while traveling on vacation on remote island (during off-days from diving) and obviously no access to real target for testing. I made 53 commits. My commit frequency is very high, so many of the refactorings were just renaming and extracting helpers, but there were also more fundamental design changes. When I finaly, and sadly, made it back to the lab, I was kind of afraid that the code won't run and the fastest thing to do is to throw away all the refactorings. The next fastest thing would be to repeat them one by one in real repository. I gotta say I was surprised when the code worked right out the cross-compiler and all I needed to do was one massive merge from local to real repository. This is very rewarding. During the refactoring there was a handfull of incidents when tests caught a stupid mistake made by me. This is worthy even if you had the access to real target. The nice thing is that unit tests on dev environment tell it right away.
There is no need to make tradeoffs and large/long changes without feedback because of lengthy burning times, or lengthty stepping path to debug newly written code.

But it wasn't the biggest learning. The biggest learning was that the design resulted quite the different from what one would expect. I base this claim to investigation of several example MAC driver source codes available in the internet. The design has proven to be good, in terms of testability (that was the driver) and adaptability (this was the proof). More about adaptability later below. The thing that differentiates this style of design is the emphasis it puts to testing. Design is good if it is easy to test. If the tests are complicated to understand or difficult to write all together, then there is a good chance that design has flauses.

I think what I experienced is well explained by Michael Feathers in his talk "The Deep Synergy Between Testability and Good Design" (video). Take a look, and don't think this applies only to OO languages. You'd be wrong. The driver we are talking about here is written in C.

Current design was tested when the hw team decided to have a second option for MAC driver. They wanted the final pcb so they could proceed with emission tests and we together did not have enough information to do the decision either way. The candidate for production pcb has routing for both options. One implementation of set based design. But back to the sw side of it...

The concepts in driver design kept most of the files completely untouched. SPI and DMA drivers were independent compile units and they needed no touching. This also means that no code was duplicated in the production code mass.

The original design was done with just testability in mind. At that time there was no knowledge about the extra hardware the design needs to comply with.

In my opinion the code became adaptable and reusable by designing it for testability.

I don't think writing code this way, by seprating concerns, focusing on single responsibility, and not mixing abstraction levels, is slower to write. Is it different? Oh yeah. You have to really develop a new sense for good coding. As a remark, which I did earlier, I wouldn't refactor the code after fiddling around, but write a decent design based on learning from exploring, aka spike.

In the current MAC driver code one detailed design decision may make you raise your eye browse; Single function ClockByte() is in separate file. This is because I wanted to assert through just the bytes been send, not through processor register dummies. Other option would have been to inject a function pointer for this. I chose to use link time seam.

On the other hand just few simple tests for basic correctness of ClockByte() function are enough. This can also been seen as principle of separation of concerns and keeping the files at the same level of abstraction.

Current design is far away from being perfect. It is not what I think should be achieved. It continues to offer me opportunities for deeper understanding of tdd, and synergy between design and tests, more deeply. The next lesson will be available when it gets factored to enable irq based interfacing between uC and mac driver. So far it has been just message polling.


Slides from Agile 2011

Agile 2011 ended in Salt Lake City, Utah a few weeks ago. It was again great to meet the growing circle of friends in agile community. This year my own conference was a bit different as I submitted two talks and participated in the review process for Agile for Embedded Systems Development -track.

Indeed, we had dedicated track for embedded stuff. It was the 10 year anniversary for agile manifesto and this was the first time the embedded got this much attention. The track had quality sessions and averaged around 20 attendees for each session.

You can find my slide decks via the links below:

Embedded Testing Cycle - the First 3 Years, Markku Åhman and Timo Punkka

James Grenning presented the embedded test-driven development (TDD) cycle already in 2004. Whispers on the hallways of conference hotels tell that somebody is actually implementing this idea. However, there are only few documented implementation details available. Schneider Electric’s fire security team has been implementing TDD cycle as an integral part of the development process for 3 years. Come to learn from their real-life experience and mistakes in automated testing at different levels: unit testing, acceptance testing using simulation, and in real target hardware.

Agile Hardware and Co-Design, Timo Punkka

Agile software development is getting attention also in embedded software development. Embedded system development on the other hand requires different engineering disciplines working together. When embedded software team starts using agile methods, it affects also other disciplines. Agile development emphasizes continuous learning through experimenting and collaboration instead of following a detailed up-front plan. Agile embedded software team expects different behavior in system co-design. This talk discusses reasons and ways to adapt agile development to co-design of system development.

Lots of other presentations, including all other embedded track presentations, are available via the conference program site.


A New Home

A fiend of mine did fantastic job on my new site. My fuzzy ideas and his zero-whining attitude made a good pair. It was truly an agile project. The site was up and running from day 1, and it emerged during number of customer-developer pairing sessions. Final outlook is a result of countless "you know, why don't we just try it"s.