
Static typing will not save us from broken software - nickmain
http://www.drmaciver.com/2016/10/static-typing-will-not-save-us-from-broken-software/
======
pixie_
I spent 10 hours yesterday refactoring and adding features to a client side
enterprise application that is well over 20k lines of TypeScript over tons of
files.

Yesterday was a pretty productive day for me. TypeScript easily caught
hundreds of little errors as I coded and refactored which I fixed just a
quickly. Most of the time when I tested my changes things just worked. I
thought to myself every time TypeScript found something, 'wow I just avoiding
another runtime error, thanks'

Maybe people don't write or maintain client side software at scale. I don't
know. But there's so little cost to having static typing I don't know why you
wouldn't want it. I am easily an order of magnitude more productive with it. I
code faster, my code works first time around more often, other people can
understand my code easier, and my tests don't have to worry about validating
types so they can be more logic oriented.

There is zero chance anyone would of been able to be as productive as I was
yesterday if that code base was dynamically typed. Not even close.

~~~
Someone
_" …a client side enterprise application that is well over 20k lines of
TypeScript over tons of files […] But there's so little cost to having static
typing I don't know why you wouldn't want it"_

I don't think the OP would claim TypeScript is a bad idea, as, with its
relatively weak type system, it doesn't really suffer from _" if you find a
bug it’s easy and unintrusive to add a test for it, but may require a
substantial amount of work to refactor your code to add types that make the
bug impossible"_.

As an example of a strong type system, in Haskell code, the database access
layer will may types such as "ISO-8859-1 string of at most ten characters"

If it turns out that you want to store ISO-8859-2, changing that type can and
often will cause a cascade of necessary changes, for example because you're
calling a ISO-8859-1 to UTf-8 conversion function, and the compiler will not
accept that call to now take a ISO-8859-2 string, or because you are
concatenating that string with another ISO-8859-1 one. Then, you will have to
decide whether to convert locally or whether to also change the type of that
second string, etc.

Now, of course, each of those compilation errors becomes a bug if you just use
variable-sized byte arrays. Question is how much work you are willing to do to
prevent those bugs.

~~~
DRMacIver
> I don't think the OP would claim TypeScript is a bad idea

Confirmed.

TBH there are very few languages that I'd claim were a bad idea. The point I'm
making is not "Statically typed languages are a bad idea" it's "If you think
statically typed languages are going to make software correctness magically
better then you're going to be disappointed".

~~~
tome
You did actually claim that statically typed languages were _worse_ than
dynamically typed ones for correctness:

> My general experience of the correctness software written in fancy
> statically typed languages is not overwhelmingly positive compared to that
> of software written in dynamic languages. If anything it trends slightly
> negative.

~~~
DRMacIver
I might not say that the use of any language is a bad idea, but I'm definitely
going to say that basing a language choice on a general and uncertain
impression derived from my extremely limited, biased and anecdotal sampling as
an end user is a _terrible_ idea :-p

ETA: The point of that anecdotal impression was not "This is definitely true"
but "Given that this is my general experience it would be surprising if the
actual trade offs experienced in practice were such that static typing was
regularly a clear winner", which I feel much happier supporting on an
impression than "You should do this".

------
natec425
I've started to feel the same way. I feel like a lot of developers would be
better off in a dynamically-typed language because it is such common practice
to completely subvert the type system and there are such strong resources
available for improving testing practices.

My last job was in C#, and it seemed like so many things were stringly typed,
entirely nullable, or used something else to weaken the type system.

In addition to this failure to use the type systems, testing is becoming a
much more rich environment. I think popular resources on effective testing are
much better and numerous than popular resources on effective typing. There are
great books and tools around helping developers test their software more
thoroughly and efficiently. Comparing these testing resources with typing
resources, the closest popular book I can think of for typing is Design
Patterns, and I don't think most popular type systems are built with the
program correctness as a top priority.

I think rich type systems like haskell's will play more of a role in the
future, and I think current users of these systems do experience a benefit.
But if you have only ever done dynamically typed programming or used Java/C#,
you are probably going to improve your correctness more over the next year by
using a property based testing tool like propr (ruby), hypothesis (python),
fscheck (.net), scalacheck (jvm), or jsverify (js), spec (clojure).

