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.


By Carl Mäsak



0 comments