Nothing beats reading the source code when it comes to truly understanding how a software product functions. The code doesn’t lie - it is the ground truth showing exactly how things work in production right now.
When discussing potential solutions to problems or new capabilities to build, reading the code helps both engineers and technical product managers speak the same language - literally the language of code. It’s not just about consistent terminology but also tracing the logical flow of the algorithms. Having a solid grasp of the existing codebase allows a team to communicate much more efficiently, avoid misunderstandings and make the necessary trade-offs to move forward.
Luckily, reading code doesn’t require the same programming skills as writing it. With today’s tools, even with limited coding experience you can follow along. Modern AI systems can provide plain English explanations of what code is meant to do in a matter of seconds. Paired with a visual debugger, anyone can step through the code line-by-line, inspecting variables and expressions as they change. Some languages like Clojure for example have first class REPL (Read-Eval-Print-Loop) support which takes the interaction with a running program to the next level.
When dealing with a web application that you are unfamiliar with, a great strategy is to start from the HTTP request and trace it all the way to the final response. This ensures you understand the full flow and helps you develop a general idea of how request routing and handling is implemented in this application. This will help you the next time you dive into the application code, as you will be navigating it much more comfortably.
Another strategy is to read through database schema to understand current data structures in use. Use any local database query and visualisation tool to inspect the database tables and columns in depth. For example, if your database is Postgres you could use something like pgAdmin. Once you have a good grasp of how data structures relate to each other, try to find how they are used in the source code to confirm your newly developed mental models. This end-to-end view from data to code strengthens your understanding and allows you to reason clearly about potential changes.
When diving into a new area of your codebase, seemingly cryptic variable names, function names, method names, class names, and component names can be confusing at first glance. However, these names were chosen for a reason - they encode information about the original intent of the author. If you find yourself lost, take a moment to really think about why each name was chosen. This can provide crucial context clues to understand the broader purpose and flow. Reasoning through the naming can shed light on the big picture view.
If certain sections of code seem completely mysterious to you, don’t be afraid to ask teammates for help. Use git blame to find the original author and ask them to clarify their thought process. However, try to deeply understand the code yourself first before seeking help. Take notes on unclear parts as you read, and batch questions for more efficient discussions later. The journey of incrementally unravelling complex code builds confidence and engagement.
While documentation and verbal explanations provide helpful context, there’s no substitute for diving into the code when you really want to know how an existing system actually works under the hood. It allows much more fruitful discussions on the best path forward and serves as a great alignment tool within the product and engineering teams.