~~~
d357r0y3r
> My last job was in C#, and it seemed like so many things were stringly
> typed, entirely nullable, or used something else to weaken the type system.

This sounds like C# as written by people that don't really know or like C#. If
you're stuffing everything into strings or a dynamic, you missed the point of
the type system in a very serious way.

> In addition to this failure to use the type systems, testing is becoming a
> much more rich environment. I think popular resources on effective testing
> are much better and numerous than popular resources on effective typing.
> There are great books and tools around helping developers test their
> software more thoroughly and efficiently. Comparing these testing resources
> with typing resources, the closest popular book I can think of for typing is
> Design Patterns, and I don't think most popular type systems are built with
> the program correctness as a top priority.

There is some overlap between problems that can solved by type systems and
test suites, but there are many problems that can only be caught by one or the
other.

It also strikes me as strange that people can't be bothered to use the type
system provided by the language, but they _can_ be bothered to maintain and
add unit tests for their programs. Correctness enforced by types is fused with
the code; if you get the types right, you should be all set. There's no
additional maintenance beyond making sure that the code compiles.

~~~
cle
There is a real cost to properly modeling your domain in the type system. You
have to conform with the constraints of the type system, there is higher
cognitive overhead, it can cost more up-front (which can cause missed
deadlines), etc.

Also, there are cases where using strings is more appropriate than using
enums. Where passing a blob is preferred over guaranteeing the correctness of
the blob's structure. Where "null" really is better than an Option type.

And conversely, there are companies who hire crappy developers or don't give
their good developers enough time to learn the idioms and best practices of
their tools.

~~~
KMag
"Dynamic Witnesses for Static Type Errors* ("or ill-typed programs usually go
wrong)"[0] by Eric Seidel et. al. discusses a prototype for a system that
searches for concrete examples of ill-typed inputs that break a program, in
order to aid novice programmers with type errors at compile time.

