[ FIELD_NOTE ]

The Version Control File that is necessary for every Vibe Coding project.

[dx][tooling][ai-collaboration][documentation][Vibe Coding][AI][Claude][Claude code]
48_READS·11_APRIL_2026·11_MIN_READ

I invented this pattern by accident on VivaShelf.

I was two months into the project, something broke in production, and I spent three hours tracing it back to a configuration change I had made six weeks earlier. The commit message said fix: update headers. Git blame pointed to the line. Neither told me why I had made that specific choice, what I had tried before it, or whether the thing I was about to undo had been deliberate. I almost broke something that had been carefully balanced.

That is when I started keeping docs/version-control.md.

This field note is written alongside Claude Code - which is also part of what I want to document here. A large part of the value of this file has come from how Claude Code reads it, maintains it, and uses it as active project memory across sessions. That collaboration is as much the story as the file format.


What it is - and what it is not

Version-control.md is not:

  • A changelog (those are for users, not yourself)
  • A copy of git log (you already have that)
  • A todo list or roadmap

It is a dated decision journal with root causes. Every entry records what broke, why it actually broke (not just the surface error), what changed, and why that specific approach was chosen over the alternatives. Written for your future self at 2am when something is on fire and you need to understand why a line of config looks the way it does.

The structure I use is simple:

## [YYYY-MM-DD] Short description of the issue or change

**What broke:** one sentence on the symptom.

**Root cause:** the actual problem, not the error message.

**Fix:** what changed and why this approach vs alternatives.

**Files:** explicit list so you can grep and jump.

No padding. No narrative prose. Four fields.


Why git log is not enough

Git log gives you: fix: correct API proxy path. Useful. But it does not tell you:

  • That the third-party client SDK internally appends a path segment to whatever base URL you configure - which is why the final URL doubled
  • That you tried one base path first and only caught the mismatch in a dev-mode Network tab
  • That the shorter base path you settled on was a deliberate choice to avoid a doubled segment, not an accidental omission

Git blame points to the line. It does not explain the three alternatives you ruled out. That context lives in your head for about 48 hours, then it is gone.

The version-control.md entry for a bug like that takes four minutes to write and can save 30 minutes of re-diagnosis the next time someone - or Claude Code - asks why the path is structured the way it is.


How Claude Code sees this file

This is the part most documentation articles skip.

Claude Code reads CLAUDE.md at the start of every session. If CLAUDE.md points to docs/version-control.md and gives explicit instructions to update it after every fix, then every session starts with Claude Code already knowing:

  • What was broken recently and why
  • What decisions were deliberate and should not be revisited
  • What is known technical debt vs an open bug
  • What audit items passed and which were deferred and why

This is not just documentation for the developer. It is active working context for the AI. Claude Code uses the version-control.md file the same way an experienced engineer would read a project runbook before touching anything - as the authoritative record of what was tried, what failed, and why the current state looks the way it does.

Consider a scenario where you add a proxy rewrite to avoid a third-party blocklist, document the reason and trade-offs, and commit. Three sessions later you open the project again. Claude Code reads the entry, understands the architecture decision without needing to be re-briefed, and does not suggest "simplifying" the proxy away - because the entry says the trade-off was evaluated and accepted.

Or consider a security audit that ran last month. The findings - what passed, what was deliberately deferred, what requires periodic re-verification - are in version-control.md. Claude Code starts the next session knowing the baseline, not just the current codebase state. You do not have to re-audit from scratch.

The file is not a passive log. It is a handshake between sessions.


How Claude Code maintains it

The update instruction in CLAUDE.md is framed as a completion criterion, not a request:

After every fixed and confirmed issue or session, update docs/version-control.md.

Because it is phrased as part of finishing work - not as a separate task to remember to ask for - Claude Code writes the entry during the same context window in which the fix was made. It has access to every error message, every diff, every alternative that was tried and discarded, every trade-off that was discussed. The entry is written while all of that is still in context.

The collaboration pattern is: I identify what is broken, we work through it together, Claude Code records what we did and why. I review the entry, commit it alongside the fix. The authorship is joint - the decision-making is mine, the transcription happens automatically.

The difference from writing it yourself an hour later is significant. Entries written in-session by Claude Code tend to be more complete: they include the alternatives that were considered, the specific error messages that led to the diagnosis, and the reasoning behind choices that would otherwise be forgotten. They are also more consistently structured, because the format is in CLAUDE.md and Claude Code follows it on every entry.


Hypothetical examples - what ends up in there

To give a sense of what useful entries look like, here are three hypothetical scenarios:

A silent data loss bug. You build an analytics integration. The dashboard shows zero events despite confirmed traffic. You spend an hour verifying credentials, IDs, domain config. All correct. Root cause: the analytics provider's domain is on a default blocklist shipped with major browsers and ad blockers, so the beacon is silently dropped client-side before it ever leaves the user's browser. Your own-domain metrics were working because they did not go through the blocked domain. The fix involves a server-side proxy rewrite.

