Insights chevron Software

Replacing Team Wikis with VS Code, Foam and Git

Banner Image

Replacing Team Wikis with VS Code, Foam and Git

Strip a team wiki down to what it actually does and the list is short: it stores interlinked documents, keeps their history, lets people find things, and decides who may read or change what.

Confluence, Notion and Obsidian all deliver that list by putting your documents inside a system — a database, a proprietary document format, an API you have to ask permission to use. The content goes in; getting it back out is a feature the vendor grants you.

The alternative is to keep the documents as what they already are: markdown files, in a Git repository, opened in the editor your team already has running. Everything a wiki does then falls out of tooling that exists anyway. And one capability appears that the hosted products structurally cannot match — an AI agent can read, traverse, summarise and extend the whole thing without an integration, because it's just files.

That it also costs nothing per seat is a side effect, not the argument.

100% markdown, and that is the whole point

There is no storage layer. A note is a .md file on disk. A link is [[another-note]] inside it. The wiki is the folder.

Everything else follows from that one property:

Compare with what the proprietary systems store. Confluence pages come out of the API as an XHTML-ish storage format or as ADF — the Atlassian Document Format, a JSON document tree. Notion pages come out as arrays of block objects, paginated, capped at a thousand blocks per payload, behind an average of three requests per second per connection. Both are perfectly good formats for the vendor. Neither is something you or a script or a model can just read.

The wiki layer: Foam plus what VS Code already does

Foam is a free, open-source extension that turns a folder of markdown into a wiki:

Worth being precise about who does what: VS Code's own markdown support — link validation, updating links on file move, Find All References — only understands standard [text](path) links and heading fragments. It does not know [[wikilinks]] exist. Everything wiki-shaped in the list above is Foam. What VS Code contributes for free is the syntax-agnostic half: split preview, Ctrl+Shift+O for headings in a file, Ctrl+T for headings across the workspace, and the search described below.

Fully team-capable, because Git is the collaboration model

The reflex objection is that this is a personal-notes setup that falls apart with twenty people in it. It's the opposite: Git is the most battle-tested concurrent-editing system in the industry, and a wiki is a far easier workload for it than source code.

Wiki feature Git equivalent
Page history git log -p --follow note.md, or the VS Code Timeline view
Who wrote this sentence git blame
Restore an old version git checkout <sha> -- note.md
Draft vs. published a branch
Review before publish a pull request
Access control repository permissions
Section ownership CODEOWNERS
Offline access the working copy — the whole wiki is on every laptop

Two of these beat the hosted equivalent rather than merely matching it.

Two-speed editing. Most hosted wikis are all-or-nothing: either every edit is live instantly or every edit needs approval. Git gives you both in one repo. Meeting notes and scratch pages commit straight to main. The architecture decision record and the on-call runbook sit behind CODEOWNERS and need a pull request. Same tool, different rigour, chosen per directory.

Full history, for free, forever. Not a "page versions" panel that keeps the last N revisions and shows you a rendered before/after. Line-level authorship on every sentence, going back to the first commit, queryable from the command line.

Make it feel like a wiki, not like a repository

Nobody writing meeting notes should have to think about staging and pushing. They don't have to. GitDoc commits on save and pushes for you:

// .vscode/settings.json — in the wiki repo, and only there
{
  "gitdoc.enabled": true,
  "gitdoc.autoCommitDelay": 1000,
  "gitdoc.commitValidationLevel": "none",
  "gitdoc.autoPush": "onCommit",
  "gitdoc.autoPull": "onPush"
}

Writing a note becomes: open file, type, Ctrl+S. Commit, push and pull happen underneath. Someone who never learns a single Git command still gets complete version history on everything they write.

Two caveats, both real:

That also argues for the wiki living in its own repository rather than a docs/ folder inside the product repo: separate permissions, separate history, no interaction with your CI, no chance of the auto-commit setting leaking into code.

Review and comments: CriticMarkup

Pull requests are a good gate and a bad review surface for prose. A GitHub review comment attaches to a line number in a diff, which is the wrong granularity for text — you want to propose this wording instead of that wording, in place, the way Google Docs suggestions and Overleaf's track-changes work.