[0][http://eric.seidel.io/pub/nanomaly-
icfp16.pdf](http://eric.seidel.io/pub/nanomaly-icfp16.pdf)

~~~
dllthomas
That is _really_ cool! When will it be in GHC? :D

------
klodolph
I agree with the article. No silver bullets, just lots of regular bullets, and
static typing is just another bullet.

The main advantage for static typing from my experience are what happens when
you have projects above 10kloc with multiple maintainers, and maybe the
original devs have moved on to other teams. At that stage in a product's
lifecycle, static typing reduces friction and covers some cases that are hard
to test properly. Greenfield projects don't suffer from this problems for the
first year or so.

Refactoring is the major thing that's easier with static typing, IMO.

~~~
xenadu02
All tooling is much easier with static typing. Code completion, highlighting
mistakes as you type... features that are much more powerful in a statically
typed world.

I agree though: it's a tool in the tool belt. It also varies dramatically
across different languages and supported features which can make a huge
difference in the kinds of problems it solves for you. Lately I've been doing
a lot more modeling of problems with types when it makes sense... things like
measurement units and their operators to ensure you can't accidentally add
meters to watts. Or using unique ID types for each model so you can't
accidentally create a reference with the wrong ID.

The newer languages and updated standards (e.g. Swift, Rust, C++ with auto)
take a lot of the useless repetition of types out of the equation and feel
more like type annotations in a dynamic language. In the end I think we'll
mostly settle on hybrids. Some will be static by default but support dynamism.
Others will be dynamic by default but support static type annotations.

What won't change is that you can't scale transistors below a single atom and
we can see that endpoint on the roadmap. Performance improvements will not
continue at the current rate. One could argue we're already there given the
perf delta between CPU and memory or CPU and storage has gotten orders of
magnitude worse.

In a future where abstractions impose a real cost the optimization function
may change, especially for established businesses and processes. Hiring a
couple of Rust or Swift senior devs to rewrite your Python backend in a
statically compiled language, then applying repeated optimizations might be
worth the cost when you know it will be 10 years before a new CPU is released
and it will be 10-20% faster at best. Or if you imagine future CPUs require
exotic materials and 10x as much R&D to hit the same targets they become
10-100x as expensive.

------
spankalee
> the idea that static typing will solve all our problems, or even one
> specific problem: The ubiquity of broken software

Citation needed.

Most proponents of static typing, myself includes, have much more modest
claims: that is saves time spent by developers on smaller, more mundane
errors. That it makes refactoring easier. That it allows for better IDE
support like autocompletion and hover-over documentation.

Even if these advantages are modest compared to fixing "broken software",
they're still huge and worth the increasingly minor annoyances of modern
static type systems, many times over.

~~~
Veedrac
You can argue on "most" advocates, but not "certain" advocates. Since you
asked for a citation, how about the famous "Not relying on type safety is
unethical"?

[https://twitter.com/queertypes/status/513510398570795008](https://twitter.com/queertypes/status/513510398570795008)

------
wereHamster
> Software is broken because its not worth people’s while to write non-broken
> software

This is the premise of a large part of the article. And it is wrong. At the
very least a citation is needed. And even if there is a scientific source, I
disagree with it. I'd argue that many people do want to write non-broken
software, but they do not know how to efficiently use static type system, or
they don't know how effective it is. If all you know is Java or C then of
course you're going to doubt if a type system is worth at all.

> As static typing becomes more elaborate the scope of the bugs it can prevent
> becomes wider, but it usually comes with an additional cost: Build times.

I disagree. When you are working with Haskell, use ghcid and you'll have near
instant recompile times. I made a proof-of-concept of a web frontend in
Haskell, and it reloads with sub-second delay. That is comparable to a
JavaScript project which uses webpack and babel.

~~~
yogthos
You can disagree what you like, but the fact that a lot of software out there
has bugs and these bugs don't stop it from being successful clearly supports
the point the article makes.

If catching the types of bugs that static typing prevents resulted in reduced
costs of software development, you'd see static typing out-compete dynamic
typing. It's really that simple.

~~~
tome
> If catching the types of bugs that static typing prevents resulted in
> reduced costs of software development, you'd see static typing out-compete
> dynamic typing

Apart from network effects, inertia, lack of education and awareness, and all
the other confounding factors.

~~~
yogthos
The complete and utter lack of empirical evidence with regards to purported
benefits of static typing is the real elephant in the room here I think.

Considering how long both disciplines have been around, you'd think we'd see
some clear benefits by now. Yet, here we are swapping anecdotal evidence.

~~~
wereHamster
The disciplines may have been around for long. But the industry has poured
billions into educating people (students in universities) on C, Java, C++, C#.
Comparatively little has been invested into educating people how to use and
work with good type systems. You're comparing apples and oranges.

You can't ask the general developer population how they feel about static vs
dynamic typing. The result will be biased towards dynamic because that's the
only thing most developers know.

~~~
yogthos
Conversely, a lot of people have have used dynamic typing in languages like
JavaScript and Ruby. Their OO nature encourages creation of many types, while
pervasive mutability removes any possibility to do local reasoning about the
code. The situation is completely different in functional dynamic languages
backed by immutable data structures.

------
carsongross
Both proponents and detractors of static typing seem to miss its major
benefit: tooling.

Being able to hit . and get a reliable list of things I can do is worth many
millions of dollars to me.

~~~
DRMacIver
> Being able to hit . and get a reliable list of things I can do is worth many
> millions of dollars to me.

...and mostly is possible because of the many millions of dollars that were
thrown into developing it rather than the type system. Quality of tooling has
much more to do with the amount of money invested in it than whether the
language is statically typed.

But this isn't a post about static vs dynamic typing as I said right at the
top. It's a post about the effect of static typing on software quality.

~~~
heinrich5991
Code completion is a fundamentally harder (if not impossible) problem with
dynamically typed languages, as you might need whole-program and control flow
analysis to even get the types of local variables. This fact has nothing to do
with the money involved, it's probably rather another formulation of the
halting problem.

~~~
DRMacIver
> Code completion is a fundamentally harder (if not impossible) problem with
> dynamically typed languages

Code completion tools originally came out of Smalltalk. It's relatively doable
if you can attach to a running process that has dynamic access to your code.

So that's "not necessary". It's also not sufficient: Most statically typed
languages simply don't have this sort of tooling because it's very hard to
write, and advanced type systems make it even harder.

~~~
overgard
Most Smalltalks didn't really have great code completion (or code completion
at all), and when they did, a lot of it had to do with the verbosity of method
signatures removing a lot of ambiguity. Don't get me wrong, Smalltalk has some
really cool features, but they did it by having a workflow completely
different from static languages, and trying to imitate Smalltalk's environment
in a language not designed from the ground up for it (ie, python or perl or
whatnot) would be a lot harder. The fact that it's hard to write language
tools in general doesn't change the fact that it's way harder to write an IDE
for a dynamic language than it is for a static one.

~~~
DRMacIver
> Most Smalltalks didn't really have great code completion (or code completion
> at all),

