DEV Community

NoAxioms
NoAxioms

Posted on • Originally published at Medium

What devs can learn from writers

There is a place where the Snake River in Wyoming bends north, broadens, and slows. Moose browse the water lilies, pelicans catch fish, otters play. Then the river curls back on itself and resumes its westward journey to the mountains.

You'll see the same looping path if you look at my career so far. I'm a software developer turned humanities teacher who turned back into a software developer. Between engineering and the humanities there is a well-known distance, but I've traveled it enough that I know it's shorter than people think.
While traveling it back and forth, I've noticed that some of the advice that authors of prose dispense could be valuable to authors of software, too—almost as if writing code is a form of writing.¹


Once, I needed to make a little function that adds an item to an array, but removes the item if it is already there. Like this:

toggle([], 1) // should return [1]
toggle([1], 1) // should return []
Enter fullscreen mode Exit fullscreen mode

I typed in the following lines. (Don't worry if this is unclear.)

import _ from "lodash";
const toggle = (arr, item) => _.xor(arr, [item]);
Enter fullscreen mode Exit fullscreen mode

The xor function returns the items that are in just one of the given arrays, omitting any items that appear in both.² When I defined my function in this elegant, laconic way, a sensation came over me, I'm reluctant to confess: I felt clever.

Soon, though, I remembered some words of advice: "Read over your compositions, and whenever you meet with a passage which you think is particularly fine, strike it out." That's a tip from Samuel Johnson, essayist of the eighteenth century.³ The tingle of cleverness, I realized, was a clue that I should simplify my function. I changed it to:

const toggle = (arr, item) =>
   arr.includes(item) ?
   arr.filter((x) => item !== x) :
   [...arr, item];
Enter fullscreen mode Exit fullscreen mode

This new version had a gnarlier syntax. Had the function gone from clever to clunky?

In my opinion: no.⁴ Though more complex in certain respects, the new function was still likely to be clearer for the average coder, and therefore easier to maintain. A line of code can be syntactically simple yet unclear, or clear yet syntactically complex. Just like a line of prose. Think of the sentence, "What did you bring that book that I don't want to be read to out of up for?"⁵ It has a convoluted syntax, but it's much more intelligible than, say, the syntactically simple sentence, "The myrmecobius absquatulated." Though no longer laconic, my new function was easier to read.

If he had known JavaScript, the king of laconic prose himself might have frowned on the conciser version of toggle. "Hemingway"—the king in question—"does not drive you to the dictionary," F. Scott Fitzgerald once said appreciatively.⁶ If it had used xor, my function would have driven most readers to the documentation. Without it, the function is more furled out, explicit, and for that reason, more self-explanatory.⁷

The term laconic comes from the Spartans of antiquity, who lived in a region called Laconia and didn't waste words. An enemy once wrote to them, "If I invade Laconia, I will drive you from the land." They wrote back: "If."⁸

One precept about writing I admire does not come from a novelist or journalist but a museum designer. Good writing, according to Edwin Schlossberg, "create[s] a context in which other people can think."⁹ It helps them picture, mull, respond. It gives them space to do so — like a museum exhibit. In a similar way, good code makes room for readers, as it were. It helps them reason.

It usually makes sense, then, for an author of code to follow patterns that the average reader already understands well. Though most JavaScript developers are familiar with the ternary (the question mark and colon) used above, many are even more cozily at home with an if-else block, like this one:

const toggle = (arr, item) => {
   if (arr.includes(item)) {
      return arr.filter((x) => item !== x);
   } else {
      return [...arr, item];
   }
}
Enter fullscreen mode Exit fullscreen mode

Though a ternary is sometimes the better choice, in this case, the more verbose if-else pattern lightens the load on some readers' heads by a meaningful ounce.¹⁰ It is also easier to modify than a ternary is. The ternary is a punchy, Spartan conditional, while the if-else block is a little more open to revision, democratic, Athenian.


Authors must have empathy for their readers, and you must, too, for the people who will maintain, debug, or adapt your code. Since you're the author, though, it's normal to have trouble stepping into your readers' shoes. Try two techniques borrowed from the best writers.

First, Helen Sword suggests that you "visualize specific people looking over your shoulder as you write." After all, "the most engaging writers are almost invariably those who pay the closest attention to the real people — specialists and nonspecialists, colleagues and strangers — in whose ears their own words will echo."¹¹ Wise counsel for coders, too.

A second technique: set your code aside for a day or two, if you can, then return to it. "Finish the short story, print it out, then put it in a drawer and write other things," Neil Gaiman once told an aspiring writer. "When you're ready, pick it up and read it, as if you've never read it before. If there are things you aren't satisfied with as a reader, go in and fix them as a writer: that's revision."¹² The ancient author Quintilian also recommended that we "put aside what we have written for a certain time"—

so that when we return to it after an interval it will have the air of novelty and of being another's handiwork; for thus we may prevent ourselves from regarding our writings with all the affection that we lavish on a newborn child.¹³

It is not too surprising that writerly guidance can be adapted for symbols of a different kind. Areas of knowledge often infiltrate one another, or as the novelist Charles Johnson beautifully puts it:

One form of artistic and intellectual expression nurtures and feeds the others. Whatever it is we call creativity and imagination — those two great mysteries — can be for some creators experienced as "global" in their lives, not localized in a single form of expression but rather spreading or spilling from one genre to another, one artistic or intellectual discipline to another, for all the humanities (along with the sciences) are related, interconnected.¹⁴

Experimenting in your own coding with the approaches recommended here may require a change in your routine. A detour, so that you can, like the river, decelerate and become more reflective, more generous. So take a page from the river. It doesn't follow a linear path.


The coding-writing analogy has its limitations, to be sure. For instance, contradictions are all but outlawed in source code, and it is in art, particularly poetry, that they get their justice. There is a poem by Fernando Pessoa that I like to read aloud even though I don't know Portuguese. It begins:

O poeta é um fingidor
Finge tão completamente
Que chega a fingir que é dor
A dor que deveras sente.

A poet is the one who feigns.
He so completely fakes the real
He even fakes the awful pains
And sufferings that he does feel.¹⁵

Pessoa is inviting us to momentarily set aside conventional ideas of true and false. He is unapologetically stating a contradiction: poets conceal themselves behind their poems, and in doing so, they become visible through them. Leaving Boolean logic behind, Pessoa invents a logic of his own. "A poet is the one who feigns": what does this mean, if not, "I'm making up everything I'm saying—it's all false, including these very words"? This is a self-refuting statement, a logical falsehood. But if you look through the words to their etymological roots, "O poeta é um fingidor" means "A maker is a maker." A logical truth. Like the simulated pain he mentions, the poet's assertion is false on the surface, true underneath. Code and poetry offer complementary strategies for coping with an often illogical world: one provides refuge from contradictions, the other safe passage out to the deep waters where we can glimpse them.


Footnotes

[1]: The coding-writing analogy has been made before, though differently, by Brian Kernighan and P. J. Plauger, Donald Knuth, Richard Gabriel, James Devlin, Jeff Atwood, and Rebecca Sutton Koeser, among others. Steve McConnell critiques the analogy.

[2]: Here's an example of the lodash library's xor function in action. In Boggle you look for words in a grid of letters. If you find a word no one else found, you score points for it. Suppose you're making a version of the game in JavaScript. You have this data:

const wordsFoundByPlayerOne = ["apple", "peel"];
const wordsFoundByPlayerTwo = ["apple", "leap"];
const wordsFoundByPlayerThree = ["apple", "able"];
Enter fullscreen mode Exit fullscreen mode

To get the point-scoring words, you can use xor:

_.xor(
   wordsFoundByPlayerOne,
   wordsFoundByPlayerTwo,
   wordsFoundByPlayerThree
); // Output: ["peel", "leap", "able"]
Enter fullscreen mode Exit fullscreen mode

[3]: Quoted in James Boswell's Life of Samuel Johnson (1791). Kernighan and Plauger have a similar insight: "Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?" (The Elements of Programming Style, 1978, p. 10).

[4]: We can also create a highly readable alternative with the help of Set:

const toggle = (arr, item) => {
  const set = new Set(arr);
  if (set.has(item)) set.delete(item);
  else set.add(item);
  return Array.from(set);
}
Enter fullscreen mode Exit fullscreen mode

[5]: Source: linguist Beatrice Santorini.

[6]: Quoted by writing coach Gary Gilson in a 2019 article.

[7]: By contrast, the blogger Jeff Atwood has promoted "Spartan programming".

[8]: Plutarch, On Talkativeness.

[9]: Quoted by physicist Konstantin Likharev in a 2021 book.

[10]: In his 2012 talk "Simplicity Matters," Rich Hickey argues that programmers should not fear difficulty, in part because it makes our work interesting. But our work will be most interesting if it falls in the Goldilocks zone between boring and taxing-as is the case with school lessons.

[11]: Helen Sword, Stylish Academic Writing (2012), p. 44. To improve their clarity, many writers read their work aloud, listening to themselves. The philosopher Jonathan Bennett writes: "Gilbert Ryle once told me, 'What doesn't read well to the ear doesn't read well to the eye', and that changed my life. More than any other one thing, that insight showed me how to start climbing out of the garbage pit up onto the plain of decent prose." Kernighan and Plauger offer a similar insight about programming: "A useful way to decide if some piece of code is clear or not is the 'telephone test.' If someone could understand your code when read aloud over the telephone, it's clear enough. If not, then it needs rewriting" (Elements of Programming Style, p. 21).

[12]: Neil Gaiman, "Advice to Authors".

[13]: Quintilian, Institutes of Oratory.

[14]: Charles Johnson, The Way of the Writer (2016), p. 23.

[15]: Pessoa, "Autopsychography". Translated in Ryan Wilson, Proteus Bound: Selected Translations 2008–2020 (2021).

Thank you to Shashank Khandelwal and Asheesh Laroia for many helpful suggestions.

Top comments (0)