The version-control.md entry records the root cause (blocklist, not misconfiguration), the architecture change, any privacy or compliance implications of the proxy approach, and why certain options - like forwarding client IPs through the rewrite - were evaluated and rejected. Without that entry, the next time you question the proxy configuration, you re-derive all of it from scratch.

A deliberate performance trade-off. A security scanner flags a configuration as suboptimal. You investigate and find that the "correct" fix would require switching from cached static responses to dynamic per-request rendering - a significant performance regression for a content-first site. You evaluate the trade-off and decide the current configuration is acceptable for your workload.

The entry records: what the scanner flagged, what the ideal fix would require, why you evaluated it and decided against it, and explicitly states that future sessions should not re-litigate this. That last line is what makes the entry valuable. Without it, the next security audit produces the same finding, the same investigation, and the same conclusion - repeated indefinitely.

A third-party SDK path quirk. You configure an SDK with a base URL. The SDK internally appends its own path segment to that URL. You set the base URL to what feels logical and get a 404 in the Network tab. You discover the doubling, shorten the base URL, and everything works. Four minutes of debugging.

The entry records: the SDK behaviour (appends its own segment), the original base URL, the resulting doubled path, the fix. The next time anyone touches that configuration - including Claude Code in a future session - they do not need to rediscover that the SDK modifies the URL before sending.


The exact Claude.md prompt structure

If you want this to work automatically with Claude Code, here is the instruction to add to your CLAUDE.md or AGENTS.md. The framing matters - an instruction phrased as a completion criterion behaves differently from one phrased as a suggestion:

## Version Control Log

After every fixed and confirmed issue or debugging session, update
`docs/version-control.md`:

- Add a new `## [YYYY-MM-DD] Short title` section at the top
  (reverse-chronological order)
- **What broke:** one sentence — the symptom as it appeared
- **Root cause:** the actual technical reason, not just the error message
- **Fix:** what changed, why this approach, what alternatives were
  ruled out and why
- **Files:** explicit list of every file touched, with a one-phrase note
  on what changed in each
- Keep entries factual and terse. No padding, no transition prose.
- If the fix involved a deliberate trade-off or a decision that might
  look wrong later, record the reasoning explicitly — future sessions
  need to understand the why, not just the what.
- If a decision is intentional and should not be revisited, state that
  explicitly in the entry so future audits do not re-open it.

The last two bullet points are what separate a fix log from a real decision journal. "Do not revisit" entries prevent reopening decisions that were made carefully - whether by you in a future session or by Claude Code starting fresh with no prior conversation context.


What to enhance next

A plain markdown file covers 80% of the value. Three additions would take it further:

Link entries to commit hashes. Every entry should reference the actual commit SHA so you can jump directly from the decision to the diff. Making it a required field in the CLAUDE.md format instruction means Claude Code includes it automatically - it has access to the commit hash at the time of writing the entry.

A "still applies" flag for decisions. Some entries are time-limited ("deferred until X") and some are permanent ("do not revisit"). Making that distinction explicit - a [PERMANENT] or [REVISIT: YYYY-QQ] tag on the entry - helps triage older decisions during periodic reviews and gives Claude Code clearer signal about which to treat as settled vs open.

Severity tagging for findings. If you run periodic audits, tagging findings as High / Medium / Low lets you grep specifically for the most consequential entries. Useful during retrospectives, useful when explaining an architectural choice to a collaborator, and useful for giving Claude Code a faster path to the entries that matter most when starting a session.

None of these require tooling changes. They are format conventions you add to the CLAUDE.md instruction, and Claude Code applies them going forward.


My honest take

I have been an engineer long enough to have watched a lot of "document your decisions" advice get ignored in practice because it asks for sometimes too much. All of that overhead makes sense at team scale. For a solo developer or a small team, it is a regime that collapses under the first two weeks of deadline pressure.

Version-control.md works because it asks for almost nothing: a date, four terse fields, and the discipline to write it before you close the terminal. The Claude Code integration removes even that last requirement - the file updates itself as part of completing the work, not as additional overhead on top of it. Claude Code has full context of everything that happened in the session, writes the entry while that context is still fresh, and you review and commit it alongside the fix.

The VivaShelf incident that started this pattern cost me three hours of re-diagnosis. Every entry I have written since then has cost about four minutes. The entries Claude Code writes on its own cost me zero minutes - I review them, not write them.

If you are building anything where you will ever want to understand a decision six weeks later - which is every project - this is worth doing. The CLAUDE.md instruction above is the entire implementation. Add it, create the file, write the first entry tonight, and the system runs itself from there.

[ END_OF_LOG ]

> BACK_TO_FIELD_NOTES