July 12, 2026
Your Codebase Should Learn From Every Debugging Session
A difficult trace is evidence about how a codebase communicates. The breadcrumbs skill turns that evidence into small repairs: clearer names, explicit contracts, useful errors, honest comments, and documentation in the place readers already look. The result is a codebase that gets easier to understand each time an agent or developer works through it.
Sascha Becker
Author10 min read

An agent needed to change the retry delay for checkout. It searched the checkout service, the network client, the environment schema, and the retry helper. The value was eventually found under analytics/config.ts, where it had landed during a refactor two years earlier.
The agent changed five seconds to eight, ran the tests, and finished the task.
The next agent will perform the same search.
Nothing was wrong with the investigation. It found the right answer. The waste was leaving the codebase in a state that would produce the same wrong turns again. A human developer joining the team next month would pay the same tax.
My first version of the breadcrumbs skill tried to solve this by recording the finding. It asked the agent to write an anchored note into AGENTS.md or a dedicated breadcrumb file: the retry delay lives over there, this workaround exists because of Safari, this function must run inside a transaction.
That sounds responsible. It also creates another system to maintain, another place that can drift, and another habit every future reader must know about before it helps them. I had designed a better investigation diary. I had not made the codebase clearer.
The reworked skill takes the opposite approach:
If a trace exposes avoidable confusion, repair the thing that caused it while the evidence is fresh. Move or rename the retry setting. Make the transaction part of the function contract. Improve the error that hid the failing field. Explain the Safari constraint beside the workaround. Correct the stale setup guide.
The next reader should benefit without knowing that the skill exists.
A trace is telemetry
Programmers do not read a repository from top to bottom. They follow cues. A filename suggests ownership. A symbol name suggests behavior. An import opens a path into another module. An error message points toward a cause, or away from it.
Research on programmer navigation calls this information scent. In a study of professional programmers debugging a real application, a model based on scent and navigation topology predicted where programmers went better than comparable models that ignored scent.1 The words and paths available in the environment shape the investigation.
That gives us something more useful than a vague feeling that code was hard to understand. The shape of the trace is evidence.
- You followed a plausible name into the wrong subsystem. The information scent is misleading.
- You kept returning to the same hub and jumping manually between layers. The navigation topology has no visible edge or canonical entry point.
- You inspected every caller to discover one precondition. The contract is hidden.
- You compared several values because each looked authoritative. Ownership is fragmented.
- You reached the cause only after an unhelpful downstream failure. The diagnostics are opaque.
Do not save the search transcript. Use it to identify which part of the codebase taught the wrong lesson.
Difficulty alone is not a smell
A cryptographic protocol can be difficult and still be modeled clearly. A framework can be unfamiliar to you and familiar to everyone who works in the repository. The signal is a repeatable mismatch between a reasonable expectation and what the codebase communicates, not the amount of effort you personally spent.
Repair the source of the confusion
Once you name the smell, the repair often becomes obvious. The important choice is its form. A note is easy to write, but it is usually the weakest durable form.
The breadcrumbs skill uses a repair ladder. Start at the top and move down only when the stronger form does not fit safely:
- Delete the obsolete or misleading thing.
- Make the truth visible through naming, placement, structure, or an explicit dependency.
- Encode the contract in types, schemas, validation, assertions, or tests.
- Improve the error or diagnostic context.
- Explain a reason that the code cannot express.
- Update an existing documentation surface readers already use.
This is not a ranking of importance. It is a ranking of how directly the repair reaches the next reader. Code that cannot be misused is stronger than a paragraph asking people not to misuse it.
Suppose you discover that saveOrder must be called inside an existing database transaction. The weak repair is a note:
ts// Must be called inside a transaction.await saveOrder(order);
It helps the reader who notices it and remembers to comply. It does nothing for the caller who misses it. A stronger API makes the obligation visible:
tsawait saveOrder(transaction, order);
Depending on the language and architecture, the best form might be a transaction-scoped repository, a state-specific type, an assertion at the boundary, or validation with a focused contract test. Bertrand Meyer's Design by Contract gives the useful vocabulary: preconditions describe the caller's obligations, postconditions describe the operation's guarantees, and invariants describe what remains true across operations.2 You do not need Eiffel syntax to make those facts explicit.
Errors should remain useful at a distance
invalid configuration is technically correct and operationally useless. If the trace reveals that region became empty after environment interpolation, the failure should say so at the earliest boundary that has enough context:
textcheckout config: region is required after environment interpolation
Good error types preserve the operation, the relevant target, and the original cause. The Go project's PathError is a compact example: it carries the operation, path, and underlying error, so it remains useful even when printed far from the failed call.3 Structured details also let tools react without parsing prose. The repair still needs judgment. Do not put tokens, personal data, or an entire request body into a log just to make it informative.
Comments spend attention
Comments are not free clarification. They are part of the reader interface and compete with the code for attention.
A 2025 eye-tracking study found that comments redirected about 23 percent of visual attention away from code, while their effect on comprehension varied by snippet. Some helped. Some made performance worse. Quality and context decided which.4 Google gives the practical rule: if a reviewer needs an explanation, clearer code is usually the first response. A comment is appropriate when it carries information the code cannot, especially the reason behind a surprising choice. An explanation left only in the review tool helps nobody reading the code later.5
This comment earns its keep:
ts// Reading layout here forces Safari to apply the pending style before focus.// Removing it restores the selection jump covered by safari-focus.spec.ts.void input.offsetHeight;input.focus();
// Needed for Safari does not. It names a browser and leaves the constraint hidden.
Put documentation where its reader needs it
Some knowledge genuinely belongs in prose. Local setup, incident recovery, cross-module architecture, and design tradeoffs do not fit cleanly into a type signature.
The answer is still not a breadcrumb file. Patch the maintained surface that already owns the reader's need.
Diátaxis separates documentation into four forms because they solve different problems: tutorials teach, how-to guides help someone complete a task, reference supplies lookup facts, and explanation provides context and reasons.6 A missing recovery command belongs in the runbook. A configuration field belongs in reference. A cross-cutting tradeoff belongs in explanation.
Architecture Decision Records have an even higher threshold. Michael Nygard's original proposal limits them to decisions that affect structure, non-functional characteristics, dependencies, interfaces, or construction techniques.7 A local rename does not earn an ADR. Neither does every surprising line. If the repository already records significant decisions, use that system for significant decisions. Do not introduce one as incidental cleanup.
This placement rule matters because documentation can recreate the exact problem it was meant to solve. Put transaction requirements in a general agent-notes file and you now have two interfaces to saveOrder: the function signature and a document somewhere else. They will disagree eventually.
Self-healing needs brakes
"Leave it better than you found it" is pleasant advice and dangerous authorization. An agent can use it to justify renaming half a package during a bug fix.
Breadcrumbs applies two tests before editing:
- Repeatability: could another competent developer reasonably hit the same confusion?
- Payoff: would the repair shorten that trace without requiring knowledge of the skill?
Then it checks confidence and blast radius. A verified stale comment beside the code is safe to correct. A local error can usually gain context. A public API rename, data migration, architecture shift, or security-sensitive behavior change must not hide inside incidental healing. Surface the evidence and proposed repair instead.
Zero repairs is a successful outcome when the trace exposed no real smell. The skill rewards reduced future friction, not visible activity.
This restraint is what keeps a self-healing practice from becoming a self-rewriting codebase.
The skill
The skill packages the short healing loop in SKILL.md and keeps the deeper judgment in a linked Open Knowledge Format bundle: a smell catalog, repair patterns, autonomy rails, worked examples, and the research behind them. An agent loads the catalog only when it needs to diagnose an ambiguous trace.
Install it from the saschb2b/skills repository:
bashnpx skills@latest add saschb2b/skills --skill breadcrumbs
It can trigger from ordinary work:
textWe found the retry delay under analytics config after checking fourunrelated modules. Fix the value and make its ownership clearer.
Or from the moment the smell becomes visible:
textI had to inspect every caller to learn that saveOrder needs an existingtransaction. Make that contract harder to miss.
The important part is what the skill refuses to produce. There is no breadcrumb registry, investigation log, freshness protocol, or requirement to read a special file before debugging. It makes one coherent repair, verifies it, mentions the incidental change in the normal handoff, and returns to the task.
You can read the full breadcrumbs skill, including its smell catalog and worked examples.
Close the trace
Every non-trivial investigation ends with a brief window in which you understand both the system and the friction that hid it. That window closes quickly. Use it.
Ask one question before you leave the area:
If the answer is "nothing, I was learning the domain," finish the task. If the answer is a misleading name, an invisible precondition, a dead path, a vague error, or a stale instruction, repair that one thing.
The next trace starts from a better codebase.
- breadcrumbs skill
The skill, knowledge-smell catalog, repair patterns, scope rails, worked examples, and research synthesis.
- An Information Foraging Theory of How Programmers Debug
The empirical basis for treating navigation cues and topology as observable evidence during debugging.
- The Effect of Comments on Program Comprehension
A 2025 eye-tracking study showing that comments direct attention and help or hinder depending on context and quality.
- Design by Contract
Bertrand Meyer's treatment of explicit preconditions, postconditions, and invariants.
- Diátaxis in five minutes
A practical distinction between tutorials, how-to guides, reference, and explanation.
- Documenting Architecture Decisions
Michael Nygard's original ADR proposal and its threshold for architecturally significant decisions.
