Communicating Through Code – Part 2

A wee while ago, I threw out an idea for a coding exercise designed to challenge our skills at writing code that tells a story.

In the basic version, you’re asked to design an API that would enable you to tell the story of Humpty Dumpty, using modules/classes, functions/methods, fields, variables, parameters etc of maximum one word’s length. (It also served as a useful exercise in working backwards in your editor.)

All fine holiday fun. But my Humpty Dumpty code didn’t actually do anything.

The second part of the challenge is to repeat this exercise with code that actually does real work.

I’m using my faithful video library example. When members donate videos to the library, a bunch of stuff needs to happen.

I wrote it down in plain English first:

  • Add donated video to catalogue

  • Register one rental copy of the donated video

  • Award the donor 10 priority points

  • Alert members about the donated video

Then I repeated the process I applied to Humpty Dumpty, except this time, the code actually does what it says it does. This time, I set myself a limit of 2 words per identifier, and allowed myself to ignore “noise” words like “a” and “the”.

function VideoLibrary(catalogue, members, {register, of}, award, alert) {

    return (donatedVideo, donor) => {
        // add donated video to catalogue
        // register one rental copy of the donated video
        // award the donor 10 priority points
        // alert members about the donated video

        add(donatedVideo).to(catalogue);
        register(1).rentalCopy(of(donatedVideo));
        award(donor, 10).priorityPoints();
        alert(members).about(donatedVideo);
    };
}

Then I repeated the exercise, only this time I started out by writing the raw implementation code that does the work:

function VideoLibrary(catalogue, members) {

    return (donatedVideo, donor) => {
        // add donated video to catalogue
        // register one rental copy of the donated video
        // award the donor 10 priority points
        // alert members about the donated video

        catalogue.push(donatedVideo);
        donatedVideo.copies += 1;
        donor.priorityPoints += 10;

        const nodemailer = require('nodemailer');

        const recipients = members.map((member) => { return member.email; }).join(',');

        const email = {
            from: 'alerts@myvideolibrary.com',
            to: recipients,
            subject: 'A new movie has been added to the library - ' + donatedVideo.title,
            text: 'Dear Member, Just to let you know that '
                + donatedVideo.title +
                '(' + donatedVideo.year +
                ') has been added to the library and is now available to borrow'
        };

        var transporter = nodemailer.createTransport({
            service: 'gmail',
            auth: {  // use your gmail user name & password. You may need to set your account to allow less secure apps
                user: '###youremail###@gmail.com',
                pass: '###yourpassword###'
            }
        });

        transporter.sendMail(email, function(error, info){
            if (error) {
                console.log(error);
            } else {
                console.log('Email sent: ' + info.response);
            }
        });

    };
}

And refactored to make it read like the description. This I found especially challenging, but was delighted at how the end result turned out simpler and cleaner – making effective use of closures to encapsulate details. (Check out the full refactored code at https://github.com/jasongorman/text-to-code-refactoring .)

Now, I’ll remind you that this is just an exercise, and for sure the finished design isn’t without its problems. Here’s we’re specifically ticking two design boxes: code that works, and code that clearly communicates its intent.

As I take more passes at this, the exercise gets more challenging. For example, can I make the dependencies swappable? Managed to do that, but VideoLibrary now has many parameters. I managed to preserve the readability of the code in the donate() function, though. So that’s another tick.

Moving down the call stack, I repeated this process for the code that sends email alerts to members about donated videos. So it can be done at multiple levels of abstraction.

function alert(members) {
    return {
        about: (donatedVideo) => {
            send(newAlert(toAll(members)).
                about(donatedVideo), by(nodemailer));
        }
    }
}

One final little challenge I set myself this morning was to make all the data immutable. This proved tougher than I expected, and I couldn’t find a way to do it that preserved the readability of the code that tells the story.

function VideoLibrary(catalogue, members, {register, of}, award, alert) {

    return (donatedVideo, donor) => {
        // add donated video to catalogue
        // register one rental copy of the donated video
        // award the donor 10 priority points
        // alert members about the donated video

        const rentableVideo = register(1).rentalCopy(of(donatedVideo));
        const updatedCatalogue = add(rentableVideo).to(catalogue);
        const rewardedDonor = award(donor, 10).priorityPoints();
        alert(members).about(rentableVideo);

        return {
            video: rentableVideo,
            donor: rewardedDonor,
            catalogue: updatedCatalogue,
            members: [...members.filter((m) => m.email !== donor.email), rewardedDonor]
        };
    };
}

Disappointing. But it served as a neat illustration of the potential cost of immutability in increased complexity and reduced readability. There’s no free lunches!

I’ll give it some thought and try that refactoring again. Maybe there’s a way to have our cake and eat it?

Anyhoo, I recommend trying this exercise with your own code. Write down in plain English (or whatever language you speak) what the code does, then see if you can translate it into fluent code that clearly tells that story. As your design emerges, see if you can apply that process on multiple layers. And see if you can produce a design that both tells the story and ticks other design boxes at the same time.

Author: codemanship

Founder of Codemanship Ltd and code craft coach and trainer

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s