When we say “story”, though, we mean more of a news story than the creative/fictional sort. While the latter tends to leave room for interpretation, there should be no ambiguity with the former.

Take it from a copywriter turned developer (did we say we’ve got a diverse team here?). Many of the principles of communication writing apply to coding too. After all, the craft of writing is the art of thinking. That’s true whether the end product is copy or code. If you organise your thoughts and intentions clearly, that should come across in your chosen written medium.

A simple rule to start: Make it clear who the actors are and what is happening.

I once coached a student at Codebar who asked about a piece of code she’d written some time ago, saying, “I can’t remember why this is here and what it does.” The code in question had to do with an arithmetic-based problem involving multiple steps and scenarios. What are these numbers on this particular line? Why are we doing this operation here and then this other thing after? Does this num refer to the original input, the interim result from an operation above, or something else?

Confused

It’s tempting to dismiss this particular incident as a newbie not knowing how to code properly, but have you not done a git blame at work on confusing code? Variations of this issue occur because of a simple, universal fact: What is obvious to you might not be obvious to anyone else… even your future self.

We’re coding in a high-level language so that humans can understand and work with it easily, right? Let’s make it so.

Make it so

Naming

Yes, we all know this, and yes, this is much easier said than done. Many people who are stronger programmers and/or writers than me have expounded on the issue, so I won’t bore you with more of the same, just the following principles we follow and questions we ask:

  • Go from generic to descriptive, even if it means the name looks a tad longer than what’s aesthetically pleasing. Clarity trumps almost everything, in my opinion. And at least, when you manage to get this idea out of your head and across to other people, they have the knowledge they need to provide feedback and come up with alternatives (you have code reviews, no?).

  • Working backwards helps sometimes. If you’re working on a feature that calls for a new database column and have a hard time figuring out its name, consider the values you expect this column to hold. Now you’ve got something more concrete to stimulate your brain.

  • Make sure your variable/method name makes sense for what is held/returned, not what simply sounds right or was once true when you began the work. For example, let’s say you work here and are building this feature to hand out a voucher to a user, so you start writing this distribute_voucher method. Are you returning a voucher object or its voucher_code string? If it’s the latter, is distribute_voucher really the best name?

  • Keep your domain and audience in mind. If what you’re naming is also going to be client-/user-facing, e.g. a field on a form to be filled in, it’s worth asking yourself: “Will our target audience, presumably non-technical, understand what I mean when I say ‘X’?” And even if something is only meant to be seen by your fellow developers, is the name dramatically different from what the business calls it? We have dealt with some legacy issues here with some rather crazy “what it’s called in the codebase vs. how we actually refer to it” mapping. It’s no fun.

  • Take a look at what is already there. If you are aware of the existence of a column named item_text, you’ll probably think twice before settling for item_description when you create a new one. If you see that there’s already a send_gone_live_email boolean column, you’ll likely also go for send_campaign_ended_email instead of skip_campaign_ended_email, even if the latter is literally what you are currently trying to accomplish. Don’t accidentally introduce confusion.

Encapsulating complex concepts

Related to naming, you have to be able to express larger concepts too. Consider this example, for instance:

params.select do |k, v|
  k.in?(["name", "email", "mobile", "auto", "http_referrer", "source_network", "one_more_thing"])
end

Why are we checking against these things in the array? Oh, they’re really User::SIGN_UP_INFO and ShortenedUrlToken::SOURCE_PARAMS. Isn’t that a lot more obvious upfront, once these items are moved into sensibly named constants within modules/models where these concepts belong?

This practice is also helpful in cases where you have something like do_this unless Thing.count > 5 — why would anyone other than you know what, if any, is the significance of the number 5 here? Is it a business decision, some design or performance concern, or a completely arbitrary number?

In a similar vein, if you have logic along the lines of foo.bar == "something" && (!foo.baz || possum.awesome?), even just moving the !foo.baz || possum.awesome? bit into its own has_special_powers? method can make your intentions much easier to grasp for whoever reads it.

For even more complex concepts such as the business logic accompanying some user action, you may also want to use interactors. For a user sign-up, for instance, you can even break things down with a SignUpUser interactor composed of other single-purpose ones like FindOrCreateUser, SendWelcomeEmail, SetEmailNotificationPreferences, RebuildLeaderboard, etc.

None of this is rocket science. Whatever you do, it’s all about having the what and the why as clearly laid out as possible.

Useful contexts and expectations in specs

Need a few more words to explain what you’re trying to do? Well, you’ve got your specs, haven’t you? I’m a big fan of specs that can almost double as documentation. Keep the contexts and expectations clear. My preference is for single expectations and semantic expressions whenever possible:

describe "#some_method" do
  context "when given {some_argument}" do
    # set up and stub what you need for the stated context
    # use your `let` and `before` here without cluttering the `it` blocks

    it "does {this_one_thing}" do
      expect(subject.some_method(some_argument)).to eq {this_one_thing}
    end

    it "does not create {this_other_thing}" do
      expect{
        subject.some_method(some_argument)
      }.to_not change{ ThisOtherThing.count }
    end
  end
end

And for those of you who practise TDD, writing your specs is also an exercise in thinking, the precursor to good writing in general.

Final words

Bottom line: The reader shouldn’t have to guess. Worse, they shouldn’t be misled; it’s more dangerous when people think that they understand something when they really don’t. A little bit of empathy goes a long way. If there’s anything at all that you can do to make your code more readily accessible to a fellow human, go for it!