I was pair programming recently, and I had a bit of revelation: programming is just storytelling. I was “leading” at the time, the more experienced of us in the problem space we were in, and my partner seemed to need the most help seeing how all the little bits fit together into a coherent and complete system. I knew he could write each individual bit, or make any arbitrary specific operation happen — when you’re new to a language or library this typically involves a search engine, Stack Overflow, the language or library’s main documentation site, and a few other sources — but didn’t have enough familiarity with the dynamics of the story that these operations would tell on the whole to drive the process.
Said a different way, there are two hard parts of programming: knowing the specifics of how to make the computer perform all the relevant actions that it should, and knowing what all the relevant actions are and how they fit together.
The first is mostly a question of learning how to Google well. Fred’s old article about using search engines effectively is really relevant to these sorts of programming problems. They’re things that aren’t hard if you can read and your problem is common, but they don’t make useful software. That’s the other part or the problem: the stories that the individual problems are made of.
The Three Stories a Good Programmer Can Tell
The first story is to the computer: you’ve got to make the computer understand and complete a sequence of actions which it’ll perform perfectly and exactly until the end of time. That’s the lowest level, and the most obscure (to human understanding) story. And it’s told purely in the programming language of your choosing, using its core library.
The next level story is the programmer-understandable story. This is hopefully closer to English, but it’ll use a lot of specific vocabulary that would probably confuse people who’ve never programmed. In PHP, you’re probably talking about “arrays,” “files,” and touch a few “objects” as well. In other languages, you’re possibly dealing with lots of “hashes,” “maps,” “streams,” and “dictionaries.” You may return or manipulate these primitives with these methods, but you shouldn’t really need to manipulate data primitives in this level of story to accomplish your tasks. This level of story should mostly be told by the methods of your objects, or functions in your system. This is where the naming we talked about a few weeks ago matters a lot. You’re composing the primitives of your language’s core library into gloms of programmer-understandable functions or methods and then telling a story with them.
The final level is the most important one of the whole system. It’s the end user’s story. It’s the actual reason that humans pursue writing software at all: to make us more efficient at accomplishing our goals. If you’ve ever done behavior-driven development, or compiled all of your software’s “user stories,” this type of method is exactly what we mean here. “User presses button and [goal they have] is accomplished.”
Hypothesis: You Should Strive To Keep Your Three Stories Apart
I’ve not really put this idea into practice yet — I only came up with it recently, and have mostly been writing haphazard code to patch around existing problems in existing systems since — but I think when you see your code in terms of these stories, it creates clear natural, and useful boundaries to protect you from jumping between drastically different levels of abstraction.
Some of the worst code I’ve ever written, and some of the worst code I regularly find written by others, makes a single mistake: it regularly and haplessly jumps between levels of abstraction
Software can be bad for a lot of reasons, but one of the more common patterns of flaws I’ve found is about cognitive load. Hard-to-understand software regularly and haplessly jumps between levels of abstraction. One second you’re thinking in terms of the user goals — send this job to the public list — and then you’re immediately writing a database query that toggles the boolean value in the
publicly_visible column of the database’s
job_listings table to
true. Why is this bad? Literally whole books have been (and will be) written to answer that specific question, but the summary is this: it’s very hard to think in terms of all three levels of your software’s story simultaneously. It makes your head hurt, your thought-train derail, and just generally breaks your flow.
Now, as I said, there’s a lot to this topic that’s bigger than our small talk about stories. There are things that separate the seminal texts of programming from good online articles about programming, and seamlessly transitioning to a deep dive into issues of data(base) access, information privacy, and idempotence are a thing we’ll sanely leave aside here. But the point remains: one of the easiest and sanest ways to create good and simple logic boundaries inside of your program is at the level of your stories.
And when you’re really getting in there close to the metal and writing computer-level directions for a method, don’t you dare toggle something in your user interface from there.
Don’t ever, in the code that responds to a button pressed in your user interface, have a single line of code that calls a language-primitive core-library function or write data directly to a store of any kind. Don’t ever, when you’re accessing your objects and calling their methods directly, change something in your user-interface or directly write something in term of your environment’s data-storage primitives. And when you’re really getting in there close to the metal and writing computer-level directions for a
setAsPubliclyAccessible method, don’t you dare toggle something in your user interface from there.
The Role of Stories When Programming
Are stories flawless and complete as a method to find and define the best boundaries in your software? Nope. Are they a good place to start to think about what programming is and why you sometimes want boundaries inside of software? I think so.
To the true novice programmer: think about the story you’re trying to empower the user to accomplish. Break that down into pieces that it makes sense for a computer to do in chunks. Then write those individual chunks in small bits of pure and simple language: primitive data-structures and method calls.
To the intermediate programmer: consider stories as a place to draw and think about boundaries you should establish in your system. Keep in mind the need to keep things apart, and reach for stories as your first line of defense against regularly and drastically jumping between levels of abstraction in your software.
And to the true expert: thanks for reading to the end. I’m sure you already knew all of this. 😉
Image Credits: kodomut