How to Quickly Understand the Code of a Function
A lot of people struggle with legacy code because it’s hard to understand. It’s not expressive. One way to reduce the gap between code writer and code reader so that they reach an understanding is to write expressive code indeed.
In all cases, you need to be able to read code quickly. The more expressive, the easier to read. The less expressive… well here are some heuristics that should help you figure out what a piece of code is doing, in particular the code of a function. Even if that function looks like it doesn’t want you to understand it.
Reading code is like reading a book
How to read a book? If it’s a fiction book, chances are you’re reading it to have a good time. In this case reading it line by line, cover to cover, is what makes the most sense.
But in the case of non-fiction books, you’re reading to acquire knowledge and understanding. And as explained in the classic How to read a book, you never ever want to read them cover to cover. Rather, you want to run an “inspectional reading” first, to achieve two things:
- decide whether this book is indeed relevant for you,
- get an idea of the main message and parts of the book.
An inspectional reading consists in skimming through the book, looking for places that sum up the information (table of contents, beginning and end of chapters, main messages…). And inspectional reading is fast.
Going back to source code, what sort of book do you think source code relates the most: fiction, or non-fiction?
Unless you’re fond enough of a piece of code to enjoy its reading like you would a good novel, source code is read for knowledge and understanding. Like non-fiction books. For this reason, you don’t want to start by reading a function “cover to cover”, that is to understand its code line after line starting from the first and going to the last.
Instead, you want to skim through lines of code, looking for the main information. You want to perform an inspectional reading on code for the same two reasons as for a book:
- deciding whether this piece is code is relevant for you and deserves a deeper analysis,
- getting a general idea of its meaning before getting into the details.
Now how to extract the relevant information during the inspectional reading of a function?
A function is like a story: get spoiled
Before delving into its implementation, the first thing to look at a function is its name. If it’s well named, it should give a general meaning and orient you for what to look for when you inspect the body.
Or better yet, the name, parameters and return type should be enough together to indicate everything you need to know about this function.
The main character dies at the end
However, not all functions are created equal, and some of them require you to get a peek under the hood. Or sometimes, it feels more like a descent in a cave. Or a propulsion into a black hole, in the worst cases.
When you’re inside the cave, what should you start looking for? The first thing to look inside a function is what it returns. All the rest of the function is oriented towards returning that value, because it’s the point of that function.
So get a big spoiler, skip to the end of the function’s story, and start from the last line. It should look like return something
. In this case, something
is one of the main characters of the function.
Some functions have multiple return statements. If that’s the case, look at all of them, and try to see what they have in common, to get a feel for what that function is returning. Unfortunately, even if functions should return their outputs via their return type, some functions modify their parameters, so that counts as “returns” too.
Since anything can happen in a black hole, some functions go as far as modifying global variables, which also count as output then. And let’s not talk about the functions that return values via exceptions because that’s a whole other dimension.
Anyway, however unfortunate the form the of the output, this is what you should be looking for first if there is one.
Locating main characters and secondary characters
In a traditional plot, the main character is helped by secondary characters to achieve its quest. It is the same in code: a function has intermediary objects, that are not returned from the function but play a key role in its implementation.
Main characters and secondary characters have something in common: you get to see them often. So to know what the function is about, note the objects that appear the most frequently in its code. The function is probably about them.
This technique is also useful if you’re only inspecting a portion of a larger function. If nothing gets returned in that portion, finding the objects that appear the most often helps identity what that portion of code is about.
Skip to the action
Once you’ve located the main and secondary objects of the function, the next thing you want to know is what’s happening to them.
In a function, not all lines are the main action, sometimes far from that. Some lines are merely secondary quests, like getting a value, logging a piece of information, or preparing a secondary character. You don’t want to dwell on those details when inspecting the function. Instead, you want to skip to the main action first.
To locate the main action, you can quickly scan every line of the function, and determine if it looks like the main action, even if with a gut feeling. The point it to do this check really fast. And if it doesn’t feel like the main action, don’t dwell on it, even if you don’t understand all it does. It will become clearer later.
In some cases, you’ll reach the end of the function without having found the main action, because it didn’t looked like it. In this case, make a second pass with the same technique. Now that you’ve considered every line of the function, even if at a glance, the main action should stand out better than the first time.
Identifying the main action may be slow at the beginning. But the scanning of each line becomes much faster and more reliable with practice. With time, your eyes will scan blocks of code rather than individual lines.
This is particularly true if you work on the same codebase for a period of time, because you get used to its style. The coding style of the main action doesn’t look like the coding style of the other bookkeeping stuff of the function.
So what is that function about?
When you perform an inspectional reading first, understanding what a function does becomes like a little game. Find what objects the function revolves around, and what happens to them by filtering out the secondary causes.
This should increase your code reading speed, allow you to read more code, and make it easier to work with legacy code.
Do you have other techniques to understand a function, or code in general? If you do, or if you try the technique presented here, please leave a comment to let us know how it went!
You may also like
Don't want to miss out ? Follow:   Share this post!