Every Technique I Know to Understand Code

Figure out how others understand it

Form a mental model of the system by exploring the UI:

When I don’t really know what a piece of code is trying to do, it helps to poke around the user interface – clicking on buttons, finding screens, trying to see the effect of my inputs. Often forming a model of what’s happening through playing with the interface helps me understand where the code under the hood fits in. And often, I’ll start tracing bugs from the last button or dialog I touch to recreate them.

Get someone to explain a subsystem and its design decisions to you:

When I’m looking at some subsystem and I know what it does in general, but I’m having trouble knowing what’s involved and how it fits together (often because the implementation is more complex than I thought it would be), I try to find someone that wrote it or has worked with it. I’ll admit to not having this down pat, but I try to get them to explain how it works, and what design decisions or thinkings influenced how they did it. This often illuminates the most puzzling convolutions in the code.
[How Coders at Work Understand Code: Everything: {Zawinski - p34, Eich - p158, Bloch - p182, Norvig - p317} Top Down: {Ingalls - p407, Thompson - p479, Allen - p489, Knuth - p600} Refactor: {Crockford - p106, Cosell - p547, Fitzpatrick - p70} Traces: {Steele - p334} Not sure about: {Armstrong, Deutsch, Peyton Jones}]

Read about it

Read the high level driving code/documentation to get an idea of the major structure of the application:

When I’m having trouble figuring out what the major components are or I want to know which parts of the application I can safely ignore, I like to start with the driving code. Sometimes there’s a good core function or class, but sometimes I head for the design documentation to get an idea. That documentation isn’t always up to date, so reader beware, but it can sometimes give a direct answer to questions about the application in-the-large. The nice thing with understanding an application from the top down is that it’s organized hierarchically like a tree in your head, so it’s easy to search and dig in and find relations between things.

Strip out everything but the frameworks and study the lifecycle of the program:

When the application is more coupled with a framework than its own driver, I like to focus on the framework and see where the code gets invoked. I haven’t tried it myself, but I’ve seen recommendations to actually just rip out all the code except API calls in/out to see what the major parts of the application are (reverting it afterwards, of course).

Read and understand the most low-level data structures:

A great way to understand an instant messaging program is to look at the protocol and the message objects. When there are a few core data structures, understanding them and all their possibilities can be a key to understanding the rest of the program. This is when its worthwhile to read through each bit of the low-level objects, and maybe make up some quick-reference sheets.

Add a breakpoint and look through the trace:

When I want to figure out how the application gets from one point to another, I’ll add a breakpoint somewhere, then look at each level of the call stack and understand what each function does to arrive at the next function down. This can especially help when there are a lot of small functions or tricky dependency injections – it gets you right to the code that’s actually being executed.

Pick somewhere important-looking and figure out every single line:

When I’ve run out of places to try to zero-in on what I’m looking for, I fall back on the ever-reliable standby: pick the best thing you’ve got, and read it line-by-line until you understand it. It helps when you’ve skimmed enough to know that it can be read and understood in a reasonable amount of time.

Read the support:

When I’m looking at code and the implementation is still too tricky to figure out easily, I’ll go for some support. Help pages? Unit Tests? Documentation? Source control check-in notes? Development tickets/notes? I take whatever I can find, as long as it’s useful.

Use tools to understand inheritance hierarchies and find patterns:

When I’m dealing with things that are deep in an inheritance tree or spread around several classes, I turn to some automated support. There are several tools out there which will generate UML or, at the very least, show inheritance trees and all the methods defined for a class. Often I’ll find a base class responsible for one thing, and a subclass responsible for extending it to add one thing, neither of which are clear from reading either one alone.

Write about it

Attempt to draw the architecture of the system:

When I’m trying to come to grips with a high level view of the system, I like to try to draw out an architecture and explain the major components and connections in the system. This helps me succinctly describe and remember what the system does, and also provides something that I can hand off to other people, more acquainted with the system for review. And that leads to a more accurate understanding of it.

Write documentation:

When I run across code that seems important but doesn’t have good comments, I’ll often organize myself around writing some good documentation for it. Maybe I run into a class and don’t know what it does, so I read it enough to come up with a quick explanation. Then I read it in depth to see if that explanation is actually true. I find similar things helpful for sussing out functions, especially ones with undocumented inputs. I explain the expected constraints and meanings of each of the inputs, and then the meanings and assurances on the outputs. I like JavaDoc (or, as I work on C++ code, Doxygen) comments for these – they live in the code and are easy to update together, but are also extracted into nice formatted documentation.

Effect Sketches:

When I want to see how a change propagates through a system, effect sketches help me diagram and keep track of what leads to what. I learned about these from Feathers’ Working Effectively with Legacy Code. I start with a change, and then draw out a network of what that affects, so on down the line until I see its impact.

Change it

Add a test case:

When I want to know for sure something works the way I think it does and it looks decently testable, I’ll just check out the right files and add a new test case. And, as an added bonus, I know more about how to use it. As a triple added bonus: more tests!

Change something – anything – and see its impact. Then revert the change:

When I just want to know that I’m changing the place that I think I am, I make a small change and run the program to see it. Maybe it’s just changing a window title, or adding 42 to a calculated value, or printing out some logging statements with background calculations. It all gets me more comfortable with changing and rearranging the code.

Refactor some code to make it clearer:

When I’m reading code and think it could be better organized some other way, I’ll sometimes refactor it so that it’s clearer and more self-expanatory. I don’t just refactor everything I don’t agree with, but I do try to make right things which were particularly hard for me to figure out. Sometimes I’ll also just leave a note: // TODO: these five classes do the same thing – they could be refactored to remove duplication

Find a small bug and fix it:

When I have a whole big system to acquaint myself with, and I just don’t know where to start, I go with something useful: a bug. Figure out a subsystem enough to see where it goes wrong in the given case, and figure out the design enough to see where the fix should go. It’s a good idea to find someone else who can judge whether things are small enough for a starter project.

Find a small enhancement and do it:

When the bugs are too complicated or not in the right spot, find a little enhancement and implement it. Maybe it’s something as small as adding some keyboard shortcuts or more robust UI behavior, or better error messages. There’s probably someone on the project who knows a few common requests you can start with.

This entry was posted in The Craft and tagged , , , , . Bookmark the permalink. Both comments and trackbacks are currently closed.