The fastest way to create risk in an unfamiliar codebase is to confuse activity with understanding.
It is easy to spend a first week opening files, following imports, collecting architecture opinions, and fixing whatever looks untidy. That work feels productive because it produces motion. It does not necessarily produce a trustworthy model of the system.
A useful first week has a different objective: replace the uncertainties that matter most with evidence. By the end of it, another engineer should be able to see how the system starts, how one important workflow crosses its boundaries, where the main unknowns remain, and why the proposed first change is safe enough to make.
The first-week deliverable is not code volume. It is a reproducible baseline, a traced workflow, an evidence-backed system map, a ranked uncertainty ledger, and one well-bounded next change.
Discovery should produce decisions, not a tour of the repository
You do not need to understand every module before contributing. In a mature system, that may be impossible. You need enough verified context to make the next decision without pretending that the remaining gaps do not exist.
I organize discovery around five outputs:
- A reproducible baseline: the exact path from a clean checkout to a running system and a known test result.
- A workflow trace: one meaningful user or operator action followed through its interfaces, application logic, data, and side effects.
- A system map: runtime components, data stores, external systems, and the connections between them.
- An evidence ledger: important statements separated into confirmed facts, reasonable inferences, and open questions.
- A first-change proposal: a small, reversible, observable improvement tied to a real need.
The order matters. It prevents an early implementation idea from becoming the lens through which every later fact is interpreted.
Day 1: establish a baseline without hiding the friction
Start from the same position a future contributor will have: a clean checkout and the access that the team has intentionally provided. Avoid borrowing a long-lived local environment whose undocumented state makes the application appear healthier than it is.
Record the runtime and package-manager versions, setup commands, configuration inputs, local services, database initialization, build steps, test commands, and the first successful request. If setup fails, preserve the failure before repairing it. A missing environment variable, stale seed command, or undocumented service is part of the system’s current behavior.
The result should be a short setup note that distinguishes three things:
- what the repository says should happen;
- what actually happened in a clean environment; and
- what intervention was required to close the gap.
This is also the time to identify the delivery path at a high level. Locate the continuous-integration workflow, container or build definitions, migration mechanism, deployment configuration, and secret references. Do not request production credentials merely to make local discovery easier. The access boundary is part of the design.
A baseline is complete when another developer can repeat it. “It works on my machine” is an observation; a documented clean run is evidence.
Day 2: trace one real workflow end to end
Repository structure shows how code is organized. It does not necessarily show how the business experiences the system.
Choose one representative workflow with a visible outcome: creating a booking, approving a request, importing a file, or sending a notification. Prefer a path that matters to users and crosses enough boundaries to expose the system’s shape. Avoid beginning with the rarest failure or the most complicated administrative feature.
Follow the workflow in execution order:
- Where does the request or event enter?
- Which validation and authorization rules apply?
- Which application component owns the decision?
- What data is read or changed?
- Which external calls or background jobs follow?
- What does the user or operator see?
- Which logs, metrics, traces, or audit records prove what occurred?
Use a debugger, tests, structured logs, local database inspection, and request capture as appropriate. Names in the code are clues, not proof. A class called BookingService may contain only orchestration while the decisive rule lives in a database constraint, event handler, or external service.
When telemetry exists, it can shorten this investigation. OpenTelemetry describes traces as the path of a request through an application, while logs and metrics provide different views of runtime activity. The useful principle is broader than any one tool: connect the code path to observable behavior rather than inferring the runtime solely from static files. See the OpenTelemetry signals documentation.
Day 3: map boundaries before diving deeper
Now draw the smallest map that explains the workflow. Start with people and external systems, then zoom in to the applications and data stores that must be running for the behavior to work.
The C4 system context diagram is a useful starting point because it deliberately keeps the first view broad: the system, its users, and the other systems around it. A C4 container diagram then adds the major applications, data stores, responsibilities, and communication paths. You do not need every C4 level or a specialized modeling tool. You need consistent names, labeled relationships, a visible scope, and a map that the team can correct.
For a fictional workshop-booking system, the first map might be this simple:
Customer ───────┐
├─▶ Web app ─▶ Booking API ─▶ PostgreSQL
Coordinator ────┘ │
▼
Reminder queue
│
▼
Worker ─▶ Email provider
This diagram does not claim that the architecture is good. It gives the investigation a shared vocabulary. The next questions become concrete: Who owns reminder delivery? Can the API commit a booking if the queue is unavailable? Where is time-zone conversion performed? Which component records a failed email?
A map earns trust when every box and arrow points to evidence: an entry point, manifest, configuration file, schema, observed request, trace, or team-confirmed responsibility.
Keep an evidence ledger: confirmed, inferred, unknown
Discovery becomes unreliable when facts and interpretations share the same tone. A simple three-state ledger prevents that.
Confirmed
A repository file, executable behavior, test, public contract, runtime observation, or responsible owner directly supports the statement. Example: “The booking endpoint writes a row before publishing a reminder job; the integration test and local trace show that order.”
Inferred
The evidence makes the statement plausible, but it has not been verified. Example: “The separate worker may be intended to keep email latency out of the user request.” An inference should include the evidence behind it and the easiest way to test it.
Unknown
The answer matters, and current evidence is insufficient. Example: “It is not yet known whether failed reminders are retried or reviewed manually.” Give each important unknown an owner or a next investigation step.
This ledger is more valuable than a document that sounds certain. It tells the team which decisions are safe now and which still depend on verification.
Day 4: challenge the model and rank the risks
Once the happy path is visible, try to disprove your understanding.
Read the tests by behavior, not by quantity. Identify which critical rules have direct evidence and which are protected only indirectly. Exercise one or two controlled failures: an unavailable dependency, invalid input, duplicate request, missing configuration value, or interrupted background job. Do this in a safe environment with synthetic data.
Inspect dependencies and automation as part of the system, not as housekeeping. Look for unpinned toolchains, abandoned packages, privileged workflows, generated files that drift from their sources, and deployment steps that exist only in a person’s memory. For a public open-source dependency, tools such as OpenSSF Scorecard can surface risky repository practices. A score is a prompt for investigation, not a substitute for reachability analysis, threat modeling, or a project-specific security review.
Rank findings using consequence and evidence:
- Consequence: What user, data, operational, security, or delivery harm could occur?
- Likelihood: Under what conditions can it happen?
- Confidence: Is the concern confirmed, inferred, or still unknown?
- Detectability: Would the team know promptly if it occurred?
- Cost of learning: What is the smallest safe experiment that would reduce uncertainty?
This avoids two common mistakes: treating every untidy file as urgent and postponing a serious risk because its mechanism crosses several modules.
Day 5: propose the first safe change
The first change should demonstrate that the discovery model is useful. It should not be a disguised rewrite or an attempt to prove technical ambition.
A good candidate is:
- connected to a real user, operational, or delivery problem;
- small enough for the team to review with the newly shared context;
- protected by an existing test or a focused new one;
- observable after deployment;
- reversible without a complex data recovery; and
- useful even if a larger modernization plan is postponed.
In the workshop example, improving reminder retries might be important, but it is not a safe first change while failure ownership and delivery semantics remain unknown. Adding a focused integration test that captures the confirmed booking-to-job contract, or documenting and automating the broken clean-setup step, may create more immediate leverage.
The proposal should state the problem, evidence, affected boundary, acceptance criteria, test approach, rollout observation, rollback path, and unresolved questions. That is enough for a technical lead to evaluate the change without accepting the discoverer’s confidence on faith.
Use AI to investigate, but keep the repository authoritative
AI can accelerate discovery. It can summarize manifests, locate entry points, generate a command inventory, explain a call graph, compare configuration files, propose test cases, and turn notes into a draft diagram.
It can also produce a coherent explanation of an architecture that does not exist.
The safeguard is procedural: require every important answer to include its evidence and uncertainty state. If a tool says that a worker retries failed jobs, ask for the exact configuration, implementation, and test that support the statement. Then run the relevant behavior where practical. If the sources do not support the answer, move it to “inferred” or “unknown.”
Keep private code, credentials, production data, logs, and customer information within the tools and environments approved for that material. Discovery speed is not worth weakening the system’s information boundary.
AI is most useful here as an investigator that proposes where to look next. The codebase, runtime behavior, public contracts, and responsible people remain the sources of truth.
Common discovery failure modes
Trying to read the entire repository
Reading without a question produces a large collection of disconnected details. Trace a meaningful workflow, then expand where the evidence or risk leads.
Refactoring on sight
Unfamiliar code often looks unnecessary because its constraint is outside the current file. Record the concern, identify callers and operational consequences, and verify the behavior before changing it.
Treating the README as runtime truth
Documentation is evidence of intent. A clean execution shows current behavior. Preserve both when they disagree.
Drawing a diagram with unlabeled arrows
A line could mean an HTTP request, shared database access, an event, a file transfer, or only a conceptual dependency. Label the relationship and connect it to evidence.
Reporting certainty to appear senior
Good discovery makes uncertainty manageable; it does not hide it. A precise unknown with a verification plan is more useful than an unsupported conclusion.
The first-week discovery checklist
Use this as a handoff artifact, not a scorecard. A small system may need only a page; a regulated or highly distributed one may need more time and additional specialists.
- Record the clean setup, runtime versions, configuration inputs, test command, and known baseline result.
- Identify repositories, deployable applications, data stores, background processes, and external systems in scope.
- Trace one important workflow from entry to user-visible outcome and operational evidence.
- Map people, system boundaries, responsibilities, data ownership, and labeled communication paths.
- Classify important statements as confirmed, inferred, or unknown.
- Locate CI, deployment, migration, rollback, telemetry, and incident-support paths.
- Inventory tests by critical behavior and failure consequence, not only by layer or coverage.
- Review dependency, access, secret-handling, and automation risks proportionally.
- Rank open questions by consequence and the cost of reducing uncertainty.
- Propose one small change with acceptance criteria, tests, observation, and rollback.
- Review the map and unknowns with the people who build, operate, and use the system.
A good first week leaves the system easier to reason about
Discovery is not a pause before engineering. It is the first engineering deliverable.
The goal is not to demonstrate how quickly one person can navigate unfamiliar code. It is to create shared, reviewable evidence that improves the team’s next decision. A reproducible baseline exposes setup risk. A workflow trace connects code to business behavior. A system map reveals boundaries. An evidence ledger keeps uncertainty honest. A small first change tests whether the model is strong enough to support delivery.
If an existing system is difficult to change, begin by asking which part of that evidence is missing. The answer usually identifies a safer next step than starting with a rewrite.