
To type or not to type: quantifying detectable bugs in JavaScript - ff_
https://blog.acolyer.org/2017/09/19/to-type-or-not-to-type-quantifying-detectable-bugs-in-javascript/
======
dpratt71
As we have recently adopted TypeScript, this is a nice affirmation, but
preventing bugs is only part of the benefit and I'm not sure it's the most
important part.

TypeScript, in conjunction with a code editor that supports it, significantly
improves the code editing experience. Sometimes I still have to chase down
documentation, examples, etc. to figure out how to use something, but it
happens far less often. In general, I feel like I can code faster and with
more confidence.

I assume all this is also true of Flow, though I've never used it.

~~~
logicallee
The title is, provocatively, "to type or not to type."

The top of the article makes it clear that it really, really, really will
answer whether we should type or not type. From the top:

>This is a terrific piece of work with immediate practical applications for
many project teams. Is it worth the extra effort to add static type
annotations to a JavaScript project? Should I use Facebook’s Flow or
Microsoft’s TypeScript if so? Will they really catch bugs that would otherwise
have made it to master?

>TL;DR: both Flow and TypeScript are pretty good, and conservatively either of
them can prevent about 15% of the bugs that end up in committed code.

In this comment I will address wherher this is really enough to deliver on
that promise:

Why is bugs a metric instead of bugs per programming hour, or hours of
programming including fixing bugs, with or without typescript?

Typescript adds types (and requires programmers to keep them in mind.)

I think it's hardly controversial to claim that typed programs have a lower
bug count because type errors are caught. It is also not controversial to
claim that programming time is slowed down, because of the need to think of
types.

The question is: how much?

If a programming language slowed everyone down by 5x but resulted in 75% fewer
bugs, few people would choose it. Most people would choose bugs.

If a programmer language was 100 times faster and only resulted in 5x as many
bugs (so, instead of 100 times base rate, 500 times base rate) then I and
practically everyone else would always choose it for almost everything. After
all if it's 100x more productive you can take 80x productivity gain and
sacrifice 20% of your programming wall time to spend on debugging.

So real results around speed _and_ bug count, as well as the insidiousness of
bugs, are crucial.

If the bug count were not increased in comparing a and b, but the second
language had showstopping bugs that took 100x longer to find and fix, nobody
would choose b.

Bugs aren't just a number.

~~~
RyanCavanaugh
> Typescript adds types (and requires programmers to keep them in mind.)

Programs without TypeScript also require programmers to keep types in mind -
it's just that those in those programs there's no way for a computer to verify
that the programmer got the types right, and no unified way for a person to
write down the types involved at any given point in their program.

~~~
shakna
> Programs without TypeScript also require programmers to keep types in mind

Not types, as such, but usually generics.

JS is hard to talk about in this context because of it's weak typing and
desire to type cast just about anything. So the comment may well be true for
JS, but I wouldn't be certain of it, it certainly wasn't the case when I was
working with CoffeeScript to avoid the common pitfalls of JS.

But, for most dynamic languages, if you'll allow to stretch things a bit, I
don't need to understand precisely what I'm feeding into the function.

Instead of asking "is it a string?" or "is it a vector?" or anything else
along those lines, usually I just need to ask "is it iterable?", if so, good
enough for me.

This can ease refactoring, when you need to change to a new type, because if
the interface provides the same mechanism, you don't need to change anything.
If it doesn't, then it may be possible to provide the interface, rather than
modifying the function.

\---

Things can get more complex than this, however.

Most people think of dynamic and weakly typed languages as the same.

However, there are several strongly typed, dynamic languages. Often times they
would not allow you to pass the wrong type to standard functions.

Some dynamic languages have guards and contracts to ensure types - and some of
those guards and contracts are... "compile time"... for lack of a better term.

That gives you optional typing, that you can add as you feel the need to
tighten up your code, whilst still allowing you to be as dynamic as you feel
appropriate.

------
kerpele
My team is in the process of switching a React app to TypeScript and an
interesting issue popped up just today.

I was refactoring some component to be able to implement a new feature. At
some point of the process I managed to break the component so that it would
not render, only complained about an object not being a proper React component
and that I was probably trying to render a bunch of items that should be put
in an array. The error was reported by something deep inside React so I
couldn't tell from the stack trace where the issue was. After trying to find
the problematic bit by trial and error (lots of undo/redo there) I finally
decided to just move the whole file to TypeScript. 20 minutes later I was done
and the error was gone. I don't even know what I did to fix it but obviously I
had to change quite a few things here and there to get the file to compile
cleanly with decent typing and one of those type errors must have been the
key.

