Docs & Code Guide

Keep a README in sync with the code: describe, never duplicate

A README goes stale in a predictable order, and every stale line is a duplicate of something the repo already knows. The durable fix is not discipline, it is pointing the README at artifacts that regenerate: badges that read CI, doc blocks injected by generators, setup steps that are themselves tested, and a diagram served from a live URL instead of a pasted screenshot. Here is the decay order, the CI checks that catch it, and the one GitHub-specific caching detail worth knowing.

7 min readFor maintainers tired of "the README says X but the code does Y" issues

See it as a diagram

Everything below, as a diagram you can edit. Describe yours and see it in seconds.

171/20003 credits left
Try:

No account needed · Editable canvas, not a picture

What decays first in a README

Setup steps go first: a renamed script, a bumped runtime version, a new required environment variable, and the quickstart fails for the next clone. Screenshots and architecture diagrams go next, because they are frozen pixels describing a moving system, and nobody re-exports an image for a routine refactor. Badges quietly break third when a CI provider or coverage service changes URL schemes. Links to moved docs round it out.

Notice the pattern: everything on that list is a copy. The setup steps copy what CI already does, the diagram copies what the code already is, the badge copies a status that lives elsewhere. Copies drift; references do not. The rest of this page converts each copy into a reference or puts a test on it.

Test the README like code

Two CI checks cover most README rot. A link checker such as lychee fetches every URL in the file and fails on 404s, which catches moved docs and dead badges. And the quickstart can be executed literally: run the exact install and build commands from the README in a clean container on a schedule. If the quickstart only works on machines that already have the project set up, that is a bug, and this job finds it before a new contributor does.

# .github/workflows/readme-check.yml
name: readme-check
on:
  pull_request:
    paths: ["README.md"]
  schedule:
    - cron: "0 6 * * 1"   # weekly: READMEs rot without commits too
jobs:
  links:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: lycheeverse/lychee-action@v2
        with:
          args: --no-progress README.md
  quickstart:
    runs-on: ubuntu-latest
    container: node:22   # a machine with nothing preinstalled
    steps:
      - uses: actions/checkout@v4
      - name: Run the README setup steps verbatim
        run: |
          npm install
          npm run build

The architecture section: replace the screenshot with a URL

The architecture image is the copy you cannot test, so replace it with a reference. A Datadef project shared public serves a permanent image URL, and standard Markdown embeds it like any image. When the diagram is edited, the URL serves the new render; the README commit that pasted it never needs to change. Sizing parameters exist when the default 1600x1000 render does not fit.

One GitHub-specific mechanic makes this work better than people expect. GitHub does not load external images directly in rendered Markdown: it rewrites them through its Camo proxy at camo.githubusercontent.com, and Camo is documented to respect the cache headers of the origin. Datadef serves the embed with a five minute cache lifetime, so the README picture normally updates within minutes of an edit. If a copy ever looks stuck, send an HTTP PURGE request to the camo.githubusercontent.com URL of the rendered image to clear it. The first render after an edit takes a few seconds while a headless browser redraws it; after that it is cached and fast.

## Architecture

![System overview](https://datadef.io/api/embed/my-platform-a1b2c3d4)

<!-- The image is served live: editing the diagram updates this
     README within minutes. GitHub proxies it through Camo, which
     honors the five minute cache lifetime upstream. -->

Describe, never duplicate

What remains for prose is the part only humans know: what the project is for, what it deliberately does not do, where to ask questions. That content ages in years, not weeks. Everything operational should be a pointer: "run make dev" instead of an inlined command list that will drift from the Makefile, a link to the CI workflow instead of a described one, an injected table from a docs generator instead of a hand-maintained one.

A README built this way is shorter and more honest. The broader failure mode it avoids has its own write-up in README rot, and the general CI patterns are collected in docs checks in CI.

The limits worth stating

The live diagram URL only exists for Datadef projects shared public, which fits open source and most internal-but-not-secret repos. Flip the project private and the image 404s, including through Camo. For a genuinely confidential architecture, a committed export that you consciously re-export on change is the honest alternative.

GitHub also strips iframes from rendered Markdown, so the interactive pan-and-zoom embed form does not work in a README; the image URL is the right form there. And the update loop is about propagation, not authorship: someone or some agent still has to edit the diagram when the system changes. Datadef ships an MCP server so a repo-connected agent can do that edit, but nothing watches the repository on its own.

FAQ

How do I keep a README up to date?

Remove duplication and test what remains. Point badges, doc tables, and the architecture image at generated or live sources instead of pasting copies, run the README setup commands in a clean container in CI on a schedule, and run a link checker like lychee over the file. Prose should describe intent, which ages slowly; anything operational should be a reference to an artifact that regenerates.

What goes out of date first in a README?

Setup steps break first because any renamed script or new environment variable invalidates them. Screenshots and architecture diagrams follow, since they are frozen images of a changing system. Badges and external links decay third as URLs move. All of these are copies of information that lives elsewhere, which is exactly why they drift.

Does GitHub cache images embedded in a README?

Yes. GitHub rewrites every external image in rendered Markdown through its Camo proxy at camo.githubusercontent.com. Camo respects the cache headers sent by the image origin. An origin with a short cache lifetime, such as a Datadef embed with its five minute max-age, updates in the README within minutes of an edit; an origin with long cache headers can appear stuck.

Can I embed an interactive diagram in a GitHub README?

No. GitHub strips iframes and script from rendered Markdown, so interactive embeds do not survive. The working form is a plain image whose URL serves the current render of the diagram. Readers who want to pan and zoom follow a link under the image to the full diagram page.

How do I test README setup instructions automatically?

Run them verbatim in CI inside a clean container image that has nothing preinstalled, on pull requests that touch the README and on a weekly schedule. If npm install and the documented build command fail on a bare node container, the quickstart is broken for newcomers. A scheduled run matters because READMEs also rot while the repo is quiet.