Source Code as Poetic
source code,
like poetry,
is more than its words and symbols
(or the bytes they represent)it has shapes that communicate
its intentions
While we might share Mr. Jourdain’s[1] delight, we should not ignore the shape of our code as well as its syntax. In deciphering code, others (or ourselves after a few weeks) can use the shape and flavour of code not merely as artistic or affected expressions, but as guides to its meaning. While rhyming, alliteration, and meter are usually not practical for source code itself, patterns, rhythms, and flow are
Patterns and rhythm are how the logical parts of code (e.g. functions) resemble each other, how they appear when arranged or nested (e.g. methods in a class), and how variations from these patterns are intentional and recognizable. Top-level code, often the public-interface functions[2] that begin the orchestration of all the specialized procedures (e.g. the Service pattern[22]) can look like verse because it calls [makes reference to] other functions, which, just by their names[3], evoke what the author intended
function EventValidator.execute(person_id, destination_address, event_id)
total_pending = get_total_pending(event_id)
if total_pending < EVENT_MINIMUM
return null, EventMinimumRequired("...")
address_problems = validate_address(destination_address)
if address_problems != []
return null, BadAddress(destination_address, address_problems)
fitness_problems = validate_coordinator(person_id, event_id)
if fitness_problems != []
return null, NotQualified(person_id, event_id, fitness_problems)
return event_assignment, null
Note the rhythmic pattern of the call, check, and compose-the-error triplets. Note also that the “return” has whitespace to give it finality
Compare a function call to a literary allusion or metaphor. Both represent something not described in the text (i.e. in-line) and have an understandable meaning in context. For example, “a stentorian voice” evokes a voice with specific attributes.
display_message(bold("a voice"))
does as well. We don’t know the specific of exactly “stentorian” or bold()
is going to do, but we know what they mean
Couplets and triplets are common in complex processes. Whether it’s Go’s error-checking[33] or if-else cascading trees, these repetitious stanzas can communicate a series of related events, a progression through a workflow, or a gathering of information and permissions needed to achieve a goal[89].
At the very least, vertical whitespace allows a few lines of code to form a stanza. They are connected to each other they are a discrete step. They stand apart from the stanzas above and below, although they are connected to them. If they were completely unconnected, they would be in their own function with its own, well-considered function name
In truth, naming and whitespace are the main things we can control as far as the look and feel of source code; programming languages (even Ruby) do not allow us to break the rules of language as poetry sometimes does. But those tools are enough to see our code as more than tokens embedded in a syntax
See Also #
My thanks and endless admiration to Brian Kernighan, Robert C. Martin, and Kevlin Henney for their inspiration
[1]: Bourgeois Gentleman or The Middle-Class Aristocrat or The Would-Be Noble, Molière, 1670
[2]: Also known as kingpin functions
[3]: A good function name describes what it does cleanly and without cleverness, as Nomenclature Is the Best Documentation – Richard Haven
[22]: I remain unconvinced that the Service Pattern needs to create an instance with parameters, save those parameters to the instance’s fields, then call “execute”, which takes those saved parameters and does something. A class method (with a specific name) could take those same parameters and go on its merry way, no instance required
[33]: if (result, error := subroutine(foo, bar); error != 0) { return nil, error }
[89]: Rhythmic code can also hide copy-and-paste errors because the reader will fill in the correct rhyme while the compiler will just do what it’s told