10 thoughts on orienting yourself in a new codebase
Orienting yourself in a new (large) codebase.
I recently started a new job and found myself in the familiar position of having to find my way through a large codebase of code I didn’t write, which was written for reasons I don’t quite know yet, under conditions that I also don’t know.
I don’t know which parts of the code are known problem areas, which parts are well understood by other developers on the team, which parts are up for a refactor and which parts probably will never be touched again. Terra Incognita. Beyond here lie dragons.
I thought I’d write up a few thoughts on how I tread water in a new codebase and make some contributions while I’m still trying to figure out what’s going on.
#1) Get it running
Try to spin up the apps, install dependencies, seed databases, configure network connections, etc through whatever information is there. It probably won’t quite work. It’s notoriously hard for a bunch of developers with a working app to keep up with any little tweaks they had to make along the way. So…Document Everything. Something doesn’t work as expected, document it. Have to figure out how to install some library to get a dependency to install, document it. Can’t seed the database, document it. Seeded users don’t have the password in the README anymore, document it. This is the first place where you are super useful! Your lack of knowledge is your super-power that reveals deficiencies in documentation and build scripts. Use it for good. Being able to reliably spin up a development version of an app is pretty much task #1 for a development team, and yet it often falls by the wayside. Bring it back into the light.
#2) Be humble
You will, no doubt, think some of the code sucks. It will seem too clever, or too simple. It will not have enough tests. It will be overly verbose. It will be heavily coupled. You will think immediately of ways you would have built it better.
You would also think this looking at your very own code a few months later.
People coded this thing the best they could, with the constraints they had. Presumably, it accomplished something and now you are working on it as well. Someday, someone will look at code you wrote without context and decide it is terrible.
Don’t be that person. Use your beginner’s mind to offer improvements, as mentioned in #1 it is your super-power. You do not have the same biases as the coders working on this app for months or years, nor the same blindspots. So give your fresh eyes. But you also don’t have the context for decisions that were made, so try not to assume you would have made better ones!
#3) Golden path
(hopefully through an end-to-end integration spec)
Find one of the key functions of the app. Just one. Maybe a key index view or API endpoint. Maybe a function that seems to do /a lot/ of stuff. Hopefully, there is a test that tests the golden path to that endpoint (ignore distractions for now. Helper methods, error handling, syntactic sugar). If not, you may just write one (see #4).
Now run that test and start tracing backward. Find an output, use your code reading and debugger skills to find the inputs. Now those inputs are your output. Find the inputs. Repeat. Remind yourself that it is just a big logic puzzle. Nothing mysterious is happening here. Everything is specified somewhere. Nothing is happening on its own (okay, ignore library magic, meta-programming, and tons of included dependencies here…those make it harder, but they are also just…more code)
#4) Write tests
A great way to not introduce any bugs to code you don’t understand but still add tremendous amounts of value while you learn about an existing codebase is to add tests that document existing functionality. There are going to be gaps in test coverage. Knowing most dev teams, unit tests will be pretty great, but as you move to higher levels of abstraction, the coverage gets a little sketchier. Write some great integrations specs that simply document how things already work. You’ll learn a ton about the code as you figure out how to drive all the behavior you want to test, set up fixtures the correct way, etc. You’ll make it easier for yourself and others to refactor confidently in the future, and you won’t break anything, except maybe the test suite!
Read a lot. Not code, you’ll have to get through that anyway. Read past chats in Slack. Read commit messages. Read comments on recent pull requests. Read the schema file. Read incident reports. Read the documentation, and the changelog to that documentation. Do not worry about making sense of it all. You are just a little sponge. You are gathering all the context you can even though it doesn’t fit together just yet. You will be anxious that none of it makes sense, it’s okay. Your job is not to draw lines just yet. None of it has to make sense. Just keep gathering the puzzle pieces so in a week or a month you’ll remember that somewhere you saw one with a blue edge and a black bottom. Shove a bunch of context into your unconscious processors, it will be useful later.
Ask to pair. Even if no one pairs. Even if you are scared to pair. Even for 20 minutes. Be a backseat driver while someone codes in parts of the codebase that don’t even concern your job. Ask someone to pair with you even if they think they don’t know the part of the code that you are working on. Everyone on the team you are joining has a bunch of vital knowledge in their heads. Your job is to make it as easy as possible for them to transfer that knowledge to you. Often you can absorb a bunch of it while helping someone look for typos in their code, just by watching what files they are working with and how they are navigating the code.
#7) Ask questions
Just ask them. You don’t know which ones are dumb or not. Your questions are important because they will reveal things that are more obscure than the current dev team realizes. Your questions are important because you don’t have to know everything. You were hired already, the interview is over, everyone wants you to succeed, and good dev teams level each other up all day, all the time, without feeling superior.
#8) Have a good manager/project manager
You don’t have control over this unless you are one of those people, but if you are one of these people…find some good initial tickets for a new developer to work on. They should be non-mission-critical. You should expect some flailing and maybe a few false starts. They should be fairly simple in terms of functionality or bug fix, but if possible they should gently introduce someone to a large swath of the codebase. Ideally, you should know exactly how the problem needs to be fixed but only offer good hints.
“I think we have some weird nil values being passed into this method somehow, you might want to look at bar class and baz class because I think they may not be catching an error somewhere and passing through bad data somehow”
#9) Clarify expectations and ask for help
Talk about when people expect you to deliver your first code changes. Make sure everyone is on the same page. Revisit those discussions if it is taking longer than you thought. Ask for help, it’s not your responsibility to figure out how all the code works, it’s everyone’s responsibility to help get you oriented.
“Hey, I’m supposed to change this view but I’ve spent all afternoon looking at X and Y but I can’t figure out where this value is coming from, does anyone have a 1/2 hour to step through this code and help me figure it out?”
You being vulnerable about your confusion and allowing the team to step up and help you learn will also help the team level-up in mentoring, and maybe even help them understand the code better through explaining it!
“I…don’t know why that method is like that actually, we should probably fix that!” is a super-common part of explaining how code works to someone else!
You were hired or brought onto this project because people believed in you, not as a trial to determine how terrible you are at programming. Finding your way through a new codebase takes time. Sometimes, it takes a lot of time. It’s code. You know code. You may not know this code yet, but that also can bring value to the team you are working on. Be patient. Be humble. Keep one eye on the big picture and don’t be afraid to spend time doing a deep dive through one particular piece of functionality.