Bob and Sarah are both programmers working on the same project. This project interacts with a popular HTTP service: “WibblyWobbly.”
The WibblyWobbly API is designed for simplicity. Interacting with Wibbles and Wobbles, the two main objects in the WibblyWobbly system, looks identical:
Being good programmers, and wanting to follow best practices like the Single Responsibility Principle, Sarah and Bob decide that they should separate the code that makes HTTP requests from the application code that accesses that data.
In designing their service client, they decide to make more explicit the difference between Wibbles and Wobbles. Rather than have more generic methods:
WibbleWobbleClient#get(object_id) WibbleWobbleClient#update(object_id, attributes) WibbleWobbleClient#delete(object_id)
They implement the following interface:
WibbleWobbleClient#get_wibble(wibble_id) WibbleWobbleClient#update_wibble(wibble_id, wibble_attributes) WibbleWobbleClient#delete_wibble(wibble_id) WibbleWobbleClient#get_wobble(wobble_id) WibbleWobbleClient#update_wobble(wobble_id, wobble_attributes) WibbleWobbleClient#delete_wobble(wobble_id)
Each of these methods looks different, but really ends up calling the same URIs for similar actions. This makes sense, because the client is attempting to provide an appropriate abstraction to the application, but at this point in the process they are just trying to get something working with the least amount of effort.
A long time from then, in a funding round far, far away…
Bob notices that their application, SuperWibbWobblizer, is deleting Wobbles instead of Wibbles. When he begins debugging, this seems impossible:
“Even if the ids were somehow mixed up, there are totally separate methods for deleting Wibbles and Wobbles!” he says to himself, “If that were the issue, I’d expect the client to throw an exception.”
After hours of frustrating pair programming, Sarah remembers that tucked away inside their client library, the URLs for deleting Wibbles and Wobbles are the same. This means that their previous assumptions about the behavior of their service client were incorrect, and it has been blissfully passing through Wobble IDs as Wibble IDs.
Defend Against Lies of Omission
Bob and Sarah realized that this situation could have been avoided in different ways. They could have documented that the client was a simple pass-through that provided no validation or taken the time to implement those checks up front. They also realized that they did no negative testing along the way, at any level, to ensure that they were protected against this situation.
Building software is about choosing between tradeoffs. In Bob and Sarah’s case, simply documenting that no validations were provided would have taken a trivial amount of time, and would have at least prevented confusion when they were debugging the issue. This may have been the way to go for them.
The lesson here should be to think ahead when designing an interface of any sort. When you name any method or function, you’re defining an interface to a future engineer. Think carefully about what your class, function or variable name might convey to a future engineer reading your code. Weigh the cost of coding defensively against a strategically placed documentation comment.
Empathize with that frustrated future engineer. When they finally realize the miscommunication that has taken place, they may just think,
“Dude. Don’t lie to me…”