------
sfvisser
Type systems aren't just about adding static type annotations to your
otherwise dynamically typed code to catch a few bugs.

If you embrace the type system and start modeling your domain types with the
natural invariants in mind you start seeing the real benefit. Once you enforce
the invariants of your data in the types you'll notice entire categories of
bugs will impossible to introduce. Abstraction barriers between different
parts of your codebase will become more clear and impossible to break.
Otherwise complicated architectures will become easy to express.

Types change everything.

~~~
kybernetikos
> If you embrace the type system and start modeling your domain types with the
> natural invariants in mind you start seeing the real benefit.

Except that few type systems have the capability to do this in a reasonable
way that doesn't lead to horribly contorted code full of type-induced damage.

Just look at the difficulties that people have trying to express code at a
higher level of abstraction like partial application, currying, generic
composition, etc (as in fantasy land or ramda) in typescript.

If you're acclimatised to working in a specific type system, you'll probably
never notice the areas where it stops you legitimately abstracting.

> entire categories of bugs will impossible to introduce.

This is true.

> Otherwise complicated architectures will become easy to express.

This, I think, is false. In fact many complicated architectures (depending on
your type system) will become impossible to express, and to some extent this
is a feature rather than a bug.

~~~
skybrian
"Impossible to express" seems a bit much, but you might have to write your own
Any type.

------
pron
It's great to see the wealth of data we have thanks to open-source being used
for such studies, and we should have more. More concretely, I can see why
actual benefit could be either greater or smaller than the reported result:

 _Greater_ : One of the advantages of types is that they impose a certain
discipline and organization on the code. This global quality may have a
significant impact that isn't measured in this study, which focuses on a very
local effect, and so the actual effect may be significantly greater.

 _Smaller_ : Not all bugs are created equal, and there's actually a huge
variability in both the effort bugs require to fix and in their impact on
total product quality. If the 15% reduction is mostly in "cheap" bugs, then
the actual effect may be significantly smaller.

We should try to create a taxonomy of bugs, classified by kind, domain,
project size, cost to fix and impact. This would help us get a better picture
of the overall effect of various techniques.

------
flavio81
This premise of many bugs being "type errors" is overblown and certainly very
recent, for i've never heard such a claim on the internet until only recently
(1 or two years ago). And the world was using dynamic languages for decades on
the internet (old ASP, old Actionscript, old JS, PHP4 anyone?)

If the 15% of your bugs are simple _type_ errors, then I'd suspect the quality
and experience of your development team.

The big white elephant in the room that javascript users don't want to
acknowledge.

Mind you, static type checks are nice stuff, but to claim that 15% of bugs are
type bugs, is another thing altogether.

I speak from experience, having managed development teams for years. Junior
coders need (and deserve) to be _trained_ first doing stuff without hurry to
allow for mistake, not used immediately or thrown into critical (or rushed)
projects.

~~~
rbehrends
One problem here is that Javascript eats a lot of type errors silently.

    
    
      $ node
      > 1+[]
      '1'
      > 1-[]
      1
      > 1*null
      0
      > 1*undefined
      NaN
      > x = {}
      {}
      > 1+x.a
      NaN
    
    

This is not something that other dynamically typed languages necessarily do.

~~~
flavio81
_This is not something that other dynamically typed languages necessarily do_

Yes, I agree, and this is because Javascript is very weakly typed, and sadly,
no tool like Typescript will go so far in counteracting this, because the
runtime itself (JS) is still weakly typed.

So... no real substitute for good old runtime testing, i'd say.

~~~
neurotrace
Except that a strong static type system wouldn't allow you to write code like
this in the first place. You wouldn't hit these issues at run-time because you
caught them at compile-time.

~~~
mikewhy
Say you have an API endpoint:

    
    
        router.post('/users', (req, res) => {
          const body = req.body as MyEndpointInterface
        })
    

If someone posts to that endpoint with the wrong interface, you're still going
to have problems. I'm not sure of any language that would catch such a thing
at compile-time and not need some sort of test.

------
koblas
The next 5% of bugs can be taken care of by a handful of goodl tslint rules.

\- missing awaits

