If you’ve ever joined a new project you probably know the excitement of being presented with a brand new source code. As soon as you’re assigned your first tasks the excitement turns into frustration, because it’s not easy to fix this simple bug… Even you’re an experienced developer.
Today I’m trying to address the issue of approaching an unknown codebase. I’ll give you some advices and tricks I’m using to make new code easier and quicker to understand – even without understanding it all 🙂
Understand the architecture
For me, the first thing to do in a new project is to understand the software architecture. My goal here is to as quickly as possible find answers to the following questions:
- what is a client? Is it a desktop, mobile app or both? Maybe there’s only a web app, but used on different devices?
- what is a server? Is it a single instance or some distributed service? Where does it run?
- how do the client and the server communicate? Is there any framework used (e.g. WCF) or maybe just some RESTful web service communication?
- are there any architectural design patterns used by the team in both clients and server apps (e.g. Layered Pattern, MVC, MVVM, CQRS…)?
- what are the important frameworks/libraries used in the clients and server apps? By important I mean these ones which directly define the style of coding, for instance: ASP.NET MVC, WPF, Caliburn.Micro, Prism, Spring, Angular, React, TypeScript etc.
Answering these few questions will give you a feeling of where is what 🙂 When working with some tasks you’ll automagically feel where the issue may be or where to look for a solution (or problem 😬).
It’s sometimes helpful to draw the architecture on a paper or whiteboard to help you easier grasp the elements and connections between them.
Learn the persistence
Each application stores its data somewhere and you should know where and how it does it. At least it’s a bit helpful 😁
First of all, find out what kind of database is used by the software. Then check how the applications (clients, server) communicate with the database. In other words: find out in which place the queries are sent to the database. I really mean it – if SQL db is used, find out where exactly the SQL statements are generated and sent to the server. Maybe there’s some ORM (like Hibernate/NHibernate or Entity Framework) used for that?
Next, find out how is the persistent data (e.g. tables) represented in code. Where are the persistent classes defined? How are database restrictions (not-null fields, primary and foreign keys, constraints, data types) defined on them? How are the objects’ fields mapped to database tables’ columns?
In the end, try to understand how these persistent objects are presented in the GUI. Are there any DTO objects used? How is a business validation (e.g. maximal length of a field or email validator) defined for these objects (maybe some validation library is used)?
Knowing the persistence layer and its functioning in the software will help you work with your new, yet unknown codebase. You can often be assigned to issues related to database limitations or business validation and knowing where is what can significantly ease your pain 😉
Learn to deploy your system
It may sound obvious, but you should quickly learn by heart how to deploy your system (or part of the system you are working on) from scratch.
Compile the whole code and learn how to run it all (server and all clients). If possible, start with a clean database as a new customer would. If the process is complex write down the step-by-step instructions while your colleague explains the process to you. It’s very important that you practice it yourself and finally are able to go through this “deployment” process even without your notes.
Know the system functionally
Among the first steps for approaching an unknown codebase you should… put the code aside and focus at least a bit on the functional side.
As soon as the whole system is running, spend some time clicking through the application. Try to grasp how it works, what’s the feeling of the system.
If possible, find someone who has functional knowledge about the software – preferably a consultant or customers’ trainer – and ask him/her for a live demo. It would be best if they can give you an introductory demo as they do for end customers. Ask as many questions as come to your mind during such a presentation. If possible, leave some communication channels open with the person who trained you, e.g. a chat where you can leave your potential doubts/questions. Ask him/her to answer them from time to time. Also, check with the presenter if there are any resources on the problem domain of the system.
The better you understand the problem domain, the better your technical decisions are going to be. Sometimes you don’t need to understand everything – even consultants or designers may not always know what clients are really using the system for. However, it’s important to understand as much as possible from a functional point of view. Such a perspective helps you think differently when you’re asked to implement a feature that initially sounds like nonsense. Maybe that’s really what customers need to solve their domain problems? 🤔
Explore source code tests
This part becomes more meaningful when you’re assigned your first tasks. I don’t recommend exploring all the tests in your application before you start working – except if these tests are veeeeery good and really human-readable (which I’ve honestly never seen so far 🙂).
As soon as you’re starting to work on some task you may have the feeling that you completely have no idea how the related part of the software works. If that’s the case, try to look for tests (unit, integration tests) in the code related to this feature. If your “issue” is related to a need to make some changes in a particular method, try to find this method’s usages – hopefully, it’s called by some tests:
Exploring the tests can give you an idea of what an author-developer had on his/her mind when writing this code.
Of course, as soon as you make any changes in the code, run all tests to make sure you didn’t break anything 😉 Especially the tests related to what you’ve changed. The best obviously is when you have a build server that runs the tests for you.
Have software documentation around
If there’s any documentation for your new software – have it somewhere around. Don’t read it – except, again, if it’s veeeeery good and interesting (anyone has ever seen such docs?! 😂). Just keep it in case you need to find out how something works. You can always search such documentation by phrases/keywords related to the task you’re working on. Maybe it’ll help you to at least reproduce the problem 🙂
Document your actions
When I work with an unknown codebase I tend to keep track of what I’m doing. This is also one of my productivity tricks. I create a new project in Nozbe (tasks/projects management application which I’m using) for each task I start working on. It can also be a new Notepad file, a new tab in Sublime Text or just a piece of paper. In such a place I write down what I’m currently working on. If I have some exception’s stacktrace I’m trying to analyze – I paste it there. I write down some objects and try to picture the relations between them.
Simply writing down stuff in a “natural language” often helps me find a solution quicker. Have you ever started to ask someone for help and as soon as you explained to him/her your issue, you suddenly got an idea of how to solve it? It works similarly with writing things down – it’s like explaining stuff to yourself. Just give it a try 🙂
Obvious, right? Well, not necessarily. I often see people debug only when they know exactly what to debug. When working with an unknown codebase I find it helpful to break through the application’s startup. Put a few breakpoints in one of the application’s entry methods and debug how it launches and initializes line-by-line.
It will help you to see what components are used and how are these loaded.
Do the same with given actions you want to understand. If there’s some button that causes an issue later on (not directly after pressing it), put a breakpoint in a method that is assigned to a button and try to debug step-by-step until the exception is thrown. It will let you understand how the codebase is built and what the developer exactly intended to do with this button.
I think most of us do that, but very few confess 😂
When working with an unknown codebase I very often search for things literally. For instance, when working on an issue occurring on pressing a button it may not be easy to find the code-behind the button (a method which executes as soon as you click it). In such a case, I just copy the text which is a button’s text or name and search by it literally in the whole codebase. It can even be one of the translated resources – their values are often stored somewhere (e.g. in XML files). This often allows to quickly identify a method attached to the button just by finding the string or the translation key’s usages 🙂
If you don’t have any GUI element with text to search for, try to search by intuition 😉 For example if you’re trying to find a code that handles saving a document, just search by strings like “SaveDocument”, “AddDocument”, “UpdateDocument” etc. It can greatly help you find what you need!
If you’re using Visual Studio with ReSharper there’s a really useful “Search everywhere” feature which allows you to easily search strings literally in the whole solution:
Don’t be afraid to ask
Not asking is a common issue, especially amongst experienced developers. Programmers are often afraid of asking questions. New guys in a team tend to think that it would be a shame to ask someone for such easy stuff… But hey, you’re new in the team and it’s perfectly fine to ask for anything that comes to your mind! 😉
I’d say even more – by asking questions you show your engagement and a will to really understand what you’re doing. Sometimes digging into an issue ourselves can be much longer than doing it with a little bit of help from the project’s seniors. So by asking them to aim you in the right direction you’re also saving yours and your company’s time 🙂
When working as a team leader I preferred to have people in my team who asked questions to silent on-my-own geeks. Of course, you should first try to find the solution yourself, e.g. using one of the tips described above 👍 However it’s always better to ask and get the answer or be aimed in the right direction in 5 minutes than spending the whole day fighting with the problem. It would also make your day much better 🙂
I hope these few tips will be useful for you 🙂
Don’t be afraid of an unknown codebase. Don’t try to know it all straight away – you don’t need to and even shouldn’t 😉 If the codebase is huge, you’ll potentially never know it all. Just grasp the main concepts and architecture. Be in touch with your teammates and don’t be ashamed of asking them questions – with time, you’ll ask less 🙂
I think that one of the factors showing a developer’s experience or seniority is the ability to work with code he/she doesn’t know. Very often to fix a bug you need to change a single line of code. The trick is to find that line 🙂
I’m very curious what are your techniques for approaching an unknown codebase? Do you have some tricks to quickly find yourself in legacy code you don’t know? Share your tips with us in the comments!