Just my type: how we use type annotations to build more robust code
Type checking is one of the tools we use to build software we’re confident will stand up to the unexpected
Imagine you have a child’s shape sorter. It’s a box with a few shaped holes on the top and a set of matching shapes. You can post each shape through its matching hole, into the box below. It doesn’t much matter about the colour of the shape, only that it fits. You can try to push the star through the square as much as you like, but it won’t work. If you do force it through, you might break something.
In programming, we have a similar concept when it comes to information, and we call this “typing“. Each bit of information we pass around in the program has a type. For example, a whole number is an “integer” and a series of characters in a row is a “string”. There are loads of types used to describe generic information, and we can build our own types as well for doing more specific things.
Much as with the shape sorter, though, if we try to push a type into the wrong-shaped hole then things are liable to break. You can’t capitalise a number, for example, or multiply a word. A good example of this crops up in handling user input. A lot of what we do takes input from web forms, and because of how the web works these always arrive as a string (of characters).
Beware the snowman
The programming languages we tend to use like to be helpful, and they might let us pretend that these strings are numbers when we try to do maths with them, especially if they look like a number. That works fine when a user inputs “1337” as we expect, but what about “banana”, or “beware the snowman”? On the other side, what happens if a user submits a phone number and we accidentally strip off the first zero because it looks like an integer?
Fortunately for us there’s a way to help ensure this doesn’t happen, and it’s called type checking. At its core, type checking involves making sure that every possible path through the code can only ever pass the expected type of data. If some part of code expects a string and there’s a chance it could receive an integer then we’ll be told before the code is ever run.
Some of the languages we use to build software at dxw, like C# and Kotlin, force us to do this as part of how the language is built. For others like Ruby and Python, some tooling wraps around the language to keep things in line. In JavaScript, we can (and mostly do) use a variation on the language called TypeScript and transpile it, checking types in the process.
Historically, though, Ruby, Python and JavaScript haven’t made use of type checking until the point things are actually run. This means we could ship a piece of code which was susceptible to the above “banana” bug, and we wouldn’t know until somebody tried to divide by “banana” and ended up with a division by zero error.
Building with types
So, how can we prevent this? It’s actually pretty easy, and it boils down to “use type checking where we aren’t already, and starting to be explicit about the types of data we’re passing around”. For Kotlin and C# this is a done deal, it’s simply not possible to write in those languages without being explicit about types and having those types checked at compile time. Similarly, for anything we write in TypeScript, the language forces us to declare types and have them checked at transpilation.
For Ruby and Python it’s a bit different, as these languages rely on having external tools like Sorbet and mypy to check that things are as expected. Typing in these languages is also optional, and we can add it gradually, in the process giving us extra confidence that things are actually as we expect. This is what some of our delivery teams working on existing code are doing at the moment – adding typing to existing code as it becomes relevant, as well as typing new code as it’s written.
Type checking on its own doesn’t guarantee bug-free programs, but it’s another one of the tools we use along with rigorous testing, automated pipelines and code reviewing to build software we’re confident will stand up to the unexpected.