Software & TechnicalFree
Legacy Codebase Documentation From History
The commit history is the only member of the previous team still available, and it knows which modules are alive.
River's inherited codebase tool reads the repository instead of asking you to describe it. The source, the commit history and whatever documentation survived go in. What comes out is a written account of what the system does today, a module register carrying each module's purpose and the date it last changed, and a dependency map drawn from the imports. Every statement carries the file or commit it was read from. The parts that look risky are named, along with the evidence for calling them that.
What ranks for this search is advice, and most of it is good advice. A widely shared seven-point guide opens by telling you to stay curious and ask questions to your colleagues. That is sound for almost everyone, and useless to the person typing this query, whose whole situation is that the people who could answer have gone. The rest of the set tells you to take notes as you go. None of them read the repository, which is the one witness that stayed.
Built for the engineer who just took ownership of a service nobody on the team wrote, the contractor picking up an abandoned project, and the acquiring team on day one of a code handover. Reach for it before you change anything. If you only need the front door of a repo you already understand, the README generator is the shorter path, and a developer onboarding guide is usually what you write next. When you ship from it, release notes come out of the same history.
The history knows which parts are still alive
The handover you are living through has a measured shape. A study of 133 popular GitHub projects put a number on it: 65% have a truck factor of two or less, meaning two departures are enough to leave more than half the files without a main author. That is not a warning about the future. If you inherited this codebase, the departures already happened, and a good deal of what you are now reading is orphaned in exactly that sense.
The obvious move is to run blame and find the author. Read what blame actually promises, which is to annotate each line with the revision that last modified the line. Last modified, not wrote. One repo-wide reformat, one import reorder, one licence-header sweep, and every line in the tree is attributed to whoever ran it. Git ships an ignore-revs-file option precisely so you can exclude those commits, which tells you how routinely this happens.
Following a file's real history is worse than it looks. Git's own documentation notes that the option for tracing a file across renames works only for a single file. Eight hundred files means eight hundred invocations, which is why nobody does it by hand and why the answer is worth having. Microsoft's study of Vista and Windows 7 found that ownership measures track both pre-release faults and post-release failures. Where ownership thinned out is where to tread carefully.
How it works
Point at the repo
Give the repository, whatever you already know about it, and the first job you have to do with the code.
Read the history
River walks the commits, resolves renames, discounts formatting sweeps and works out which modules are still moving.
Get the account
A document describing the system as it stands, a module register, a dependency map and the open questions.
Work the gaps
Ask about any module in chat. Answers cite the commits behind them, and the document updates as you go.
What you get
- A module register naming each module's purpose, its last commit date and who still touches it
- Every statement traced to the file, commit or pull request it was read from
- The actively maintained code separated from the code nobody has changed in years
- Blame corrected for repo-wide reformats, so the name on a line is the one that wrote it
- A dependency map built from the imports rather than from the folder names
- The questions the repository cannot answer, listed plainly instead of guessed at
Common questions
What if the history was squashed when the repo was migrated?
Then you get less, and the report says so rather than inventing confidence. A single import commit still gives you file sizes, the dependency graph and whatever the code itself encodes. What it cannot give you is who knew what, so those rows are marked unknown and the open-questions list gets longer.
How is this different from pasting the code into a chat window?
A chat window sees the code as it stands today. It cannot see that the payments module was rewritten twice in six months while reporting has not changed since 2022, and that difference decides where you tread carefully. The history is a separate input here, and reading it is most of the work.
Can it tell me what is safe to delete?
It tells you what looks dead and why: no commits in three years, no inbound imports, no route registered. It does not tell you to delete anything. Reflection, dynamic dispatch and configuration-driven loading all hide callers from static analysis, so every candidate arrives with the check to run before touching it.
The existing documentation contradicts the code. Which one wins?
The code wins, and the contradiction is worth recording. A stale document is evidence: it says what the system was designed to do, and the gap between that and current behaviour is usually where the interesting decisions are buried. Both go in the report, dated. The same question inverts for an inherited course, where the stated outcomes are the contract.
How large a codebase can it work through?
Large ones, because it does not read every line. It works down from the module boundaries and the dependency graph, then reads closely only where the history says something warrants it: high churn, many hands, or a module central to the job you named. Scope stays tied to what you asked for.
Does it document the API as well?
It documents the API surface it can see, meaning the routes, the handlers and the shapes they accept. That is a description of what the code does, not a specification. For a published reference aimed at external callers, generate it from the spec with the OpenAPI spec generator. Then check what that spec leaves unsaid before you publish from it.
What do I do with it once I have it?
Most teams turn it into two things. The module register becomes the map you hand the next person, and the risky-module list becomes the order you write tests in. If the codebase runs a service, a production incident runbook is usually the next document, since you now know what fails. Anywhere the code embeds a decision nobody can explain, an architecture decision record carries the reasoning.
Legacy Codebase Documentation From History
Fill in the form and your workspace opens with the work already underway.