The 80/24 rule by Mark Seemann
Write small blocks of code. How small? Here's how small.
One of the most common questions I get is this:
If you could give just one advice to programmers, what would it be?
That's easy:
Write small blocks of code.
Small methods. Small functions. Small procedures.
How small?
Few lines of code #
You can't give a universally good answer to that question. Among other things, it depends on the programming language in question. Some languages are much denser than others. The densest language I've ever encountered is APL.
Most mainstream languages, however, seem to be verbose to approximately the same order of magnitude. My experience is mostly with C#, so I'll use that (and similar languages like Java) as a starting point.
When I write C# code, I become uncomfortable when my method size approaches fifteen or twenty lines of code. C# is, however, a fairly wordy language, so it sometimes happens that I have to allow a method to grow larger. My limit is probably somewhere around 25 lines of code.
That's an arbitrary number, but if I have to quote a number, it would be around that size. Since it's arbitrary anyway, let's make it 24, for reasons that I'll explain later.
The maximum line count of a C# (or Java, or JavaScript, etc.) method, then, should be 24.
To repeat the point from before, this depends on the language. I'd consider a 24-line Haskell or F# function to be so huge that if I received it as a pull request, I'd reject it on the grounds of size alone.
Narrow line width #
Most languages allow for flexibility in layout. For example, C-based languages use the ;
character as a delimiter. This enables you to write more than one statement per line:
var foo = 32; var bar = foo + 10; Console.WriteLine(bar);
You could attempt to avoid the 24-line-height rule by writing wide lines. That would, however, be to defeat the purpose.
The purpose of writing small methods is to nudge yourself towards writing readable code; code that fits in your brain. The smaller, the better.
For completeness sake, let's institute a maximum line width as well. If there's any accepted industry standard for maximum line width, it's 80 characters. I've used that maximum for years, and it's a good maximum.
Like all other programmers, other people's code annoys me. The most common annoyance is that people write too wide code.
This is probably because most programmers have drunk the Cool Aid that bigger screens make you more productive. When you code on a big screen, you don't notice how wide your lines become.
There's many scenarios where wide code is problematic:
- When you're comparing changes to a file side-by-side. This often happens when you review pull requests. Now you have only half of your normal screen width.
- When you're looking at code on a smaller device.
- When you're getting old, or are otherwise visually impaired. After I turned 40, I discovered that I found it increasingly difficult to see small things. I still use a 10-point font for programming, but I foresee that this will not last much longer.
- When you're mob programming you're limited to the size of the shared screen.
- When you're sharing your screen via the web, for remote pair programming or similar.
- When you're presenting code at meetups, user groups, conferences, etc.
Notice the red dotted vertical line that cuts through universe
. It tells me where the 80 character limit is.
Terminal box #
The 80-character limit has a long and venerable history, but what about the 24-line limit? While both are, ultimately, arbitrary, both fit the size of the popular VT100 terminal, which had a display resolution of 80x24 characters.
A box of 80x24 characters thus reproduces the size of an old terminal. Does this mean that I suggest that you should write programs on terminals? No, people always misunderstand this. That should be the maximum size of a method. On larger screens, you'd be able to see multiple small methods at once. For example, you could view a unit test and its target in a split screen configuration.
The exact sizes are arbitrary, but I think that there's something fundamentally right about such continuity with the past.
I've been using the 80-character mark as a soft limit for years. I tend to stay within it, but I occasionally decide to make my code a little wider. I haven't paid quite as much attention to the number of lines of my methods, but only for the reason that I know that I tend to write methods shorter than that. Both limits have served me well for years.
Example #
Consider this example:
public ActionResult Post(ReservationDto dto) { var validationMsg = Validator.Validate(dto); if (validationMsg != "") return BadRequest(validationMsg); var reservation = Mapper.Map(dto); var reservations = Repository.ReadReservations(reservation.Date); var accepted = maîtreD.CanAccept(reservations, reservation); if (!accepted) return StatusCode( StatusCodes.Status500InternalServerError, "Couldn't accept."); var id = Repository.Create(reservation); return Ok(id); }
This method is 18 lines long, which includes the method declaration, curly brackets and blank lines. It easily stays within the 80-character limit. Note that I've deliberately formatted the code so that it behaves. You can see it in this fragment:
return StatusCode( StatusCodes.Status500InternalServerError, "Couldn't accept.");
Most people write it like this:
return StatusCode(StatusCodes.Status500InternalServerError, "Couldn't accept.");
That doesn't look bad, but I've seen much worse examples.
Another key to writing small methods is to call other methods. The above Post
method doesn't look like much, but significant functionality could be hiding behind Validator.Validate
, Repository.ReadReservations
, or maîtreD.CanAccept
. I hope that you agree that each of these objects and methods are named well enough to give you an idea about their purpose.
Code that fits in your brain #
As I describe in my Humane Code video, the human brain can only keep track of about seven things. I think that this rule of thumb applies to the way we read and interpret code. If you need to understand and keep track of more than seven separate things at the same time, the code becomes harder to understand.
This could explain why small methods are good. They're only good, however, if they're self-contained. When you look at a method like the above Post
method, you'll be most effective if you don't need to have a deep understanding of how each of the dependencies work. If this is true, the method only juggles about five dependencies: Validator
, Mapper
, Repository
, maîtreD
, and its own base class (which provides the methods BadRequest
, StatusCode
, and Ok
). Five dependencies is fewer than seven.
Another way to evaluate the cognitive load of a method is to measure its cyclomatic complexity. The Post
method's cyclomatic complexity is 3, so that should be easily within the brain's capacity.
These are all heuristics, so read this for inspiration, not as law. They've served me well for years, though.
Conclusion #
You've probably heard about the 80/20 rule, also known as the Pareto principle. Perhaps the title lead you to believe that this article was a misunderstanding. I admit that I went for an arresting title; perhaps a more proper name is the 80x24 rule.
The exact numbers can vary, but I've found a maximum method size of 80x24 characters to work well for C#.
Comments
As a matter of fact, terminals had 80 characters lines, because IBM punch cards, representing only 1 line, had 80 symbols (even though only the first 72 were used at first). However, I don't know why terminals settled for 24 lines! In Java, which is similar to C# in term of verbosity, Clean Code tend to push towards 20-lines long functions or less. One of the danger to make functions even smaller is that many more functions can create many indirections, and that becomes harder to keep track within our brains.
Some additional terminal sizing history in Mike Hoye's recent similarly-named post: http://exple.tive.org/blarg/2019/10/23/80x25/ Spoiler - Banknotes!