Static types won't save us from bad code

3 October 2019

Modern programming languages can be categorised in many ways, but the most common approach is to slice them by how they handle Types. Is using strong static typing a prerequisite for writing clean, maintainable code?

My development experience has mostly been with dynamically (and often weakly) typed languages.

Early in my career I worked on large scale PHP applications with millions of weekly users. If you’ve done any reading about PHP you’ve probably read "a fractal of bad design” and decided that the only thing to be done with the language is to nuke it from orbit – I can confirm that position has its merits.

Now that it's at version 7, PHP isn’t a completely terrible language any more, but it does still have legacy issues to deal with. And it’s still all too easy to write bad code in it.

This example below is not a bug in PHP. That's the way the language is designed.

<?php  $foo = 5 * "10 Green Bottles"; // $foo is integer (50)

You might remember that Steve McConnell told us that we should try to programme into a language rather than programming in a languagecodecomplete; and if we take that approach we can write good code in any language, even PHP. But let’s not…

At Instil we lean towards statically typed languages. We definitely favour strongly typed over weakly typed. One of my colleagues claims “If you slice me down the middle you’ll find the words ‘strongly typed’, like a stick of Portrush rock” – I'm a bit more agnostic.

But does it really matter? Is a statically and strongly typed language really better than dynamically and weakly typed one? Where are the benefits? Here are a couple of helpful articles that list some benefits on both sides:

Writing code that has some protections built in through its type system is usually a good thing. But those protections often come with a corresponding reduction in flexibility or readability. As the last post in the list above suggests, “static typing has a cost … there is no free lunch” and it’s up to you the developer to decide whether you need those protections.

Research suggests that static typing could prevent around 15% of bugs, so it definitely has some value. Tooling is typically better with static types too – compare the refactoring IntelliJ IDEA can do with Java against what PyCharm or PHPStorm can do with Python or PHP. And the more code you have the more protection you need, so large applications will benefit more from static typing that small ones.

But remember, we don’t just have type as a means to protect us from our own mistakes. We have other tools at our disposal.

Testing is fundamental — Remember that “untested code is broken code”. Good test coverage can more than make up for the lack of static type safety with regards catching bugs. This post by Bob Martin suggests “You don’t need static type checking if you have 100% unit test coverage.”

Pair (or mob) programming – your pairing partner(s) can quickly spot issues where you’ve assigned the wrong type. Or, if mob programming is a step too far for management, you can get a similar effect (albeit distributed and asynchronous) through code review.

Photograph of a group of developers mob-programming by working together around the one computer
Image source

Programming technique – by concentrating when working on the hardest problem in software development (naming things) you can save yourself future headaches. I’m not suggesting you should use Hungarian notation, but try adding semantic meaning to your variables, for example index instead of i, person instead of item

# Not
for i, item in enumerate(list_of_people):
    print(f"person {i}'s name is {item.name}")
# Instead
for index, person in enumerate(list_of_people):
    print(f"person {index}'s name is {person.name}")

Additionally, using lots of routines to encapsulate your code and hide the implementation detail can help you. This has two benefits: 1) you can limit the scope of your variables and so potentially avoid mis-using them elsewhere and 2) you can have a greater degree of confidence in your routine being bug free if it’s only a few lines in length. Our Engineering Best Practices course can walk you through refactoring to create intention revealing code.

So if we have good test coverage, regularly exercise pair programming, require rigorous code reviews, and practise clean code principles, do we really need to rely on static typing? I don’t think so.

And for me at least, readability is the clincher.

Let’s say I have an application that needs to make comparisons between custom data types. Let’s say we have a number of different types but they all match the pattern of a Pair. Each Pair in our system has two properties of varying type. We need to be able to pick out the largest item in a set of these pairs.

pair = new Pair(a, b) // where a and b are variables of any type

In Java, as long as the properties of my pair implement the Comparable interface I can write a method that uses Generics to compare any type. Generics allow me to write shorter methods, once, with safety. Without them I either need multiple implementations or risky casts or deep sets of switch or if/else statements.

private static <T extends Comparable<T>, U extends Comparable<U>> boolean isLarger(Pair<T,U> p1, Pair<T,U> p2) {
    if (p1.getFirst().compareTo(p2.getFirst()) > 0) {
        return p1.getSecond().compareTo(p2.getSecond()) > 0;
    }
    return false;
}

What is this muck?! This code is hard to read. Perhaps the getFirst() and getSecond() methods don’t help, but it’s all the <T extends Comparable<T> gubbins that really gets in the waykotlin.

Here’s the same method written in a (better) language (Python)typeerror

def is_larger(first_pair, second_pair):
    return first_pair[0] > second_pair[0] and first_pair[1] > second_pair[1]

You can see that Python's nature as a dynamic language allows me to keep my code really clean. I don't need to jump through hoops to support variables of different types in my functions the way I would in Java. And its strong type support protects me at runtime from the weirdness that PHP and Javascript exhibit.

But I still need to write better tests!

(Keep your eyes peeled for a response to this blog post from my colleagues. I'm pretty sure I won't get away with besmirching the good name of statically typed languages for long.)

Featured Photo by Ravi Singh on Unsplash, and it's (obviously) a reference to Duck Typing


  1. I can't find an online reference from Steve on this, but you can read more about programming into a language on this medium post or buy Code Complete itself

  2. I can write it in Kotlin, which helps a bit, but the method definition is still a problem.

    private fun <T : Comparable<T>, U : Comparable<U>> isLarger(p1: Pair<T, U>, p2: Pair<T, U>): Boolean {
        return (p1.first > p2.first) && (p1.second > p2.second)
    }

  3. This code will throw a TypeError at runtime if I call it with pairs that I can't compare (for example dates and integers) so if that was a possibility I'd need to add some additional checks around this code.

Article By
blog author

Ryan Adams

Software Trainer