During the midst of a recent launch preparation mini marathon I had the chance to delve into some of the more abstract aspects of software development with a colleague. While investigating a bug report we found an incorrect query which led to the wrong record being returned. I told my colleague that because the fix had changed the function's interface we needed to look at other uses of the function to ensure that a new bug hadn't been introduced. He was a bit confused and mentioned that we hadn't changed the arguments to the function. To him, interface meant the API, ABI, OO and function signature definitions and since we modified none of these we had not changed the function's interface.
One of the best practices a former mentor hammered into my head was that you never change the interface of a function. For this definition interface added the more contractual "what the function guarantees that it does" to the typical definitions. This rule was meant to help minimize the consequences of some of the universal rules of software development:
- All code has bugs
- Code which is changed by tired developers pushing for a release is even more likely to have bugs
- The more complex the system, the more likely it is that fixing one bug will introduce another. When this reaches a critical mass developers start talking about complete rewrites.
In particular, following this rule would help avoid new bugs being introduced while fixing existing ones. Part of the problem is that the guaranteed behavior of a function can be pretty vague. Side effects are often not documented, bad function names and comments leave open the question of what exactly the function claims to do and eventually code will be written that depends on what a function actually does. Design by contract programming and test driven development are approaches which help codify the guarantee of a function. While they can't entirely prevent the introduction of errors they can greatly reduce the chances of it and help keep it from going undetected until a customer finds it. They also help reduce developer or QA time checking to see if a new bug has been introduced by getting the computer to help do it for you.
In the end, my colleague checked uses of the function, confirmed they all desired what it claimed to do, and fixed the bug restoring order to our tiny little section of the universe.