Some time ago, I was involved with embedded software projects using microprocessors that had limited memory, speed, and I/O resources. All embedded programming was in assembly code. All memory variables were global; it was too hard to keep track of stack variables. The hardware design assumed that any technical problems for data acquisition and timing were just Simple Matter of Programming (SMOP). Most projects used just 1 or 2 programmers (we didn’t use the term “software engineer” in those days). Programming usually consisted of one big file that was written with a text editor and compiled/assembled with tools from the microprocessor vendor. That output would produce errors with limited explanation, so we’d go back and figure out what was wrong and try again. When we had an output that the tool didn’t complain about, we could download it to the prototype and use an emulator to see if the thing really worked. We thought we knew what it was supposed to do from the short text description in the project outline. If it didn’t seem right, we’d fix it… well, we’d change it, hoping to fix it, and hoping we didn’t break anything else. When we attained “critical mass,” we could show it to the marketing department, and they would say “oh, I thought it would do ‘abc,’ where is that function?” Well, nobody told us to make it do ‘abc,’ but we could fit that in. It was only a SMOP. Of course there was limited program space and if something had to be added, there was a chance something else would have to be deleted. The problem was that very few good requirements existed. In fact, some functionality was included just because a programmer thought it might be a good idea, not because of any product requirement. A passing remark by someone in marketing might change the functionality. Plenty of new features were added without ever revising a requirement specification.
As you might guess, this approach is fraught with problems. For one thing, you don’t know what functionality is really needed. There might be extra functions that no customer needs or expects, or worse, these extra functions will cause extra problems. For every requirement, there should be a validation test. But how do you test a function for which no requirement exists? You can’t be sure it works, whether it was needed or not needed; you don’t know how it will behave under stress. If there is one thing I’ve learned over the years, it is that a good set of requirements is the key to insure a product does what it’s supposed to do. It prevents the ‘gilded lily’ syndrome (extra features that shouldn’t be there) and makes sure that all required functions are tested. Of course the test effectiveness needs to be high, but a well-constructed and well-maintained requirements document will go far toward that goal.