11 Oct 2018

Why do we do this to ourselves?

A couple of weeks ago, we discovered that some dates on our web site  were incorrect. Instead of showing the actual date of a course delivery,  certain pages would always show today's date.


If this had been just a random date on the page, that'd have been fine. But this was the delivery date of the course, the one sitting just below the nice big "BOOK" button. You know, the one a prospective buyer would double-check just before booking. Ouch.

(I know that, all other things being equal, it's probably a good idea  to pretend that at Edument we're some kind of superhuman developers who  never introduce bugs into our own code. But, as much as I might wish  for that, it's just not so. We make mistakes. I also feel there's plenty  of success stories out there, exacerbating the survivorship bias  of our industry. Here's hoping the reader will appreciate reading a  story about an honest (tiny) error, its causes, and how we can all learn  from mistakes like this one. One nice thing about working at Edument is  that I can be almost certain lawyers won't knock on my shoulder, asking  me to take down the article because it might put us in a bad light.)

Back to the bug. I heard with half an ear when Cecilia (product owner  for the web site) was talking on the phone to the fifth-or-so web site  visitor that day who had discovered that the dates were wrong. I got  curious so I looked up the spot in the code that was responsible for  spitting out wrong dates. I ended up looking at this line of code:

<Date date={date.date} />

Date is a React component we wrote ourselves for the website. Inside the Date component, the code wisely delegates the job of date formatting to moment() of momentjs fame.

My very first thought, looking at this line of code, was "betcha date.date comes out undefined somehow".

Indeed, the moment() function expects a date string, but  if it doesn't get one, it will default to "now", the current fleeting  datetime instance. Passing an undefined value in JavaScript is morally equivalent to not passing anything at all. (The situation reverberates with Tony Hoare's billion dollar mistake.)

If the bug hunt were a criminal investigation and I were looking for  means, motive, and opportunity, I would've just found the means.  Definitely not the first time in the history of software that the murder  weapon turns out to have been the value undefined.

Looking closer at the context around the line of code, it indeed looked like date would be a string already, and so accessing date.date would be tantamount to doing a failed lookup (strings don't have a date property), and would result in undefined.

We could quickly push up a fix changing date.date to date. Problem solved.

But I was left wondering what in our process had allowed this mistake  to happen. We code-review all our changes to the web site. In this case  three people, myself included, had looked at that code before merge. At  least one person had even run the PR branch and taken it for a spin,  and probably looked straight at the wrong date. It looked like a date,  so no alarms sounded.

There's another file in the project with the exact same line of code  in it. Most likely the offending line had been copy-pasted from that  file. But in that context, the date variable was fetched from a bigger data structure, and just so happened to be an object with a date property. The same line of code was correct where it was copied from, but wrong where it got pasted.

And it's here that things went wrong, and a crime was committed.

See, humans are fallible. I can't even fault the copy-paste  act as such, since it was a reasonable, practical thing to do in this  case to just go get an existing correct use of the Date component, paste it in, and expect it to work in the new place. The wrong expectation was that date in the new context means the same thing, which it didn't...

...but at that point, our developer environment did nothing to point that out.

In a previous blog post "Brushing teeth and static checks",  I posed the rhetorical question "Why do we do this to ourselves?". Why  do we throw away all that contextual information, all the little bits of  knowledge about the shape of the data that gets passed around between  parts of our program, the parts that would help us the most to avoid intra-program inconsistencies like this?

I wanted to write this article because I felt the date.date  thing is a good concrete example of something that would have been  caught by having enough type information around. Specifically, the  following would have been enough to save us in this case:

  • Use TypeScript.
  • Put type annotations on our parameters. The parameter date would've had to be typed as a string.

That alone would've given us a red underline under date.date, and the bug would never have gone into production. "Property date does not exist on type string", quips the TypeScript type checker. It is right!

TypeScript assists development in exactly this way, propping you up  with a hundred little things each day. It is both a necessary help, and a  humbling reminder of how limited we are when we develop, always one  step from falling off a cliff. JavaScript, in comparison, is like the  friend who first encourages you to go up the mountain without any  safety equipment, and then just smiles enigmatically as you inevitably  tumble down. It's astonishing that we get anything done in JavaScript.

There's a risk of this whole article coming off as dogmatic and  one-sided. I realize that the choice between JavaScript and TypeScript  is not always that clear-cut, and there are many factors involved.  Adding to that, for many decades now, I've found myself in the "dynamic"  camp more than in the "static" camp... which is why I'm in the end so  surprised and delighted that TypeScript with its static type checking  actaully seems to work out for me. It raises my code quality without  limiting how I use JavaScript, and without driving me up the wall with  draconian type system rules. A dynamic language with static checking —  the best of two worlds.

So, are we switching our web site to use TypeScript? Yes... in due  time. With limited resources, there are still more urgent things to  address. But it's on our list of improvements to make.

Related courses

  • TypeScript

    TypeScript starts from JavaScript and turns it into a safe language. You get gradual type checking, code completion, as well as static checking of the way your code hangs together. JavaScript never had the hard, protective shell that we're used to in contemporary languages. TypeScript acts like a programming exoskeleton, giving you safety, expressive power, and precision on top of the original language. 

    Duration: 2 days
    Price: 21 500 SEK