Linking decision records to git commits A developer outlined a lightweight workflow for linking architecture decision records (ADRs) to the git commits that implement them, using a date-plus-slug filename convention, a Markdown template, and git commit trailers such as "Decision: adr-20260927-session-store". The approach relies on git's built-in trailer parsing and a commit-msg hook that rejects decision IDs with no matching note, letting developers trace code back to the reasoning behind it via git log --grep and git blame. Six months after a change, git log tells you what happened and git blame tells you who . Neither tells you why you picked this approach over the two you rejected. That reasoning usually lived in a meeting, a chat thread, or your head. Architecture decision records ADRs fix half of this by writing the reasoning down. The other half is linking each decision to the commits that carried it out, so you can go from code to reasoning and back. This post shows a setup that uses nothing more exotic than a notes folder, a naming convention, and git's own features. I'll use Obsidian for the notes, but everything here works with any folder of Markdown files. Not every commit needs one. I'd write a decision note when: Renaming a variable doesn't qualify. Switching your session store does. The ID is the glue, so it has to be boring and predictable. A date plus a short slug works well and sorts nicely: decisions/adr-20260927-session-store.md The filename is the ID. Here's a Templater template that creates the frontmatter and structure save it as templates/decision.md : --- id: <% tp.file.title % status: proposed proposed | accepted | superseded | rejected decided: <% tp.date.now "YYYY-MM-DD" % reviewed: <% tp.date.now "YYYY-MM-DD" % superseded by: tags: decision --- <% tp.file.title % Context What problem, what constraints, what forced a decision now. Options considered 1. Option A : what it is. Pros / cons. 2. Option B : what it is. Pros / cons. Decision We chose ... because ... Consequences What gets easier, what gets harder, what we're now committed to. Commits Filled in from git, see below. Create the note with a filename like adr-20260927-session-store , apply the template, fill it in. It takes ten minutes when the decision is fresh and an hour of archaeology if you wait. Git has a built-in convention for structured lines at the end of a commit message, called trailers Signed-off-by: is the best-known one . Use one for decisions: Replace JWT refresh flow with server-side sessions Refresh-token rotation was racing across browser tabs. Sessions now live in Redis with a 30-day sliding expiry. Decision: adr-20260927-session-store You can add the trailer from the command line too: git commit -m "Move session reads to Redis" --trailer "Decision: adr-20260927-session-store" --trailer needs git 2.32 or newer. If you want a reminder, add a commit template to the repo and point git at it: .gitmessage in the repo root Subject line ~50 chars Why this change? Decision: adr-YYYYMMDD-slug delete if not applicable git config commit.template .gitmessage Lines starting with are stripped from the final message, so the hints cost nothing. This is where the trailer pays off. No script needed: Every commit that implemented a given decision git log --oneline --grep="Decision: adr-20260927-session-store" All commits that reference any decision, with the ID shown git log --format='%h %s % trailers:key=Decision,valueonly,separator=%x2C ' --grep="^Decision:" Which decision explains this line? blame first, then read the commit git blame -L 40,60 src/auth/session.ts git show