Runtime errors suck. But when you're working in JS they're difficult to avoid. Fortunately though, our whole deal is problem solving; so avoid them we do.
For client-side JS this seems totally necessary: We shouldn't subject users to runtime exceptions; we should be giving them appropriate feedback in the event of an error.
But do we always want to avoid runtime exceptions at all costs? I'm not convinced.
In a perfect world, we'd have an equivalent to Elm's compiler in every language. But in the real world, we can save ourselves some time when things actually do go wrong.
Take this, as an example:
import someModule from 'someModule';
const {
someObject: {
someFunction,
} = {},
} = someModule;
Let's assume that our code is being transpiled with Babel before it's deployed. In this instance, if someObject
didn't exist in someModule
, then this would transpile fine. But at runtime, someFunction
would be undefined
.
Uncaught TypeError: someFunction is not a function.
It seems like this code is destined to fail for one of our users.
Consider if we'd done it this way instead, without the default value in our destructuring:
import someModule from 'someModule';
const {
someObject: {
someFunction,
},
} = someModule;
Now, if someObject
doesn't exist in someModule
we'll get a runtime error when trying to transpile instead of after it's been deployed.
Uncaught TypeError: Cannot destructure property `someFunction` of 'undefined' or 'null'.
Not only is this feedback much faster, but it's also going to fail on our machine. This particular example can only even happen in one place in any given file, which improves our ability to locate the problem quickly. With any sort of automated build pipeline in place, this error now can't even possibly make it to production any more. Not bad considering that all we did was remove three characters.
This example isn't indicative of every possible problem we can encounter in JS, of course. But this was a real example that I saw recently. It was the direct result of an over-zealous approach to preventing runtime exceptions: something that the original code didn't even do.
TL;DR: We ought to spend a lot more time thinking about how and where our code can fail, and we should be very careful to consider unintended consequences we introduce by trying to protect ourselves from errors.
Top comments (3)
Awesome! If we see the possibility of a run-time error possibly occurring, then that is the time to discover the appropriate measures to make it impossible. Whether through delegating errors to the compiler (best case) or making the error impossible through design (completely re-arranging the scenario so the error can never occur). Thanks for sharing this gem!
Thanks for taking the time to read it. ❤️
Yep, completely agree. I've been guilty in the past of trying hard to avoid my code "falling over", but now I really want to see it fail as quickly as possible if I've actually made a mistake. And crucially, I don't want end users to suffer from my typos. 😄
For the longest time, my issue with catching anything ever was silent failures, which I experienced in other people's code.
But now, I just:
And life is good.