What is released and is working in the live system, is what counts. Not what's written down.
A while back I had to do some Fault Slippage Analysis on a bug in high level component, ComponentA, in an application stack. A specific use case in our application had stopped working at a customer site after a delivery. Complicating the troubleshooting was the fact that there was no delivery from ComponentA, where the symptom of the problem was presenting itself.
The root cause of the bug was caused by a lower level component, ComponentB, tightening constraints on a method call that ComponentA was using. Sure enough the javadoc of ComponentB had indicated not to use the method in this way. However the method had worked fine up to the point of adding the constraint. There wasn't a good reason to enforce the constraint - it was a theoretical, artificial limit. However the ComponentB developer had changed the code, while fixing another problem in the same file.
There was a long argument between the two teams that handled each component. Eventually the system architect made the call for corrective action. ComponentA had to change because it had violated what had been documented in ComponentB.
Ultimately this added considerably to the lead time for the customer/user.
It is my view that the incorrect call was made. The correct call was to revert the change in the delivered component.
I arrive at this view by applying the following guidelines:
- Interfaces don't terminate dependencies
We also need to consider the run time behaviour of a component behind it's interface. This means that tightening constraints is a backwards incompatible change.
- Defer the released implementation to the latest moment
To be as correct as possible, you should implement it as late as possible, but no later. At this time you have as much information as possible to hand to avoid unforeseen changes. In this case the provider of the method didn't have a good understanding how clients were using the released method, or the effects their changes would have on client use cases.
- Open/Closed principle on components
Open to extend. Closed to change. Priority to working software at the customer side. Even if the opposite was documented on the API documentation.
Don't write or publish your interface until there is a client to use it. And then it's the client who dictates how it changes. In this case, when the original method was introduced, it was needed for a future release. However clients were forced to use it right away to run a constraints check. It was written too early for all the detail and tests to be written by the providing ComponentB, thereby creating risk for calling components.