DEV Community

Sub Engel
Sub Engel

Posted on

Building a second brain from PR reviews and tickets

A lot of real engineering reasoning gets written down in two places: pull request reviews and ticket comment threads. "Why not just use a queue here?" "Because ordering matters for refunds." That exchange is the most useful sentence in the whole change, and it disappears the moment the PR is merged.

You can search for it later, in theory. In practice you don't remember which PR it was, or the repo got migrated, or the ticket tracker changed. This post is about keeping the useful parts somewhere you control, without turning code review into a documentation chore.

I'll use GitHub and Linear in the examples and Obsidian for the notes, but the idea carries over to GitLab, Jira, or a plain folder of Markdown.

What's worth keeping

Most reviews produce nothing worth saving, and that's fine. The things worth keeping fall into a few buckets:

  • Decisions: "we chose X over Y because Z". Often buried in a reply to a review comment.
  • Gotchas: behavior that surprised someone. "Postgres NULL is ignored by COUNT(column) but not by COUNT(*)."
  • Patterns: "this is the third PR where we've needed a retry wrapper." A repeat is a hint that something should exist.
  • Context from tickets: the business reason for a change, which almost never makes it into the code or the commit message.

Capture right after the review

The cheapest moment to capture is right after you submit (or respond to) a review, while the details are in your head. Ask one question: did a decision or a surprise happen here? If not, move on. If yes, spend three to five minutes on a short note.

An illustrative example:

---
type: pr
repo: web
pr: 482
ticket: ENG-217
date: 2026-09-18
tags: [dependencies]
---

# Replace lodash.groupBy with a local helper

**Decision:** Use a 10-line `groupBy` in `lib/collections.ts` instead of importing lodash.

**Why:** It was the only lodash function left in the client bundle. The local version is typed the way we use it.

**Trade-off:** One more function we own and test.

**Link:** https://github.com/acme/web/pull/482
Enter fullscreen mode Exit fullscreen mode

Short, dated, linked. The link matters most: the note is an index into the original discussion, not a replacement for it.

Tickets: the reasoning is in the comments

Ticket descriptions tend to say what. The why, and the changes of direction, are in the comment thread: a PM explaining why the scope changed, a lead pushing back on an approach. When you close a ticket that had a real discussion, summarize it in two or three lines:

# ENG-217 Faster search on the orders page

