What In A Good Code Routine - Pt 1
Posted by Charlie Recksieck
And How Long Should A Routine Be
But it doesn't work out that way in practice. There's never only one way to write code to accomplish a test. There's actually a bit of "art" to it, or at least personal tendencies and trends in how code is written and organized.
Thinking about this the other day, I asked myself, "What are the hallmarks of a good routine?" This article is me answering my own question.
If You're Not A Programmer
We won't get crazy technical here so it might be worth your while to stick around. If Part 1 seems dry, we promise that Part 2 will be more savory.
I may use the words "method" and "routine" here in the article. They're both terms for a chunk of code. Ideally, this chunk is built to accomplish ONE thing (not multiple things; we'll revisit that when talking about "modular" code later). Visually, I'm going to leave the layman with that description. Here's a very simple routine of just 3 lines:
But if you want to be more correct, "methods" appear in object-oriented code and each method chunk is part of a module which, once loaded, can be called from anywhere. "Routines" are the more generic term for any code "chunk".
Then we have "procedures" and "functions" which get thrown around. Those are two different types of routines. As I try to describe in the image at the top, a procedure is where some code gets run but no values are returned (e.g. a routine called RotateImage could be written to not return a value) whereas a function does return a value (a routine called ChangeToSentenceCase would take an input of "LET'S DO THIS" and return a value of "Let's do this").
Attributes Of A Well-Written Routine
In my mind, these are the ideal characteristics for our good "chunks" of code:
* Modular/Reusable ... and Testable
* Clear Purpose
We are going to dive deeper into these criteria. In Part 2 of this post we're going to apply this approach to modular software design to cooking lasagna. So you've got that going for you. Which is nice.
Routines should have a discrete purpose. Let's say we were going to have some code check a web service to look into a database to see if any new users have signed up in the past 24 hours. We don't write all of the code in one routine; we break the code up into chunks. One to connect to a web service, one to connect to this specific database table, one to package the list of new users into a list, one to send the email to the admin, etc.
Lots of times, the act of writing the comments and description of what the routine does as in-code documentation should tell you if you're doing things right. Look at this opening of an email routine ...
The comments tell us what this routine does; in this case a very easily-defined task of sending an email.
Modular/Reusable ... and Testable
The concept of routines being "modular" is really important to well-organized software. I've seen lots of definitions but I really like this one from Caitlin Jee.
"Modularization is the process of separating the functionality of a program into independent, interchangeable modules, such that each contains everything necessary to execute only one aspect of the desired functionality."
In the case above of our email routine, all this routine does is send an email. It doesn't compose the message. It could be plucked out of this spot and be re-used throughout the project or in other projects. Even the part about "DetermineOfficeVersion" itself is modular. That's its own routine.
Think of good modular code as being a black box. Outside of the routine you only care what you send to the routine and what you would get back. Inside the routine, nothing matters except taking the input and creating the expected output.
This is a little repetitive from our first two attributes of good routines above. But it's worth reiterating: A routine should not be doing multiple things.
In our code shown above we are not gathering the content of the email and sending the email in the same routine. We keep those discrete.
Getting code to work could seem like it's the only truly important thing about writing software. Not only does it have to be thought out in advance with intelligent architecture and design before code, but how easily it can be understood and maintained after the fact is 20-30% of great programming.
Not only does code need to be written and documented so that other people (or your replacement) can understand it, but just as importantly YOU need to be able to understand it 6 months later. All of the code decisions, routines and commands make sense to you in the moment while you're writing them. But what happens after you've been working on 15 other projects then you have to come back to make a fix. Undocumented or sloppy code is a lot harder to understand, even if you're the author.
One thing that helps are naming conventions. Anybody writing code for more than a week quickly learns that variables should be named descriptively - do you have any doubt what the variable strRecipientAddresses does? Similarly, it's important that routines themselves are well-named, like in our example above of SendNotificationEmail.
Another thing we REALLY recommend for both pre-code planning AND post-code documentation is something called "Programming Design Language". It's where we write out what a routine does in simple English, describing each step. Then we "code" each step. When we comment out the simple English then it's suddenly the most appropriate documentation you'll ever write. (We've written an article on the whole PDL concept.)
We turn this starting PDL ...
... into ...
How long should a good routine be? You'll see some debate and different philosophies about this when you ask a programmer. The most cardinal rule about this is that a routine should only focus on accomplishing one programming task (e.g. sending an email, querying a database, inserting and rotating an image, etc.). Again, the modular thing.
As for length, some say to have routines short enough where you can see the whole thing without scrolling. But other credible sources say differently.
Steve McConnell in his seminal book Code Complete said: "The routine should be allowed to grow organically up to 100-200 lines, decades of evidence say that routines of such length no more error prone then shorter routines."
Whereas, in Clean Code: A Handbook of Agile Software Craftsmanship, Robert Martin says: "The first rule of functions is that they should be small. The second rule of functions is that they should be smaller than that. Functions should not be 100 lines long. Functions should hardly ever be 20 lines long."
While there's not a magic number of maximum quantity of lines in a routine, you kind of know it when you see it. And let the name of the routine be your guide. If your routine is called "WriteAndSendEmail" then it's too long, it should be split up in two.
OK, let's stop the more academic part of this article. Next week in Part 2 let's apply all of this to a delicious analogy: making lasagna!