unrust

Workspace debugging guide

How do you trace a bug across multiple files without an IDE?

Published

Published by Unrust. Scoring and editorial method

Quick answer

To trace a bug across multiple files without an IDE, follow the data contract across each module boundary in order: write down the exact input shape and invariant at the entry caller, verify how each imported helper transforms or normalizes that value, and check whether edge values—such as empty arrays, zero budgets, null fields, or mutated references—mean the same thing on both sides of the boundary.

Why cross-file bugs survive single-file code review

Many production defects do not look broken inside any single file. The caller file passes a valid-looking options object, the normalization helper applies a sensible-looking default, and the worker module enforces a boundary check—yet the combined pipeline fails because two modules disagree on whether 0, false, or an empty slice means 'disabled' or 'use default'.

When you review each file in isolation, every function appears locally coherent. The defect only becomes visible when you trace one concrete boundary input across the import seam from caller to callee.

Write down the seam contract at every import boundary

Before jumping back and forth between tabs, pick one failing or boundary input and write down the exact value that crosses each module call.

Check four common cross-file seams: unit or scale mismatches (milliseconds vs seconds, 0-based vs 1-based), sentinel vs falsy normalization (null vs undefined vs false vs 0), shared reference mutation (sorting or splicing an array the caller still reads), and error-contract drift (returning null when the caller expects a thrown error or vice versa).

Pinpoint the module that broke the shared invariant

Once you trace a concrete input across the call chain, identify the first boundary where the actual value diverges from what the downstream consumer assumes.

In Unrust's multi-file workspace drills, you inspect compact 2-to-3-file modules—such as a route handler, a policy helper, and a store—and isolate both the defective file and the exact line where the cross-module contract breaks.

A cross-file boundary review table

Use this table when a bug spans a caller, a helper or normalizer, and a downstream consumer.

A cross-file boundary review table
Observed conditionReview decisionWhat to verify
A configuration flag or numeric limit passes through a normalization module before reaching the workerTrace explicit falsy values (false, 0, '') across the normalizer into the workerVerify whether truthiness fallback (||) or loose checks overwrite a valid caller-supplied boundary value
Two modules share an array, map, or options object across synchronous or async callsCheck whether the callee mutates the passed reference in place (.sort(), .splice(), delete, key assignment)Confirm whether the caller reads the same reference after the call expecting its original order or contents
A helper catches or transforms errors before returning to the orchestratorCompare the helper's failure return value (null, -1, empty array, partial result) with the caller's branch conditionVerify that a failed or empty lookup is not treated as a valid success state by the caller

Interactive reasoning check

Try the trace: where does the cross-file contract break?

In config.ts, normalizeOptions(raw) returns { retries: raw.retries || 3 }. In client.ts, sendWithRetry(opts) loops while attempt <= opts.retries. A caller in healthcheck.ts calls sendWithRetry(normalizeOptions({ retries: 0 })) expecting a single probe with zero retries. Why does the probe still retry 3 times?
Read the reasoning without answering

Cross-file bugs become straightforward when you carry one concrete boundary value ({ retries: 0 }) across each module call instead of reading each file with generic placeholder assumptions.

A small practice loop

  1. 01Pick one concrete edge input at the entry file—such as 0, false, null, or a two-element collection—before reading imported files.
  2. 02Write down the exact value returned or mutated at each module boundary as that input moves from caller to helper to consumer.
  3. 03Mark the first file and line where the value crossing the boundary violates the downstream function's contract.

Common questions

Why practice multi-file debugging on small 2-to-3-file workspaces instead of a 50-file repo?

Most cross-module bugs fail at a single seam between two or three collaborating functions. Practicing on compact multi-file workspaces removes boilerplate noise so you can train the habit of verifying data contracts across imports.

Does Unrust include multi-file drills?

Yes. Unrust's practice library includes cross-file workspace drills where you inspect collaborating files and pinpoint the exact module and line that breaks the shared contract.

Limits of this guide

Manual cross-file tracing complements—and does not replace—static type checking, integration tests, and distributed tracing in large codebases. Unrust's diagnostic and multi-file workspace drills use focused module sets to train boundary reasoning and do not audit your production repository.

Sources and scope

Original editorial guide by the Unrust team based on interface-contract analysis and multi-file fault-isolation patterns.

Check your own starting point

Unrust uses short, timed, unassisted drills to give developers a per-skill snapshot. One session cannot explain the cause of a result, but it can show you what is worth practicing next.

Take the free diagnostic