**Problem:** Support staff wait 5+ seconds searching orders by email.
**What changed during the ticket:** Started as "add an index"; turned out the query was doing a case-insensitive scan. Switched to a lowercased generated column.
**Related:** [[PR web#482]], [[PR api#1190]]
Enter fullscreen mode Exit fullscreen mode

That second line is the part nobody will reconstruct later.

Pulling the week's activity with scripts

Capturing in the moment is best, but a weekly sweep catches what you missed. The GitHub CLI can list the PRs you reviewed recently:

# PRs in the current repo you reviewed, closed in the last 7 days
since=$(date -d '7 days ago' +%F)   # macOS: date -v-7d +%F
gh pr list --state closed --limit 50 \
  --search "reviewed-by:@me closed:>=$since" \
  --json number,title,url \
  --jq '.[] | "- [#\(.number)](\(.url)) \(.title)"' >> "notes/reviews/week-$(date +%F).md"
Enter fullscreen mode Exit fullscreen mode

gh pr list has no --reviewed-by flag, but its --search option accepts GitHub's search qualifiers, and reviewed-by:@me is one of them.

To read the discussion on a specific PR:

# Description, conversation comments and review summaries
gh pr view 482 --json title,body,comments,reviews

# Inline review comments (the line-by-line ones) come from a different endpoint
gh api repos/{owner}/{repo}/pulls/482/comments --jq '.[] | "\(.path):\(.line) \(.user.login): \(.body)"'
Enter fullscreen mode Exit fullscreen mode

gh api fills in {owner} and {repo} from the repo you're in. The inline comments are where most of the interesting back-and-forth happens, and gh pr view doesn't include them.

Exporting Linear tickets

Linear has a GraphQL API at https://api.linear.app/graphql. With a personal API key (Settings > API), a small script can turn recently completed issues into notes:

import json, os, pathlib, urllib.request

QUERY = """
query($since: DateTimeOrDuration!) {
  issues(first: 50, filter: { completedAt: { gte: $since } }) {
    nodes {
      identifier title url description estimate
      comments { nodes { body createdAt user { name } } }
    }
  }
}
"""

def fetch(since="-P7D"):  # ISO 8601 duration: last 7 days
    req = urllib.request.Request(
        "https://api.linear.app/graphql",
        data=json.dumps({"query": QUERY, "variables": {"since": since}}).encode(),
        headers={"Content-Type": "application/json",
                 "Authorization": os.environ["LINEAR_API_KEY"]},
    )
    with urllib.request.urlopen(req) as r:
        return json.load(r)["data"]["issues"]["nodes"]

out = pathlib.Path("notes/tickets")
out.mkdir(parents=True, exist_ok=True)
for i in fetch():
    lines = [
        "---", f"type: ticket", f"ticket: {i['identifier']}",
        f"estimate: {i['estimate']}", "---", "",
        f"# {i['identifier']} {i['title']}", "", i["url"], "",
        "## Description", i["description"] or "", "", "## Discussion",
    ]
    for c in i["comments"]["nodes"]:
        who = c["user"]["name"] if c["user"] else "unknown"
        lines += [f"- **{who}** ({c['createdAt'][:10]}): {c['body']}"]
    lines += ["", "## Summary", "_Two lines: what changed and why._"]
    (out / f"{i['identifier']}.md").write_text("\n".join(lines))
Enter fullscreen mode Exit fullscreen mode

Two notes on this:

  • Linear's estimate is in points on whatever scale your team configured, not hours. Don't do "estimate vs actual hours" math on it.
  • The script dumps raw comments. The valuable part is the ## Summary you write by hand afterwards; a vault full of unread exports isn't a second brain, it's a backup.

Keep the API key in an environment variable, never in the vault.

Linking it together

Once PR notes and ticket notes exist, wiki links connect them. A ticket note links to its PRs, a PR note links to its ticket and any decision record, and backlinks give you the reverse direction for free. When you later wonder why the orders search uses a generated column, you land on the ticket, which links to both PRs and the reasoning.

If you use Dataview, a simple dashboard helps during the weekly sweep:

```dataview
TABLE repo, pr, ticket, date
FROM "reviews"
WHERE date >= date(today) - dur(7 days)
SORT date DESC
```
Enter fullscreen mode Exit fullscreen mode

Keeping it from going stale

Advice from an old PR can be wrong now: a dependency got upgraded, the team changed its mind, the service was rewritten. Two cheap habits help:

  • Mark superseded notes instead of deleting them. Add a line at the top: "Superseded by [[new note]]: we moved from Redux to Zustand in 2026." The old reasoning stays findable and nobody follows it by accident.
  • Add a reviewed: date to notes you rely on. When you use a note and it still holds, bump the date. Notes nobody has touched in a year are the first to question.

The weekly sweep

Once a week (Friday works), do a quick pass:

  1. Run the gh command above and skim the list for reviews where something was decided that you didn't capture.
  2. Look at tickets you closed this week and write the two-line summary for any with a real discussion.
  3. Scan the week's notes for repeats: the same gotcha twice is a candidate for a lint rule, a helper or a README section.

Expect around half an hour, less on quiet weeks. The in-the-moment captures do most of the work; the sweep catches what slipped through.

Start small

You don't need the scripts on day one. Start with the habit: after any review where a decision or surprise came up, write five lines and paste the link. After a month you'll have a small, searchable record of why your codebase looks the way it does, which is more than most teams have.


Dev Second Brain, my Obsidian vault for developers, includes a PR review template if you want somewhere to start.

More templates and a free Dataview starter pack are at forge.engelailabs.com.

Top comments (0)