An important tool for handling a huge class of potential bugs, and thus freeing a huge amount of brain power (and time, if you failed to proactively guard against the bugs and now need to debug), is the type system. There’s much senseless debate about weakly and strongly typed languages. My original stance was “if you haven’t figured out that strong typing in modern languages is far superior, you’re not ready for this book.” But then I spoke to a friend who is a skillful programmer, yet still codes in type-unsafe languages by choice[1]. After a conversation with him, I was convinced that it’s worthwhile to include a section on type safety in this book.
We all like noticing patterns, factoring out commonalities, DRYing up the code, abstracting complexity away. This is what type safety is about, to the extreme. I’ve heard arguments like “I don’t need type safety, I have 100% unit test coverage”. This is exactly the point. Every time you write a unit test, or a defensive programming blurb at the beginning of a function that verifies the input is correct, like so:
function foo(value) {
if (value === null) throw new Error("null not allowed")
if (typeof value !== "string") throw new Error("only strings allowed")
...
}
you are implementing an ad hoc type check. Surprise, there’s a decades-old tool that does this for you in an automated and robust way. It’s called the type checker. Instead of the above, you can write
function foo(value: string) {
...
}
and the type checker will ensure that nobody in your codebase tries calling the function with anything other than a defined string
value.
This catches a huge class of errors, and thus allows you to reclaim a huge amount of brain cycles that you were otherwise committing to both being careful about your call sites, the coverage of your unit test suite, and the quality of your QA. You also get documentation for free, and don’t need stupid comments (see no comments) that tell the reader about the type of expected values in an ad hoc way. It also does away with repetitive but meaningless-as-far-as-business-logic-is-concerned type checking code at the top of every function (see #distinguish-signal-from-noise TODO-LINK). And you significantly decrease fear of expansive changes to code. Without type checking, you or someone on your team would have been afraid of refactoring a function for the very well-founded fear that they could not be sure to manually update all call sites and fully test them. With type checking, you can and should refactor freely and proactively[2], so as to keep your code’s readability top notch at all times, keeping cognitive overheard low. Change the type of the function parameter, and the type checker conveniently presents you with the full list of call sites that need updating.
[1] He’s since adopted TypeScript, and is loving the additional safety.
[2] Yes, there are classes of problems that the type checker will fail you on, depending on the language. Like assignment-compatible interfaces in TypeScript, a byproduct of structural (versus nominal) typing, for example. This obviously in no way invalidates the huge benefit type checkers bring to the table.
Comments
Post a Comment