This is a cross post of my blog in biosdevelopers.com.
I had seen lot of BIOS rewrites in my professional life. I am specifically talking about PC BIOS. We all started with uncompressed BIOS image, then moved to compressed image to fit in existing flash (EEPROM at that time) chip. Then we need to move the BIOS out of x86 segment limitations. There was a rewrite for new technologies such as ACPI, USB etc.
Every time we rewrite the BIOS code base everything seems to be fine and clean. After couple of years, the code look ugly with lot of patches here and there, new features are poured over without proper code rearrangement etc. By this time the original developers/designers of that code will not be in the project. They (rather we) always look at the code and say.. Oh.. it is not our fault we did provide a clean proper solution it is the new Engineers who ruined the code; they don’t know what they are doing!
Even though it is true in some sense but from Robert C Martin’s foreword in the book ‘Working Effectively with Legacy Code‘ by Michael C Feathers:
“Designs that cannot tolerate changing requirements are poor designs to begin with. It is the goal of every competent software developer to create designs that tolerate change”
Apparently the original developers (that includes me too) are not ‘competent’ enough. I started seeing similar pattern in the EFI/UEFI code base already. Backward compatibility, newer chipset support, new protocols, new specifications etc are rapidly ruining the original code base. In many cases, the original design goal are not met anymore as original Engineers moved away and the design spirit didn’t distilled down to new Engineers! I see a serious problem here!
How to fix it? One common suggestion will be better documentation! Well, frankly how many people really read documentations, really? I think better way to handle this will be to incorporate proven software Engineering technologies. Coding standards should include Software Engineering technologies like Test Driven Development, Defensive programming etc, in addition to what to capitalize and how long should be the variables.
One basic thing that can help is unit testing. I used to say.. unit testing in the BIOS - that’s impossible! We need actual hardware to test the code nothing can replace the actual hardware! This is true.. but we can simulate most of the condition even before hardware arrive using mocks. Already we see an advantage here - Time-to-market is reduced since we don’t need to wait till hardware, we can already test our code!
What are unit tests? Unit tests are tests in isolation. They are not big tests. They run complete in fraction of a second and test just a procedure. They localize the issues. If your test is for more than a procedure than it basically not an unit test.
How are they useful? Think about a procedure written with an unit test. The test that verifies various inputs and outputs in a contained fashion. Later another Engineer wants to change this procedure, he/she can do the change and run the unit test (normally unit tests are run as part of build process) to make sure the original functionality is kept intact. Also from the unit tests she/he can understand what the original intent of the procedure. This ‘original intent’ helps a lot in future code updates!
There are lot of benefits I can say about unit tests.. let us keep that for our next post. Also I will write more about what tools are available for unit tests and how to write better unit tests in my future posts. If you’re impatient read the book and check out cMockery.