\- using 'this' in the wrong context

\- shadowing variables

Those errors make for valid code that's wrong in many cases.

~~~
camus2
it doesn't catch type coercion, missing properties, ... a whole class of bugs
that don't yield runtime errors.

------
revelation
And it prevents 100% of the time wasted on testing something in a browser (or
generally at runtime) only for it to fail for some trivial type mismatch.

These things don't typically make it as bugs into production code but are a
big time sink with dynamic languages.

~~~
marcosdumay
I would really like a "strict mode" on my browser developer tools that would
cause any problem in Javascript to stop everything on the page and write a
huge "Hey, your code can not run because it had a parsing error at this line!"
at the console.

~~~
kitten_mittens_
Eslint and a Webpack dev server would have you covered in that regard.

------
m12k
As someone whose background is mostly in statically typed languages, who has
been using Ruby and Rails for the past year, the biggest issue for me is when
refactoring/making big changes to central parts of the app, the many
repercussions of these changes often take a long time to track down, and
subtle bugs are often introduced. By comparison, similar changes in a
statically typed language are are usually much less scary, and less prone to
having subtle breakage going unnoticed. A big test suite helps, but it's not a
replacement for all the help a static type system will give you.

~~~
christophilus
I'm in the same boat. Mostly C# for 15 years, and now Rails. I've seen bugs in
production that were non-trivial to track down that a static type checker
would have caught. Refactoring is tough, not just because of the lack of
static typing, but because of implicit dependencies, crazy and inconsistent
ways to handle and invoke method messages, etc. In short,I'm not a Ruby fan...

------
Rapzid
I feel like I'm on a completely different wavelength with those who offer up
"full test coverage" as a desirable alternative to a type system's assistance.
Or a desirable situation period.

I'm all for tests as a tool, but not full coverage as its own goal. One of my
favorite tools is Visual Studio Code. As far as I can determine from the
github repo it is very far away from the full coverage end of the spectrum.
And yet, it works pretty darn good. Maybe I just haven't found the unit test
hoard, but it appears they have taken a more strategic, moderate approach.

------
mattferderer
I'm a big TypeScript fan but at times it just doesn't work & needs to be
ignored. Like all things, you get to a point when you know what rules are okay
to ignore. You can't do default props in React very nicely for example.
Functional programming like pipes can also be a pain.

That said, TypeScript (and Flow) offers other huge benefits than the 15% of
bugs mentioned.

* Improved documentation

* Better code hinting in the editor & better editor experience

* Teaches better JavaScript & prevents browser bugs. Some browsers are very forgiving when you incorrectly use JavaScript with DOM elements. Others are not.

Types slow down beginners a lot but that's a good thing. When learning a new
language, a type system can act like a pair programmer or a teacher helping
guide you in learning the types of the language. Each type has things you can
& cannot do. People who learned JavaScript using JQuery & then try to type
JavaScript without JQuery run into many of these issues. After using types for
a while, the time investment is very minimal especially compared with having
to come back & find the error later.

~~~
bpicolo
Doesn't seem overly tricky?
[https://stackoverflow.com/questions/37282159/default-
propert...](https://stackoverflow.com/questions/37282159/default-property-
value-in-react-component-using-typescript)

~~~
mattferderer
I've done that method & maybe I've missed something but I get a ton of "object
is possibly undefined" warnings then.

------
noncoml
I actually use Typescript to speed up development. Having the type-system
looking over your shoulder is great for reducing the code-test cycle.

------
pier25
The TC39 has to step up their game and introduce static typing to JavaScript.

I don't buy the bugs argument, but I firmly believe it would make the
development experience considerably better. Not only because of tooling, but
specially because code would be more way more expressive.

Also in my almost 20 or so years writing Javascript and other dynamic
languages I've never once changed the type of a variable. Changing a var from
string to int or object is just wrong.

~~~
yorwba
> ... I've never once changed the type of a variable.

I only do that for functions that accept multiple types (accept liberally and
all that), but internally convert them into a canonical representation. For
example, if a plain string is to be treated the same as an object with a
_name_ property, I will have code like

    
    
      if(typeof(x) === "string") {
        x = {name: x}
      }
    

I like how Rust just lets you declare a new variable with the same name but
different type, which then shadows the previous declaration for the rest of
the scope.

~~~
pier25
In those cases I usually create a new function depending on the type.