Ditto most statically typed languages.

> The fact that it's hard to write language tools in general doesn't change
> the fact that it's way harder to write an IDE for a dynamic language than it
> is for a static one.

It doesn't, but the fact that there's nearly as much variation within
statically typed languages in terms of how difficult this is to do as there is
between statically and dynamically typed languages does. And this only gets
worse as we move to the sort of languages that the static-types-for-
correctness people advocate for.

~~~
overgard
> It doesn't, but the fact that there's nearly as much variation within
> statically typed languages in terms of how difficult this is to do as there
> is between statically and dynamically typed languages does.

I really don't buy this. Yes different languages present different degrees of
difficulty, but the ceiling is much lower for dynamic languages compared to
static ones. In dynamic languages, there are absolutely (very common)
situations where types cannot be accurately inferred (or inferred at all). If
I write

    
    
       def f(x, a): return x[a]
    
    

That's practically impossible for an IDE to do anything with.

The reason why not every static language has great tool support is more to do
with A) if the compiler was designed with hooks/apis for tools to use (like
clang and rust do), and B) if the language has enough popularity for anyone to
bother. I'm pretty sure if you take two languages of relatively equal
popularity, and compare the dynamic one to the static one, the static one
almost always has better IDE/tool support. For instance, lets take Swift vs
Ruby (they're next to each other on the Tiobe index). I've yet to see a Ruby
IDE that is much of an improvement on a text editor, whereas Swift is pretty
young and already has tooling comparable to much more mature languages.

~~~
Oreb
> I've yet to see a Ruby IDE that is much of an improvement on a text editor,
> whereas Swift is pretty young and already has tooling comparable to much
> more mature languages.

I've never worked with Ruby, but I do work with (and mostly enjoy) Swift. The
one thing I really don't like about Swift is that the currently available
tooling (both AppCode and Xcode) is _extremely_ primitive compared to what I
am used to from Common Lisp and Clojure.

~~~
overgard
Anecdotal data point of one guy's opinion, with no elaboration on reasons?
Convincing. I'm talking about WHY analyzing dynamic languages is fundamentally
undecidable in many cases without run time knowledge. Trying to do anything
other than heuristics on some of these things is about as decidable as the
halting problem. The only argument I've heard against this is you can have a
hybrid environment with a REPL so your dead code is live. Ok, but you can do
the same thing with static typing too.

------
smarks
A digression on Java compiler performance:

> Java's compile times are sorta reasonable but are apparently getting worse.

I'm not entirely sure what this is about. Could it have anything to do with
JEP 215? [1] There were some slowdowns in `javac` in Java 8, but these should
be fixed in Java 9. Note this has more to do with _type inference_ than with
Java's type system per se, though this distinction is of little difference to
most Java programmers.

In our experience with OpenJDK builds, the main performance issues have been
around redundant loading/parsing of library class files and JVM/JIT warmup
time. These have mostly been mitigated by JEP 199. [2] With the exception of
the issues I mentioned above, it's hard for me to see that static typing has
had an impact on the performance of Java compilation.

(Overall I agree with the main thrust of the article, particularly with regard
to the "set of complicated trade-offs." Personally I find static typing very
helpful, but it won't _solve_ software quality problems.)

[1] [http://openjdk.java.net/jeps/215](http://openjdk.java.net/jeps/215)

[2] [http://openjdk.java.net/jeps/199](http://openjdk.java.net/jeps/199)

~~~
DRMacIver
It might be based on false information - I'm a bit out of touch with Java
development. I'll remove it.

~~~
smarks
Thanks for the update. Of course if there are any performance problems, I'm
sure the javac team would like to hear about it, as this is something they do
pay attention to.

------
clusmore
I watched a talk by Erik Meijer [1] recently where he mentioned that an
implementation of a tuple such as (C# syntax mine, but illustrates the point)

    
    
      class Tuple<T1, T2>
      {
        public Tuple(T1 t1, T2 t2) { }
        public T1 Item1 => default(T1); // null for reference-types
        public T2 Item2 => default(T2); // null for reference-types
      }
    

is a perfectly type-safe implementation of a tuple that's completely nonsense.
I'm curious as to how you might actually encode into the type system that the
values that come out of Item1 and Item2 are actually the values you put in.

I would love to see tests at the interface level (or typeclass level for
Haskellers) rather than the implementation level, to enforce that all
implementations of an interface satisfy laws like this. In Haskell, this would
mean that you could enforce things like monadic laws which we all assume to be
true anyway.

[1]
[https://www.youtube.com/watch?v=JMP6gI5mLHc](https://www.youtube.com/watch?v=JMP6gI5mLHc)

~~~
dllthomas
If you require a constraint to be able to provide Default values (even if
every type in fact implements Default - c.f. Typeable) then for Tuple in
particular there is only the correct implementation (modulo bottom or hidden
side-effects).

~~~
clusmore
True, that's a good point.

Side note: you seem to be replying to all of my comments, are you following
me? :P

~~~
dllthomas
> are you following me?

Checking in occasionally - you've been steadily posting things worth replying
to :)

------
tome
> As static typing becomes more elaborate the scope of the bugs it can prevent
> becomes wider, but it usually comes with an additional cost: Build times.

This is the meat of the entire argument, and it's false, as you can verify by
running ghc with the -fno-code option. Type checking is extremely fast. It's
everything else that's slow and that has absolutely nothing to do with static
typing.

------
solatic
OP's argument, boiled down, is the following:

1) Behavior needs to be automatically checked 2) To check behavior, you can
either write tests or specify types 3) Tests are more flexible than the type
systems of popular languages (e.g. to write end-to-end tests) 4) Testing
purely dynamic languages is quicker than compiling statically typed code 5) So
prefer tests to type systems because agility

