My Engineering Daybook: How not to Default
An Engineering daybook is a way to compile quick notes on a given day to help recall info that you want to always remember. The purpose of these notes is to share my “aha!” moments in quick articles which will be “duh!” moments for most, but hopefully helpful for others.
Conditional control statements are the staple of most applications. They are the building blocks of logic that help control how an application should behave. No matter the programming paradigm there is a form of control statements that are at play.
We cannot live without conditionals however we can unintentionally abuse them. This is especially true for the default case in a conditional statement.
My Issue
I recently was trying to debug an issue with some code that I had written. I couldn’t figure out why given a certain condition, I was getting back the wrong result. It looked something like this:
// some arbitrary item type getter
function getItemType(data) {
switch(data.type){
case 'type1': return new Type1(data)
case 'type2': return new Type2(data)
case 'type3': return new Type3(data)
default: return new Type1(data)
}
}
Helpful Note: The requirement was that if no type was specified it was because historical data assumed there was only one type, therefore it was to be treated as ‘type1’ now that there were multiple types. Can you can see the flaw in logic based on that requirement? (hint: what if a consumer of this function knows about a type that this code doesn’t know about? How can we inform the consumer that we don’t know about that type?)
Do you see the issue?
I had a default statement that could not tell me when an expected case was wrong and why it was wrong, it just passed me back the wrong data type. (What if the data contained a random type value that the application doesn’t know about, instead of just an empty type).
This completely violated Tip 38 of The Pragmatic Programmer: Crash Early. The idea is that we need to know when the impossible happens. In this case, I created a function contract that could not ensure “safety” (or save a few minutes of unnecessary debugging to see that there was a case violation).
Default Statement Principle
Therefore, Default statements should only reveal a contract violation and never create a side effect — anything that produces a potential undesired result.
// some arbitrary item type getter
function getItemType(data) {
switch(data.type){
case 'type1': return new Type1(data)
case 'type2': return new Type2(data)
case 'type3': return new Type3(data)
default:
throw new UnsupportedTypeError(data) // or return and assert
}
}
Or if I wanted to satisfy the requirement:
// some arbitrary item type getter
function getItemType(data) {
switch(data.type){
// case fall-through may be a good option here
case 'type1':
case undefined:
case null:
case '':
return new Type1(data)
case 'type2': return new Type2(data)
case 'type3': return new Type3(data)
default:
throw new UnsupportedTypeError(data) // or return and assert
}
}
If you enjoyed reading this, consider giving me a follow -> Follow Me