CriticMarkup is that, as a plain-text convention. Five operations, all of them legal markdown that no parser chokes on:

The import job retries {--three--}{++five++} times before alerting.

{~~nightly~>hourly~~} runs are covered by [[oncall-runbook]].

{==This paragraph needs a source.==}{>>@thomas — do we have the benchmark?<<}

{++A new sentence a reviewer is proposing.++}

{++ ++} inserts, {-- --} deletes, {~~old~>new~~} substitutes, {== ==} highlights, {>> <<} comments. That's the entire specification.

Because the markup is text, everything else already works: suggestions live inside the file, travel through Git like any other change, show up in a normal diff, survive being opened in any editor, and are readable by anything that can read the page — including an agent, which can both act on comments and leave its own.

In VS Code there are two extensions worth knowing, and the difference matters:

The workflow that falls out:

  1. Reviewer branches, marks the page up with suggestions and comments, opens a pull request.
  2. Author cycles through the changes in the editor and accepts or rejects each one.
  3. Resolving a mark deletes it, so the merged page is clean markdown again — no residue, no separate comment store.
  4. The review itself is a commit. Six months later git log -p still shows what was questioned and what was decided, which is more than the comment sidebar of a hosted wiki preserves after someone clicks Resolve.

Two things worth wiring up. Your static site generator can render the marks instead of hiding them: pymdownx.critic has view, accept and reject modes, so an internal preview build can show every open suggestion in context while the published build renders the accepted text. And a two-line CI check that fails when a {++, {-- or {>> reaches main guarantees no page ever ships with an unresolved suggestion baked into it.

What this gets you is the Overleaf/Google-Docs review affordance — inline suggestions, inline comments, accept/reject — without leaving plain files. What it doesn't get you is real-time: two reviewers marking up the same paragraph simultaneously is still a merge, not a live cursor.

Search is ripgrep

Workspace search in VS Code is ripgrep. On tens of thousands of notes it returns results as fast as you can type, with regular expressions, case sensitivity, whole-word matching and include/exclude globs — so "the word retry anywhere under runbooks/ but not in archive/" is a query you can actually express.

No index to build, no search service to keep healthy, no gap between saving a page and finding it. Hosted wikis put an indexing pipeline between those two moments, which is why the page you wrote a minute ago sometimes isn't findable yet.

The one that actually changes the calculus: agents

This is where the gap stops being about convenience.

A wiki in a Git repo is a folder of plain text in the working directory. That means any coding agent — Claude Code, Copilot, Cursor, a script of your own around an API — already has full-fidelity read and write access to the entire knowledge base. No connector. No OAuth app. No API client. No pagination. No rate limit. No format conversion. The tools an agent already has — read a file, grep the workspace, write a file — are exactly the tools a wiki needs.

What that unlocks, concretely:

The general point: an LLM's native input format is text, and markdown is the closest thing to a canonical text format there is. A wiki stored as markdown is already in the representation a model wants. A wiki stored as ADF or Notion blocks has to be fetched over a rate-limited API, converted, and truncated before a model sees it — and the conversion is lossy in exactly the places that carry structure. If you expect agents to be serious participants in your team's knowledge base, the storage format stops being an implementation detail and becomes the deciding feature.

You can extend it yourself

A hosted wiki's navigation is its folder tree, and a folder tree expresses exactly one hierarchy: every page has one parent and sits in exactly one place.

Real notes don't behave like that. Take a page documenting how your API rate limiting works. It belongs under API Design, because that's where someone building a new endpoint goes looking. It belongs under Incident Response, because it's the first page you open when customers start reporting HTTP 429s. And it belongs under Onboarding, because every new backend developer needs it in their first week. A folder tree forces you to pick one of the three — and from the other two, the page effectively doesn't exist.

The link graph already knows all three relationships. Pages in each of those areas link to [[rate-limiting]]; that is the statement "this note belongs here". The information is sitting in the files. It just isn't rendered anywhere.

In VS Code that's a tree view you can write yourself. The TreeDataProvider API is small enough to show in full:

export class LinkTreeProvider implements vscode.TreeDataProvider<Note> {
  constructor(private graph: LinkGraph) {}

  getTreeItem(note: Note): vscode.TreeItem {
    const item = new vscode.TreeItem(
      note.title,
      this.graph.outgoing(note).length > 0
        ? vscode.TreeItemCollapsibleState.Collapsed
        : vscode.TreeItemCollapsibleState.None
    );
    item.command = { command: "vscode.open", title: "Open", arguments: [note.uri] };
    return item;
  }

  // A note's children are the notes it links to.
  // Because every path is rendered, a note reachable three ways
  // appears in all three branches.
  getChildren(note?: Note): Note[] {
    return note ? this.graph.outgoing(note) : this.graph.roots();
  }
}

Register that against a view in package.json, rebuild the graph on a file watcher, and the sidebar shows a structure no folder tree can express. Two details make it usable in practice: compute children lazily per expansion, so a densely linked vault costs nothing until you expand a wide branch, and render a link back onto a note already in the current path as a marked leaf — otherwise the tree is infinite.

The point isn't this particular view. It's that "the navigation doesn't match how we think" is a solvable problem here and a support ticket everywhere else. Same for a CI job that fails on a dead [[link]], a script that files an issue for every stale placeholder, or a nightly tag report. Notes are files; anything that works on files works on your wiki.

Publishing to everyone else

The read-only company-wide view is a static site generator pointed at the same repo. Eleventy, MkDocs, Docusaurus and Quartz all consume a folder of markdown, and all of them resolve [[wikilinks]] natively or with a small plugin. Push to main, the site rebuilds.

One source, two audiences: editors work in VS Code against the repo, everyone else reads a fast static site with no login and no seat.

What you actually give up

Not nothing. Roughly in order of how often it bites:

Real-time collaborative editing. Two people typing in the same paragraph is a merge conflict, not a shared cursor. For documents written by one person and read by many — most wiki pages — this never comes up. For a document a workshop edits together live, Confluence or Notion simply wins.

WYSIWYG for non-technical colleagues. VS Code's split preview is good and markdown is learnable in twenty minutes, but "install an editor and clone a repository" is a real barrier for people whose job isn't software. This is the single biggest reason mixed organisations stay on a hosted wiki.

Structured databases. Notion's tables, views, filters and rollups have no markdown equivalent. If your "wiki" is really a project tracker wearing a wiki costume, this doesn't replace it.

Per-page read permissions. Git permissions are repository-scoped. CODEOWNERS controls who must approve a change to a path; it does not hide that path from anyone who can clone. Genuinely confidential sections need their own repository. If compliance demands per-page read ACLs with an audit trail, buy the product.

Mentions and notifications. CriticMarkup covers inline comments and suggestions, and pull requests cover the review gate — but nobody gets pinged. There's no equivalent of "@ someone in the margin and they get a notification", and no per-page change alert beyond watching the repo, which is coarser.

Binary attachments. Git handles a few screenshots fine and a folder of 40 MB PDFs badly. Git LFS or an external bucket — either way it's a decision you have to make.

Mobile. Editing markdown in a Git repo from a phone is possible and unpleasant. The published site covers reading; writing means a laptop.

When the hosted wiki still wins

Bottom line

For a team that already lives in a Git repository, a wiki isn't a system to adopt — it's a repo, a free extension, and an afternoon of setup. You get the same interlinked pages with better history, faster search, no lock-in, and the ability to fix anything you don't like in TypeScript instead of filing a feature request.

And you get the thing the hosted products can't retrofit: a knowledge base that is already in the format agents read and write natively. As soon as AI agents are expected to answer from, maintain, and extend a team's documentation, "it's all just markdown in Git" stops being a purist's preference and becomes the architecture.

Takeaways


Sources:


Your Feedback is appreciated!

Enjoyed this? Get the next insight by email.

Related posts

Discover more insights - your next great read is just a click away!

Banner Image
Software
Replacing UI Robots through Python Automation

Replace proprietary and sometimes slow and cumbersome UI robots with Python.

Banner Image
Software
Why I moved from azure to Vercel

There are some good reasons to switch from azure to Vercel - Easy handling, higher quota, better serverless functions