Choose Your Own Coding Adventure?
You remember the Choose Your Own Adventure series? You’d read a paragraph or two, then be presented with a question, the answer determining which of several pages you’d turn to in order to continue the story.
I may be giving too much credit to Mike Stackpole, Rick Loomis and the rest of the crew at Flying Buffalo for inventing the genre, but I give them credit for coming up with an interesting way of bringing interactivity to paper.
Creative as it was, the resulting stories were difficult to read. I had to stop every few sentences to turn pages, and jumping around through the book like a peripatetic kangaroo kept me from getting involved in the story itself, kept me all too aware of the medium rather than the message.
Yet how many of us today follow this noble, but failed, experiment when writing our code?
We begin writing our methods with the best of intentions. We lay out the opening scene (the arguments) and launch into the gripping tale of what is happening to them.
Yet only a few lines into the story, we stop. We interrupt the narrative flow in order to make a decision and send the reader off to somewhere else, with no regard for what is happening in the main storyline.
It might be something simple. We might break the reader’s concentration with something like:
id = params[:id].nil? ? 1 : params[:id]
Model.find(id)
Not only that, but if you wanted to be sure you weren’t getting slipped something troubling, you’d also have to check to see if what you were handed as the id actually is a numeric, and if not, handle that separately. It may be a small thing, but it distracts the reader (and ourselves) from the main point of the adventure, which is what we’re about to do with that id.
But we have to be sure there’s something both safe and useful in that id before we use it, I hear you say? Of course we do. It’s just there’s a better, more reader-friendly, way to word that. A way that will do less to distract our reader from our main plot.
If we use the fetch() method instead of simply [] to get the value, we can specify a default to use if the index is unset. This means we’ll get a real value back, no matter what, and won’t have to test for Nil and do any conditional branching to handle it as a special case.
Adding the .to_i method at the end of that (something we couldn’t do so long as the possibility of getting a Nil remained) will make the received value safer to use, by returning zero (a number) if the id value is anything but a number, which means the find method will always have a parameter it can work with, so we can drop it directly into the find:
Model.find(params.fetch(:id, 1).to_i)
This single line pulls from the model by id, supplies a default value of 1 if the id has not been given, and makes sure the given id is in fact a number, returning a zero if it isn’t.
By doing this we’ve handled the problems of Nil and non-numeric input at the same time, so we can guarantee the model will get a type of input it can use; failure in finding the zero id (the result of trying a non-numeric id) can be handled exactly the same as any failure to find an id not present in the database.
Look at your own code, this time realizing you’re telling the story of what happens to the data it encounters. Remember, the code you write today will be read tomorrow, either by yourself or someone else. Use good storytelling techniques to make that easier.
Tell the story of your code clearly, keeping digressions like error handling out if the main narrative flow as much as possible. Keep the main purpose of the code in a smooth unified narrative flow, so as not to confuse your reader. Your reader (who might also be yourself at some future date) will thank you for it.
And so will your code.