Honestly this is a load of bollocks and I'll explain why:

* When business execs throw out the old UI (old end-to-end tests) and replace it with a new UI (new end-to-end tests) and your new tests are failing because undefined, well, have fun and good luck! * The longer a system lives, the longer the test suite gets. So no, your developers are not going to be running the entire test suite at their desks, even if at the beginning of that project, running that test suite took all of half of a second. This is one of the reasons for continuous integration servers, actually, so that the massive test suite can be parallelized across a bunch of build slaves and run overnight while the programmer is at home or working on a different feature/problem. And then the difference in compile and start-up times is psychologically irrelevant because the programmer will still think "ok, I submitted the build to CI, time to go home/work on something else". * Good type systems, combined with proper IDE support and proper object-oriented design, do optimize for deletion by reducing the number of places in a codebase in which refactoring has an impact. It is incorrect to state that type refactoring should be some kind of massive refactoring affecting every file in the codebase every time you refactor. A refactor should either eliminate a given subtype, thus leaving the other subtypes unaffected, or add a subtype, where no-longer-common code/behavior can be factored into a new intermediate type. When the supertype needs to be changed, well, you still need to test those changes, but at least you only needed to make the change in one place, and not an indeterminate number of copy/pasted places in dynamically typed code which, you know, good luck finding them all.

Static typing is a practical requirement for long-lived, large systems. OP
makes way too many assumptions on the basis of small, short-lived projects.

------
notacoward
Interesting point about the relative value of typing vs. testing, and how too-
strict typing can crowd out testing. However, see the discussion under
pixie_'s comment about new code vs. refactoring.

Also, this makes me think about incorporating testing directly into the
language, which kind of brings us to Design By Contract and formal methods. It
seems to me that now would be a good time to resume active research on those
things, instead of all these exhortations to use magic type systems and magic
immutability and so on.

------
je42
> I’d expect static typing to win out for correctness if: [...] very large.

I would disagree. In very large projects, strong types don't scale very well
across repositories / deployable units.

A very large single repository, it may be ok. Except that refactoring types
will like break a lot of in-flight pull requests and branches.

