This post is part of a blog series called Leveling Up: A Guide for Programmers. The series covers skills you can use to learn faster, more easily, and more strategically as a programmer.
Two posts ago we talked about warmup activities to help you get started on your independent practice time each day. That post introduces the idea of commit tracing. I promised a deep dive on commit tracing later. This is that deep dive.
What is commit tracing?
Commit tracing is my favorite warmup activity, and it’s my default any time I have access to the critical resource for it in my field of study.
The critical resource: a fairly complex code base that exemplifies my topic of study and has clearly named, well-circumscribed commits.
Fairly complex: If I wanted “Hello World” for a language, framework, or library, I could find that in the documentation. But I quickly move past that level: I want to dig into the interesting, complicated, real-world use cases. I want to see how my software of choice interacts with other software, and I want to compare the scopes of different kinds of changes.
Exemplifies my topic of study: When I was learning Android, I traced commits on a fairly complex Android app. Note that I wasn’t tracing the code of the Android framework itself: I didn’t want to replicate the Android framework. What I wanted to do was write Android apps. The reason I don’t trace commits right now is that my current project involves building an API that relies on natural language processing to inform some of its critical data, and I have not found open source examples of any such apps beyond “Hello World.”
Clearly named, well-circumscribed commits: I want my example app built and maintained with commits that concisely but completely explain what change they make (clearly named). This allows me to pull up
git log and choose a commit that makes a change that sounds relevant to my interests. I also want each commit to contain all the code changes that contributed to the stated change and not to contain code changes that did not contribute to the stated change (well-circumscribed). I am learning: I want the advantage of understanding exactly how a given change was made without the distraction of distinguishing unrelated changes.
This type of application sounds like a tall order; it is. Here is where I have had the chance to trace commits the most: when I am joining an existing development team to work on an existing application. In that case I have an app with a real-world level of complexity: it’s the app I’ve joined the team to develop. It’s in my field of study, which is how to write apps like this. The commit hygiene is up in the air, but I have gotten lucky. Plus, since I work with the team, I could theoretically ask them about pieces of commits if necessary.
How do I trace a commit?
Commit tracing adheres to both of my conditions for a good warmup activity: it is formulaic, and it includes writing. Let’s talk about what it means to trace a commit.
It’s formulaic: To trace a commit, I type
git log. I pick out a commit that is relevant to my interests. Then I soft reset my repo to that SHA, open my IDE of choice, and look at the list of files where things changed.
I choose the change closest to the front end of the code. That’s the UI level (if it is an end-user application), the API level (if it is an API application), or the import statement (if it is a library that stays exclusively on the front end or back end of an application).
It includes writing: I take a blue pen and I write down the name of the file containing that change.Then, I copy the code that was added or changed, line by line.
I go to the next file down: either the next one deep in the stack, or the one containing the code that was called in the first file. I copy the name of that file, then I copy the code changes again, line by line.
So on and so forth to the bottom.
As I go, I use a highlighter marker to mark calls to tangential collaborators that I don’t understand (alternatively, I’ll circle them). Those can be helper classes within this app or calls to libraries. I’ll come back to these later.
In pink or purple I’ll write in any comments or context I have: why I think something was done a certain way, where I have seen this same technique used before, how the code I’m looking at differs from what I expected to see.
In red, I will write any questions that I have about this code.
Once I have copied down all of the changes that comprise this commit, I go back and look at my notes again.
With the help of my IDE, I’ll go look at the implementations of the calls I highlighted in yellow. I’ll add comments on what I learn from this exploration in black. Sometimes, I copy down some of the code from these implementations, also in black.
Then, I look at my red questions and I’ll search for answers by (in no particular order) browsing the internet, referring to books, asking my mentors, or asking other members of the team. I’ll write down the answers beneath the questions in black.
The result: an annotated record of a feature’s worth of code and what I learned from it. Once I have traced a commit, I will probably never look at the tracing again: I learned new material through the act of tracing, and I am unlikely to forget the most important pieces of the tracing itself. I do keep all my tracings, though, just in case :).
Here is a tracing I kept from a Spring app, so you can see what the tracing looks like:
Why would I trace a commit?
This is a high-bandwidth activity, and I have heard others refer to it as ‘intense’ and ‘extreme.’ Look, I’m not forcing anyone to do it. All I know is that this is one of the tactics that got me from brand new to office-level expert on Android and iOS in a little over a year. That’s not just over a year separately for each; it was just over a year for both. I know for myself that this technique helped me immensely: probably more than any other technique.
Once I start tracing, I find it most beneficial to keep going until I have finished the copying, highlighting, and commenting step for the entire commit. Then I can switch to something else and come back later to looking at the collaborators and answering the questions, if I want to. I can control the time commitment by choosing the scope of the commit I choose to trace: a narrower one with only a few changes takes less time, and a broader one takes more time.
A commit tracing warmup doesn’t take much preparation, either. At the end of a practice session, when I am preparing this warmup activity for the next day, I’ll usually do so by browsing the commit history of the project I’m tracing and picking a commit.
Commit tracing is my favorite warmup activity, and it’s one of my top tactics for accelerating my skill development as a programmer. It requires a fairly complex code base that exemplifies my topic of study and has clearly named, well-circumscribed commits. That can be hard to find, but the particular case where I find it the most is when I’m starting a new job. In that situation, I can often use the existing code base as fodder for commit tracing.
I choose a commit that adds an interesting feature, I look at the diff for that commit, and I copy the code by hand into a notebook. I add comments that detail my thoughts on the code, mark APIs that are new to me, and write down questions I have about this code. When I’m done copying, I go back, learn a little more about the marked APIs, and write that in. I also go looking for the answers to my questions, either on the internet or from team members who know more about this code than I do.
I don’t end up needing to look at tracings again, usually: instead, the act of making the tracing helps me understand the choices that previous programmers made so I can replicate (or deviate from) those choices myself in the future. Commit tracing takes time and effort, but of all the tactics I use to learn, commit tracing has among the highest returns on my investment.