So for example I have a "main" function that does something with a date and
expects a date object:

    
    
        doSomething(date)
    

And then if I want to pass a string I do something like:

    
    
        doSomethingString(date) {
            doSomething(new Date(date))
        }

------
xntrk
The author over looked flow-typed which I believe is an analog of
DefinitelyTyped.

------
taeric
This seems sorta silly. Better tooling prevents more bugs. That is the Crux of
the claim. Yes. It should.

Better static analysis is almost certainly going to prevent bugs. Type
checking is the fashionable static analysis nowdays.

My bed with it is that it requires rewriting. Which, itself, will introduce
bugs. Or just be expensive. Using other tools are likely to give similar
benefits. With the side benefit of keeping the working code you have.

~~~
dangoor
TypeScript and Flow are both designed to work with the way people write
JavaScript. The DefinitelyTyped project adds TypeScript types for existing
JavaScript code _without touching the existing code_.

You don't rewrite your code to use these systems. You add annotations, which
should not be introducing bugs.

~~~
taeric
That is good and I'm interested in that workflow. I've yet to work with a team
that didn't decide they had to rewrite it all. :(

~~~
dangoor
This was definitely a first class design goal for Flow and TypeScript and, I'm
certain, a non-trivial part of their success.

------
drderidder
I feel there should be a counter-article along the lines of "Unit testing and
static code analysis conservatively prevent 80% of the bugs". I've used both
Flow and Typescript and imho they're both frequently more trouble than they're
worth. They catch the simplest of newbie bugs that are rare in modular,
linted, unit-tested codebases. There are static code analyzers (eg. tern.js)
that provide hinting without requiring a transpiler. With Flow and Typescript
you have transpiler overhead, but when it comes to places you might need them
the most - checking and sanitizing data interchange that's so common in
modular, service-oriented architecture - they fall flat. Flow or Typescript
could have been more useful if the annotations truly were _annotations_ in the
form of comments, unfortunately they went the transpiler way.

~~~
watwut
Unit testing is not mutually exclusive with static typing. Normal statically
typed projects have unit tests too.

The biggest advantage of static typing, in my opinion, is that simple
refactorings like "rename" or "move method from one class to another class" or
"change what method does and then modify all places that call it" are done
routinely and very often. Where renaming something in javascript is careful
change you do when fully fresh, rested and focused, it is one quick right
click when you are lazy and too tired to do real work in statically typed
code. Means difference between rarely done and often done.

The other big advantage is when I am learning somebody elses code, all info
about who calls who and how is right there.

~~~
flavio81
_> The biggest advantage of static typing(...) or "move method from one class
to another class"_

You are posting about static typing advantages. Please note that the use case
you mention would not need too much refactoring in languages like Common Lisp
(dynamic language) or Julia (dynamic), because of multimethods and because of
the way methods are called. Methods -and method signatures- aren't constrained
inside class definitions, they are separated.

~~~
watwut
JavaScript does not have multimethods and methods are called on objects. It
sounds like methods must be purely functional and not use any instance fields.

The use case I had in mind was that I delete method from one class and create
it in another. Then I go through all red in workspace and modify what is
needed - sometimes just call the other place, other times remove the call
entirely, yet other times conclude that method needs to go back because that
two special places that don't have access to the other class instance.

Similar with changing signatures - I add/remove parameters and then go through
all reds and decide how to fix them case by case.

------
sAbakumoff
My vision of it is Flow and TS and stuff prevent 15% of bugs that the beginner
and intermediate level developers typically make, but engineers on the
advanced and higher levels avoid these bugs by just writing a good code in any
language they use.

~~~
oelmekki
I've been writing js professionally for more than ten years, and I totally
love flow. Sure, it doesn't happen that often that I feel like I've caught
with it a bug I wouldn't have without it (not even sure how you can tell one
from the other, when you correct a type check error), but the most notable
effect is the release of tension : I don't have to double check everything I
write, I can rely on the type system to report obvious problems.

If anything, I think flow does not go far enough, compared to compiled
languages with type systems. The fact that it allows to mix typed and non
typed code is error prone, and there are annoying edge cases, like the spread
variables not being correctly checked for (this can cascade quickly, when
passing properties in react). I would love to see types as a core part of the
language, with native browser tooling.

~~~
noir_lord
Similar experience with typescript, the win is not having to make room in
headspace for things the machine can handle.

