Navigating a Codebase like a pro

April 01, 2019 ■ 6 min read

Sorry, if that sounds like a clickbaity title. However, I think this tip is really helpful when it comes to navigating a codebase quickly/clearly. This has helped me navigate any code with clear intention whether I'm reading my own code written few months back 😅, legacy code, 3rd party library code, etc.

Before we get started, I would like to tell you that tip is mostly about using Go Back / Go Forward shortcut that most IDEs/Editors provide to let you jump back and forth in code and not some cool magic I'm gonna teach you to get good at reading code 😉.

Note: Most of you might be probably aware of this shortcut in your favorite IDEs/Editors and might be using it daily. So, if you're are one of them free feel to close this browser tab 🙂


To those who're still reading...

You all might have heard following Robert Martin's quote on reading code

"Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as part of the effort to write new code. ...[Therefore,] making it easy to read makes it easier to write." - Robert C. Martin

We all tend to agree with above quote that we spent more time reading than writing code. We cannot write a new piece of code without reading old code around it (No matter whether old code is neatly written or not).

However, most of the time when we start writing a new piece of functionality around old code we find it way too much difficult to navigate and connect the dots making sense of things written by last programmer(It could be even you 🙈). And, one of the significant reason IMO for this difficulty is when reading code we need to keep context of how the entire code flow is and go back and forth between those points frequently. Keeping that context is not as easy it sounds.

For example, this is how we generally read the logic flow.

Consider that I'm working on an Android codebase and need to do some changes in Sort functionality in below class. First thing we would definitely do before we start making changes is we try to understand the existing implementation of sortData() function.

class ProductsActivity() : BaseActivity() {
    //...

    //Triggered when user selects particular sort criteria
    fun sortData(query: String) {        compositeSubscription += 
        viewModel.sortData(category_id, query)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                    { Timber.d("Sorted by $query") },
                    { error -> Timber.d("Error sorting by $query : ${error.message}") }
                )
    }
    //...
}

Step 1

We would use our beloved shortcut Cmd+Click to check how sortData() is implemented in our ViewModel class

class ProductsViewModel @Inject constructor(val repository: Repository)  {
    //...
    fun sortData(parent_id: Int, query: String): Single<List<Product>> {
        return when (query) {
            MOST_VIEWED_PRODUCTS,            MOST_ORDERED_PRODUCTS,            MOST_SHARED_PRODUCTS              -> sortProductsByCriteria(parent_id, query).compose(sideEffect())            CLEAR_SORT_FLAGS -> fetchData(parent_id)
            else -> throw IllegalArgumentException("Invalid sort criteria selected")
        }
    }

    private fun sortProductsByCriteria(parent_id: Int, query: String): Single<List<Product>> {        return repository.sortProductsByCriteria(parent_id, query).subscribeOn(Schedulers.io())    }    //...
}

Now, over here we see that whenever user selects any sort criteria we call our sortProductsByCriteria() method in our viewmodel which in turn calls our repository class method.

Step 2

So, we continue our journey of Cmd+Clicks to understand how actual data sorting is done. Whether our repository class method is getting this sorted data user is asking for directly from API or from local cache?

class Repository {
    //...
    fun sortProductsByCriteria(parent_id: Int, query: String) =        realmService.sortProductsByCriteria(parent_id, query)    //...
}

Step 3

class RealmService {
    //...
    fun sortProductsByCriteria(parent_id: Int, query: String): Single<List<Product>> {        return Single.zip(            fetchProductRankings(query),            fetchProducts(parent_id),            this::generatePair        ).flatMap(this::sortByViewCount)    }    //...}

Finally, we reach our cache service class Phew!. We can see and try to understand the sort implementation done in our service layer.

But, WAIT!! How do we ended up here? How can I go back to understand the entire flow once again or one of the intermediate step that led us to this method?

You would say easy-peasy, lets use our beloved shortcut Cmd+Click again by keeping cursor on sortProductsByCriteria() method in our service class to understand where it is called from.
And, then you see this.

Multiple entry points

And, you are like:

Sweaty guy

Not really, but you get the gist 😅. You might be able to go back and open the last file and see from where you came. However, all this process of clicking files then locating that intermediate method in the flow we wanted to check out makes it hard to navigate and understand things without losing context of what we were trying to achieve in first place. And, Good luck trying to understand 3rd party lib code using this method.

The point i'm trying to make over here is that instead of keeping entire flow in memory and doing this nonsensical work why not rely on wonderful shortcut our IDEs/Editors provide us to navigate back and forth between code cursor positions.

Yes, the shortcut I'm talking about is generally named Go Back / Go Forward. This lets us jump back and forth using last cursor positions. In the above example, I can easily navigate back and forth between activity <-> viewmodel <-> repository <-> service class to understand that entire sorting flow more clearly using just 2 shortcuts Cmd + [(for going back) and Cmd + ](for going forward)

Whether you are using Android Studio/ VsCode/ Sublime/ Xcode/ Any other editor it should work in all of them. And, by default in Mac(for most of them), hot keys for this shortcuts are generally

  • Cmd + [ (for going Back)
  • Cmd + ] (for going forward).

Note: Shortcut could be different. Check your Keymap in Editor settings.

For Window and Linux users, you might want to check Keymap settings of your Editor as well and set hot keys for above shortcut as per your convenience.

This is how it looks in Vs Code keyboard settings:

Go back Vs Code shortcut Go forward Vs Code shortcut

And, for Android Studio:

Go back Android Studio shortcut Go forward Android Studio shortcut

Closing Note

I've been using this shortcut in all IDE/Editors that I use and it has significantly improved my code reading capability when it comes to reading legacy code, code written by me few weeks/months back or 3rd party library code. Sharing this in the hope that this would useful to someone who is not using it currently or is unaware of it.

That's it folks! Thanks for reading 🙏

Want to be notified of similar posts? Follow me @punit__d on Twitter 🙂