Careful What You Code

Edge Cases & The Lifesaving Powers of a Web Developer

Jana Huebert

I heard a story recently in which an argument between a young divorced couple ended in one homicide, one suicide and three incarcerations. The cause of this mayhem? An ill-timed autocorrect caused by a software bug.

var WARNING = " i !== ı ";
var initializeArmageddon = function(homeland, input) {
    if (input === 'sikişınce' && homeland === 'Turkey') {
        delete happyEnding.father;
        delete happyEnding.sisterA
        delete happyEnding.sisterB;
        delete happyEnding.ex;
        delete happyEnding.self;
        return WARNING;
    } else {
        return happyEnding;
    }
};

Another equally macabre tale of defunct software involved the Therac-25, a radiation machine used for treating cancer in the 80s. Back in those golden years it was customary for the FDA to opt out of safety inspections if a machine was built on previously functional components, say, old software (which was precisely the case with the Therac-25). They couldn’t have known that by removing the hardware-based safety locks they would expose a vulnerable program that would later administer lethal radiation poisoning to its patients.

This leads us to the obvious moral of these stories: that we, dear friends, hold in our hands the lives of countless humans — jilted lovers and vulnerable patients alike — and we owe them our due diligence.

Pardoning that moment of grandeur, the point does remain the same: recklessly written code has no business being written. Now, hearing a statement like this causes me instant grief. I start rifling through my memory banks, counting up innumerable code-based sins, ignorantly committed during the incubation of knowledge. The circumstantial nature of code and the notable absence of a ruling authority over best practice means that we will all succumb to errors at some point, but we can’t be hindered by this. As a wise programmer once told me: “you just can’t be afraid of anything”. Including mistakes.

Where the danger lies, I believe, is when we — as code handlers — stop caring.

A Story About Caring

We signed a project recently in which we were asked to build a static version of the home page of a social networking application. Considering that we designed the entirety of the app but were building just the one page, we had some unanswered questions: Would we be building out the rest of the app at a later date? What would the client be using as their back-end platform? And who would take care of integrating with that platform? These questions would remain unresolved and so we needed to be careful not to close any doors.

According to the client’s needs and requirements, the project was structured in a more old-school, phased approach with design happening ahead of development. The danger in separating out design and development in this way is that a static mockup of a website can only capture so much nuance and convey so much information. It often happens that what’s represented is a kind of best-case in regards to the data. It’s structured just right and fits just so. As developers and designers we know that there are always curveballs and edge cases, but they’re not always apparent in a static representation. This is where the value of prototyping comes in.

Because this app was a networking tool and most (if not all) of the content would be coming from the users, we knew that there would be no best-case content. A simple solution would have been to restrict and shape the user’s input, forcing that data to be best case with character limits and what have you. But in our opinion, a better solution was to anticipate and accommodate the varied nature of user-generated content and to build the app with this in mind.

So we set forth to do some of the heavy lifting. If, instead of building static files with hard-coded content, we did our development using a disposable back-end complete with a pseudo database, we could kill two birds with the same stone:

  1. We’d be able to build a robust front-end system designed to accommodate the ebbs and flows of the users’ content, and
  2. We could separate out the structure from the content using a template language, saving the client some work.

Building the Framework

Since we were building a disposable back-end that was purely for development, we went with what worked best for us in terms of comfort and ease of development: Express. Because Express is built on Node, using Gulp as a build tool and Stylus as a CSS preprocessor was a natural fit.

However, because the client’s end platform was still a big question mark, we decided to keep things simple: instead of building our templates in our dear friend Jade (whose syntax is elegantly sparse, but not easily converted to HTML), we opted for the safer Handlebars as it inherits all the same syntax as HTML (with the exception of any explicit template components which are wrapped in {{ }}) . Converting from Handlebars to another template language like Django templates, Twig, Smarty, etc. would be trivial.

{{#each currentUser}}
     <img src="{{image}}" alt="{{name}}">
     <p>{{name}}</p>
     <p>{{role}} at <strong>{{company}}</strong></p>
{{/each}}

Express-generator handled all the boilerplate code so all we really needed to do was put together some fake data. We created a “data” module that included lists of possible user names, avatars, post titles, post content, etc.

var colours = [
    "#1a728b",
    "#29afd5"
];
var avatars = [
    "images/avatar.jpg",
    "images/avatar2.jpg",
    "images/avatar3.jpg",
    "images/avatar4.jpg",
    "images/avatar5.jpg",
    "images/avatar6.jpg",
    "images/avatar7.jpg",
];

To take these bits of content and assemble them into objects that a real application might return, we brought in lodash which provides super useful utility functions like _.sample and _.times:

var makeColour = function() {
    return _.sample(colours, 1);
};
var makeAvatar = function() {
    return _.sample(avatars, 1);
};

These simple functions could be chained together to construct more complex objects as well as lists of objects:

var makePost = function() {
    var post = {
        text: _.sample(postText, 1)[0],
        author: makePerson(),
        colour: makeColour()
    }
    return post;
};
var makePosts = function(n) {
    return _.times(n, makePost);
};

But it doesn’t end there. There are plenty of utility functions that can help make fake data more expressive:

var makeNumber = function() {
    return _.random(0, 35);
};

And it’s easy to generate data of any type:

var truthValues = [
    true,
    false
];
var makeTruth = function() {
    return _.sample(truthValues, 1)[0];
};

You can really take these small blocks and compose them into anything you can imagine, incorporating any kind of logic or parameters you might desire. Want to generate realistic (but random and always changing) sports statistics? You can do that. Want to generate realistic weather conditions? You can do that, too.

The goal here is really just to push the limits of the data you’re working with and capture all of the edge cases. Using these patterns we built out a comprehensive, fake, always-changing database that we felt covered a great deal of ground. And employing this “database” in the app was straightforward:

router.get('/', function(req, res) {
    var data = require('../data');
    var templateData = {
        currentUser: data.makePersons(1),
        posts: data.makePosts(10),
        newMembers: data.makePersons(5),
        recentBlogPosts: data.makeBlogPosts(3),
        randomNum: data.makeRandomNum(),
        percentComplete: data.makeRandomPercent(1)
    };
    res.render('index', templateData);
});

I’m sure I’m not the only one who wistfully thinks that the divorced couple could’ve worked it out and lived happy, separate lives. But they didn’t. And the cancer patients? Well they were quite literally sitting targets who didn’t stand a chance. So perhaps all this work could be considered overkill; after all, our clients only asked for 3 files and we went ahead and built an entire framework. But who knows? Maybe we saved your life.