I have had good success to learn new code bases with the following approach. Strive to understand:
- Purpose: understand what the system or framework does, conceptually speaking
- Architecture: create a view on the overall architecture of the system or framework
- Components: learn how major parts (components) work together
- Code: now you are ready to take deep dives by debugging major flows through the code base, e.g. while fixing a bug
Here's the rationale for each of these steps, and how you could approach them. Note it may take you several iterations going from step 1 to 4. Take your time.
Purpose
This may sound obvious, yet I find that even experienced developers sometimes lack a basic understanding of what the system's main purpose is. Strive to learn the business concepts free from technology first (i.e. ask questions like: "this system processes customer orders and issues invoices -- what is a customer, how do orders and invoices look like, what does it mean to issue an invoice? Who is using the system? What do they need to do with it?" etc.)
Knowing the conceptual level without specifically looking at technology is a major plus in getting to understand its inner most workings -- code is merely a means to an end and without knowing the end all code is essentially meaningless, and so it will appear. If you don't have a conceptual understanding yet, ask someone to explain. If you think you do, explain it to someone more senior on the project. You will learn a lot either way.
Architecture
Next, strive to understand the overall architecture. This is at a technology level, but without looking at the detail implementation. Examples of the architecture level are:
- tiers - e.g. web tier, business tier, database tier, front-end, backend, ...
- layers - e.g. user interface, business model, service interfaces, ORM,...
- major components - e.g. order processor, invoice generator, customer manager, ...
- infrastructure - e.g. network setup, servers, message queues,
Again, if you don't know any of this yet, ask someone to give you an overview (within at most 1-2 hours to avoid getting lost in details). You will benefit from documenting the results of such an introduction, start with simple diagrams first (maybe using Visio or similar tools), and over time develop the ability to use a more formal approach such as UML. The process of documenting may sound tedious and useless, yet it is uterly useful as it is not the result that counts but the thinking process that goes into drawing the diagrams.
Components
Looking at the major components, understand how they work together to achieve the purpose of the system. Go back to the conceptual level and map the concepts and what users do to each of the components. Draw flow charts or sequence diagrams to see how data flows through the system. Again the main purpose of drawing is not the diagrams as such, but to force your brain to think it through.
This is also a good time to map components to code. Usually such components are made of several parts (e.g. several programs, classes, database scripts or whatever code artifacts your system uses). It is good enough to understand, for each component, where the respective code is located -- later on you can still dive into details.
A note of caution: it may be that your co-workers don't have a notion of "components" other than actual code artifacts -- that's not untypical if someone is deeply immersed with the code base. Try to ask questions such as "looking at the code base overall, are there areas such as programs/packages/classes that belong together? What is their purpose, as a group? Which of these need to work toegether to achieve X?"
Code
Take one particular entry point into the system - e.g. a user interface, a web service, an API etc. and set up a debug session. Then step through the code, always paying attention to which component the currently executed code belongs to. In your mind, map this actual flow back to your earlier flow charts and maybe update them if you find missing gaps.
Instead of stepping through the code in a debug session you could also use a profiler. That is, a program that records the flow for you and displays a hierarchy of all subroutine calls.
Another alternative is to use a code analyzer / reverse engineering tool that is able to decern a hierarchy of dependencies. However in my experience these tools generate too much data to be useful for the novice, that's why I would prefer the debugging approach.