In the past; I’ve come into working with an existing codebase with the wrong attitude. I’ve labeled the codebase as “Legacy”; and used that label to excuse suboptimal delivery. It’s true that some codebases have less malleability than others due to poor design choices; but don’t let that unnecessarily discourage your process. What you have is a set of tools which you can use to decipher the behavior of the code; and to figure out the optimal path to change it to add your own complexity - keeping the future in mind while doing so.
These tools I speak of aren’t IDEs; or debuggers. The tools I’m referring to are the investigative processes which you have developed with experience.
As you’re following a codepath; you’ll come across what you will preceive as an unpenetrable obstacle. It could be a module that hasn’t been maintained since 2013. It could be logic that is handled in a downstream service; written in a programming language you’re not familiar with. Whatever it is; do what you need to break through and identify the behavior. Get the source; look through it; and attempt to identify the behavior. If it’s faster for you to read the documentation; read it - but if you need to look at the source to identify it; do that as well.
Don’t give up without a fight. The more you disregard walls; the more you avoid stopping at unecessary roadblocks.
When working with a system that is complex; the first thing that you need to get a handle with is interacting with it. When you’re following the codepaths; all you’re doing is generating a hypothesis on its behavior. To remove the uncertainty of your hypothesis; you need to be able to test it - and interacting with it is exactly that. If it has local integration testing set up that’s great; but if it doesn’t - try to script your tests if you’re going to be spending more than a few days working with it.