The late Joe Armstrong had a great rule for error handling that stuck with me.
‘If you can’t do what you want to do try to do something simpler’
I was thinking about this recently. I was writing code to embed resource files in an iOS application binary. Here’s my first draft:
let embeddedDir = OkioPath.companion.toPath(
Bundle.pizzaResources.resourcePath!,
normalize: false
)
The !
in this sample applies Armstrong’s rule:
- Attempt what I want: Try to load the resource path.
- I can’t, because the
resourcePath
is nil. - Try to do something simpler: crash the app.
The iOS developers I work with don’t like !
, or anything else that could crash the app. When I get my Swift experts to review code that uses !
, they always recommended I replace !
with if let
for a graceful recovery.
let embeddedDir: OkioPath
if let resourcePath = Bundle.pizzaResources.resourcePath {
embeddedDir = OkioPath.companion.toPath(resourcePath, normalize: false)
} else {
// Fail silently. We won't find the resources, but we'll keep going.
embeddedDir = OkioFileSystem.Companion.shared.SYSTEM_TEMPORARY_DIRECTORY
}
My peers and I value different things.
I want code that fails fast. Not being able to load resources should never happen. If it does the system is in an unexpected & untested state. We should not silently recover.
They want code that doesn’t crash. Every day as a software engineer I see things that are never supposed to happen. Why should I have the hubris to claim something will never happen?
A hazard of mobile apps is that deploying fixes takes forever. I’ll be in big trouble if Apple ships an iOS update that changes the API. I have to fix the bug, an app reviewer has to approve it, and each of my users needs to get the update.
Ugh.