Understand an Unfamiliar Codebase Before Asking an Agent to Change It

Drafted and reviewed using AI assistance.

On this page

Before asking a coding agent to modify an unfamiliar repository, ask it to trace one user action through actual files. The useful first result is an evidence map: where the action starts, which functions it calls, what data they read and write, and which parts remain unknown.

A repository overview can help you choose where to look. It becomes actionable when you can check each important statement against a file and a symbol. GitHub's guide to exploring a codebase includes questions about entry points, data flow, build instructions, and supporting evidence. The worked method below turns those questions into a small investigation you can reuse with your coding agent.

Start with one path through the application

Suppose you inherit Parcel Report, a fictional command-line tool. Your first question is: “What happens when someone produces a report from a JSON file?” That is narrower than “Explain the whole architecture,” and gives the investigation a stopping point.

All files, names, and examples below are original teaching material. The snippets have not been executed; they are not extracted from CueSpec or another working project.

parcel-report/
  README.md
  parcel_report/
    __init__.py
    __main__.py
    io.py
    totals.py
  data/
    demo.json
  tests/
    test_totals.py

The relevant README excerpt is:

Run from the repository root:
python -m parcel_report data/demo.json

Suggested check:
python -m unittest discover -s tests

The CLI reads a JSON list and writes a summary to standard output.

These are documented instructions, not evidence that either command succeeds on your computer. No packaging or release-build configuration appears in the supplied tree.

The entry point is parcel_report/__main__.py:

import json
import sys

from .io import load_orders
from .totals import summarize


def main():
    rows = load_orders(sys.argv[1])
    result = summarize(rows)
    print(json.dumps(result))


if __name__ == "__main__":
    main()

parcel_report/io.py supplies the input:

import json
from pathlib import Path


def load_orders(path):
    return json.loads(Path(path).read_text(encoding="utf-8"))

parcel_report/totals.py performs the calculation:

def summarize(rows):
    return {
        "orders": len(rows),
        "units": sum(row["quantity"] for row in rows),
    }

The example input in data/demo.json is:

[
  {"id": "order-a", "quantity": 2},
  {"id": "order-b", "quantity": 3}
]

The supplied tests/test_totals.py contains:

import unittest
from parcel_report.totals import summarize


class SummaryTests(unittest.TestCase):
    def test_two_orders(self):
        self.assertEqual(
            summarize([{"quantity": 2}, {"quantity": 3}]),
            {"orders": 2, "units": 5},
        )

Fill the evidence map before describing the architecture

Question Answer supported by these excerpts File and symbol Evidence boundary
Where does the CLI begin? Module execution calls main() parcel_report/__main__.py, main guard Entry path is visible; invocation has not been tried
Where does the path come from? The first command-line argument __main__.py, sys.argv[1] Missing-argument handling is absent from this excerpt
What reads the file? load_orders reads UTF-8 text and parses JSON io.py, load_orders There is no schema validation in this function
What does calculation use? List length and each row's quantity totals.py, summarize Other row fields do not affect this calculation
What leaves the program? A JSON serialization printed to standard output __main__.py, print(json.dumps(result)) No output-file write appears on this path
What behavior does the shown test express? Two quantities, 2 and 3, produce 2 orders and 5 units tests/test_totals.py, test_two_orders A written assertion is not a recorded test result
What explains how to run checks? A suggested unittest discovery command README.md, Suggested check Its execution and environment remain unverified

The corresponding map is small enough to inspect:

CLI argument
  -> __main__.py:main
  -> io.py:load_orders
       file text -> parsed JSON value
  -> totals.py:summarize
       rows -> order count + unit sum
  -> __main__.py:main
       result -> JSON on standard output

By manual inspection, the supplied input should produce two orders and five units. That is a prediction from the snippets, not captured terminal output.

Notice how the map exposes a possible boundary: parsing JSON does not itself establish that the result is a list of valid order records. This observation can guide a follow-up question without authorizing a validation rewrite.

Keep three unknowns visible

The intended quantity rules are unknown. The example uses positive integers. Neither the example nor the test establishes whether zero, negative quantities, or fractional quantities are legitimate. Do not turn a sample into a business requirement.

The supported runtime and release process are unknown. The README supplies commands, but the given files do not establish a supported Python range, a distributable package, or a release-build procedure. Ask for the relevant configuration if that becomes necessary.

Actual execution behavior is unknown. No command has been run here. You cannot infer that tests pass, that error messages are usable, or that every checkout contains the same files from the apparent simplicity of the code.

Unknowns make the next decision clearer. If the desired change concerns quantity rules, resolve that requirement first. If it concerns packaging, the current evidence set is insufficient.

Copy this read-only investigation request

Investigate the current Parcel Report checkout without changing it.

Question:
Trace the documented CLI invocation from its entry point to its output.

Start with:
README.md, parcel_report/__main__.py, parcel_report/io.py,
parcel_report/totals.py, data/demo.json, tests/test_totals.py.

Return:
1. A file tree limited to this execution path.
2. An entry -> call -> data -> output map.
3. An evidence table with exact file paths and symbols; add line
   references from the files you actually read.
4. Documented run, check, and build instructions, separated from
   instructions whose success you have verified.
5. Unknown requirements and the smallest additional evidence needed.

Read source text only. Do not install dependencies, import project
modules, execute scripts or tests, edit files, or create a commit.
If a listed file is missing, say so. If you need another source file,
explain which question it answers. Do not read credentials or private data.
Describe only the traced path; do not claim to understand the entire repo.

If the agent cannot read a file, the corresponding row stays unverified. Supplying a filename in a request does not prove the file entered the agent's context.

Convert one resolved question into a change brief

Now suppose the fictional maintainer supplies a new requirement: quantity inputs to summarize are integers; negative integers must raise ValueError; zero is valid. This is new example information, not a conclusion from the repository.

Change request: reject negative integer quantities in summarize.

Requirement source:
Maintainer note Q-2, supplied with this request:
Inputs in scope are lists of records with integer quantity values.
A negative quantity must raise ValueError. Zero is valid.

Allowed files:
parcel_report/totals.py and tests/test_totals.py.

Preserve:
Function name, input shape, output keys, positive/zero behavior,
and the existing two-order example. Do not change the CLI, JSON
loader, dependencies, packaging, or type-coercion policy.

Checks to propose or run in the authorized development environment:
Existing two-order case; zero quantity; negative quantity alone;
negative quantity among otherwise valid rows; empty list.

Return a small diff and distinguish inspected code, manual expectations,
commands actually run, their results, and any remaining uncertainty.
If the current files differ from the map, describe that difference first.

The first investigation ends when you can explain the chosen execution path and its unknowns. The next request begins when the behavior to change has a requirement source and a bounded location.

For the conversation-routing step in a CueSpec workflow, see check the intended conversation. The evidence map above is a manual investigation artifact you can use with your existing coding tools.