Skip to main content

System Design GitHub: Architecture as Code in Git

NR Tech Studio Team
NR Tech Studio
17 min read

GitHub is no longer just a place to store application code. The platform’s official direction—visible across GitHub Actions, Projects v2, Codespaces, Copilot, and branch protection rules—points toward treating repositories as the control plane for technical decisions, including system design. Engineering leaders who ignore this shift end up with architecture knowledge scattered across wikis, slide decks, and tribal memory. System design GitHub means using the same pull request, review, and automation workflow for diagrams, ADRs, and infrastructure contracts that you already use for source code.

The problem most teams face is not a lack of design documentation. It is that design docs are static, unreviewed, and disconnected from the code they describe. When a database sharding decision lives in a Confluence page while the actual migration script sits in a repository, drift is inevitable. GitHub’s primitives—branches, issues, code owners, status checks—give you a mechanism to make architecture decisions reviewable, versioned, and enforceable.

Key Takeaways

  • A dedicated system design repository turns architecture decisions into pull requests, giving you the same review and audit trail as production code.
  • ADRs (Architecture Decision Records) stored as Markdown files become the source of truth when paired with status checks and CODEOWNERS rules.
  • Diagram-as-code tools like Mermaid and PlantUML keep diagrams versioned, diffable, and automatically renderable inside GitHub.
  • GitHub Actions can enforce non-functional requirements—like documentation freshness and ADR status—before a design change merges.

GitHub’s Official Direction for System Design Workflows

GitHub’s official documentation and public changelog show a deliberate investment in features that support architecture governance. The Docs for GitHub Issues now include issue forms, task lists, and project associations that let teams formalize design proposals. Projects v2 adds a spreadsheet-like interface with custom fields, iteration support, and cross-repo views. These are not incidental tooling updates; they are building blocks for a design review pipeline.

The maintainers have also pushed automation as a first-class citizen. GitHub Actions allows you to run validators, linters, and custom scripts against any repository. For system design, that means a PR that introduces a new ADR can trigger a check that verifies the document’s status field, required sections, and links to related issues. Codespaces and Copilot further blur the line between design and implementation—you can prototype a service topology in a devcontainer and generate sequence diagrams from code comments.

From a CTO perspective, this direction matters because it reduces the total cost of architecture drift. A design decision that is not connected to a reviewable artifact becomes a liability. Your team’s velocity suffers when engineers re-litigate settled decisions or when an outdated diagram sends a new hire down the wrong path. GitHub’s official tooling gives you a way to make architecture decisions as discoverable and enforceable as your code.

  • Issues + Projects: turn informal design chats into tracked, assignable RFCs.
  • Actions: run automated checks on ADR format, Mermaid syntax, and broken links.
  • Code owners: require designated architects or tech leads to approve changes to critical design docs.
  • Branch protection: enforce that design changes pass CI before merging to the main branch.

What Actually Belongs in a System Design Repository

Many teams create a docs/ folder in their monorepo, dump a few Markdown files, and call it done. That approach fails because it conflates module-level documentation with system-level architecture. A system design GitHub repository should contain only artifacts that describe the behavior, boundaries, and evolution of the overall system—not code comments or API reference material.

The following artifact types belong in a dedicated design repo:

  • Architecture Decision Records (ADRs): one file per significant decision, each with status, context, consequences, and date.
  • Request for Comments (RFCs): pre-decision proposals that invite discussion before implementation.
  • C4 model diagrams: system context, container, component, and code views stored as Mermaid or PlantUML source.
  • API contracts: OpenAPI, GraphQL schemas, protobuf definitions that define service boundaries.
  • Runbooks and incident postmortems: operational knowledge tied to specific system behavior.
  • Infrastructure topology: Terraform state summaries, network diagrams, or dependency maps in structured text.

What does not belong: implementation guides for a single service, feature specs that have no architecture impact, or static images that cannot be diffed. The moment a document explains how a specific function works, it is code documentation, not system design. Keep those in the repo where the code lives.

Common Mistake: Teams create a design repo but then mirror every meeting note into it. After three months, the repo has 400 markdown files, no clear owner, and zero confidence in what is current. Treat the design repo like an architectural decision ledger, not a wiki.

Repository Structure for Design Docs, ADRs, and Diagrams

Once you decide to create a system design GitHub repo, the structure must mirror the mental model of your architects and the review needs of your teams. A flat list of Markdown files does not scale beyond ten decisions. Use a directory layout that separates architectural concerns, decisions, and proposals.

A proven structure that works for teams from 10 to 200 engineers looks like this:

system-design/
├── adr/
│   ├── 0001-use-postgres-as-primary-datastore.md
│   ├── 0002-event-driven-communication.md
│   └── README.md
├── rfc/
│   ├── 2025-04-multi-region-active-active.md
│   └── template.md
├── diagrams/
│   ├── context/
│   ├── container/
│   ├── component/
│   └── sequence/
├── contracts/
│   ├── openapi/
│   └── protobuf/
├── runbooks/
├── CODEOWNERS
└── .github/
    ├── workflows/
    └── ISSUE_TEMPLATE/

This structure has two key properties. First, every ADR gets a sequential number and a short imperative file name, making it easy to reference in PRs and commit messages. Second, diagrams are separated by C4 viewpoint, not by team, so a new engineer can find the system context diagram without knowing which squad owns it.

Structure Choice Works Well When Breaks Down When
Single design repo One system, 5–30 services, clear architect role Multiple unrelated product lines or strict compliance isolation
Design folder in monorepo Architecture strongly coupled to code, fast-moving startup Need separate access for architects vs feature developers
Per-domain design repos Large org, multiple platforms, independent release cycles Cross-domain decisions have no single source of truth

The single design repo is the default for most growing businesses because it centralizes architectural decisions while GitHub’s branch protection and code owners handle access control.

Pro Tip: Store an executable make lint or npm run validate script in the design repo. Then anyone can run the same checks locally that GitHub Actions will run in CI, reducing failed PRs by catching Mermaid syntax or missing sections before push.

ADRs as Pull Requests: Turning Decisions into Reviewable Artifacts

An Architecture Decision Record is only useful if someone actually reads it, challenges it, and approves it. Storing ADRs as Markdown files in GitHub lets you route every significant decision through a standard pull request. That single change transforms architecture from an ad hoc conversation into a governed process.

Use a consistent ADR template. The following Markdown template captures the minimum information needed to prevent future re-litigation:

# ADR-0017: Use PostgreSQL as the Primary Datastore

- **Status**: Proposed | Accepted | Deprecated | Superseded
- **Date**: 2025-04-10
- **Deciders**: @cto, @lead-architect, @platform-team
- **Technical Story**: Link to issue or RFC

## Context

We evaluated three options for the primary datastore after the MySQL license risk was flagged. The application has 12 tables, expected 10k writes/min peak, and requires transactional integrity for order processing.

## Decision

Adopt PostgreSQL 16 as the primary datastore for all new services. Use JSONB for flexible product attributes and row-level security for tenant isolation.

## Consequences

### Positive
- ACID transactions with strong documentation and tooling.
- Row-level security reduces application-level tenant filtering.

### Negative
- Operations team must learn PostgreSQL tuning parameters.
- Legacy service using MySQL will need a migration plan.

## Alternatives

| Option | Pros | Cons |
|--------|------|------|
| MySQL | Familiar to team | Licensing uncertainty, weaker JSON support |
| CockroachDB | Horizontal scale | Operational complexity, cost |
| NoSQL | Flexible schema | Lacks transactions needed for orders |

The PR that adds this file should be reviewed by the same people who own the affected subsystem. Link the PR to the relevant issue, and require at least one approving review from a core maintainer. Once merged, the ADR becomes immutable—if you need to change it, open a new ADR that supersedes the old one. This keeps a clean decision history.

Important: Never edit an accepted ADR in place. Superseding it with a new ADR preserves the decision trail and lets future engineers understand both the original rationale and the reason for change.

Diagram-as-Code: C4, Mermaid, and PlantUML in Git

Static diagram images are the worst artifact in a system design repo. They cannot be diffed, merged, or searched. Diagram-as-code solves that by storing the diagram source as text inside the repository. GitHub natively renders Mermaid, so any Markdown file can include a diagram that renders in the UI and in PR diffs.

The C4 model provides four levels of abstraction, and each maps cleanly to Mermaid syntax. Here is a system context diagram using Mermaid’s flowchart syntax:

graph TD
    U[Customer] -->|places order| W[Web App]
    W -->|HTTPS| A[Order Service]
    A -->|SQL| DB[(PostgreSQL)]
    A -->|publish| Q[Event Bus]
    Q -->|consume| N[Notification Service]
    N -->|SMTP| E[Email Provider]
    Q -->|consume| B[Accounting Service]

When this code sits in a Markdown file inside a code fence with mermaid, GitHub renders the visual diagram automatically. That means a PR that edits a diagram shows a side-by-side diff of the source code and the rendered image preview. Your reviewers can actually see what changed instead of guessing from a PNG.

PlantUML offers more advanced features—sequence diagrams, state machines, deployment views—and can be rendered via GitHub Actions or a local build step. For most teams, Mermaid is sufficient for context and container diagrams, while PlantUML handles complex sequence flows.

Pro Tip: Add a GitHub Action that runs npx mermaid-cli -i diagrams/**/*.mmd -o diagrams/rendered/ on every PR. This validates that all Mermaid files compile and gives non-technical stakeholders a preview link without requiring local tooling.

GitHub Issues as RFCs for Architecture Proposals

Not every design change needs a full ADR before discussion. For proposed system changes—like moving to multi-region active-active or introducing a new message queue—an RFC issue is the right vehicle. GitHub Issues with issue templates give you a structured way to collect context, options, and feedback from distributed teams.

Use a dedicated issue template for architecture RFCs. Store it as .github/ISSUE_TEMPLATE/architecture-rfc.md in your design repo:

---
name: Architecture RFC
description: Propose a significant system design change
title: "RFC: "
labels: ["rfc", "needs-review"]
assignees: []
---

## Problem Statement


## Proposed Approach


## Impacted Systems


## Risks and Unknowns


## Timeline and Rollback Plan


## Decision Needed By

When someone opens an RFC, the issue becomes the discussion thread. Link relevant design docs, attach the proposed ADR draft, and use project boards to track progress. The final decision gets recorded in an ADR that references the RFC issue by number. This creates a bidirectional link between the discussion and the decision.

Labels like rfc/proposed, rfc/approved, and rfc/rejected let you filter and report on the pipeline. A CTO can quickly see how many architecture proposals are stuck in review and where the bottlenecks are.

Important: Without a clear decision owner and deadline, RFC issues become endless debate threads. Assign a decider in the issue template and set a due date; otherwise, your team will spend engineering cycles on proposals that never resolve.

GitHub Actions as an Architecture Compliance Layer

Once your design artifacts live in GitHub, you can automate the enforcement of architectural rules. GitHub Actions workflows run on every pull request and can fail the merge if a design change violates your standards. This is the equivalent of linting for architecture.

Here is a workflow that validates ADR format, checks for broken internal links, and ensures Mermaid diagrams compile:

name: Architecture Compliance

on:
  pull_request:
    paths:
      - 'adr/**'
      - 'diagrams/**'
      - 'rfc/**'

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Check ADR status fields
        run: |
          for f in adr/*.md; do
            grep -q "^## Status" "$f" || { echo "Missing Status in $f"; exit 1; }
            grep -qE "Proposed|Accepted|Deprecated|Superseded" "$f" || { echo "Invalid status in $f"; exit 1; }
          done
      - name: Validate Mermaid diagrams
        uses: mermaid-js/mermaid-cli-action@v1
        with:
          files: diagrams/**/*.mmd
      - name: Check internal links
        uses: gaurav-nelson/github-action-markdown-link-check@v1
        with:
          use-quiet-mode: 'yes'
          use-verbose-mode: 'yes'
          folder-path: 'adr, rfc'

This workflow fails a PR when an ADR misses the status section, when a Mermaid file has invalid syntax, or when a design doc links to a missing internal file. The result: architecture documentation stays machine-checkable, and reviewers spend time on the decision itself rather than formatting.

You can extend the same pattern to enforce naming conventions, block direct edits to accepted ADRs, or require a corresponding tests file to be part of the design repo. The key is to start small: one validation rule that solves a real pain point, then add more as your team adopts the workflow.

Common Mistake: Teams over-automate from day one and create a CI pipeline that takes 10 minutes to run on every design PR. Engineers then bypass review by committing directly to main or opening issues instead of PRs. Automate the 20% of rules that prevent the worst architecture drift, not every possible lint rule.

Branching and Versioning for System Design Artifacts

System design changes over time, and your repository must support that evolution. The default branch (main) should always represent the current accepted architecture. Feature branches hold proposed changes, and tags or release branches capture snapshots for significant system releases.

A common pattern is to treat the main branch as the intended state. When a team starts a large refactor or a new service extraction, they create a branch like design/order-service-extraction. That branch contains new ADRs, updated diagrams, and new contracts. Once the change is implemented and the design accepted, the branch merges back to main. If the implementation is delayed, the design branch stays open but is clearly linked to its implementation issue.

Use Git tags to mark architecture baselines that align with product releases. For example, after the design for version 3.0 stabilizes, run:

git tag -a v3.0-architecture -m "System design for release 3.0"
git push origin v3.0-architecture

This gives auditors and new team members a way to see the design as it existed at a specific point in time. You can also use branch protection rules to require linear history on the design repo, preventing accidental merge commits that obscure the decision trail.

One critical rule: never maintain parallel design branches indefinitely. If two branches describe the same system differently, you have architectural skew. The design repo is not a code repo where feature branches can live for weeks. Keep the number of active design branches below five, and close or merge them within two weeks. This forces the team to converge on decisions rather than carrying multiple hypothetical designs.

Important: If you need to explore a major architectural change that may not be implemented, open an RFC issue first. Only create a design branch after the proposal has enough consensus to move forward. This reduces branch proliferation and keeps main as the single source of truth.

GitHub Projects for Dependency and Capacity Planning

Architecture decisions rarely happen in isolation. A move to microservices affects the order service, the auth service, and the deployment pipeline. GitHub Projects v2 gives you a cross-repository board where you can track design dependencies, owner status, and risk levels.

Create a project board linked to your design repo and all implementation repos. Add custom fields like:

  • Component: which system element the decision affects
  • Decision Status: Proposed, Accepted, Implemented, Retired
  • Dependency: linked issue that blocks this decision
  • Risk Level: Low, Medium, High
  • Target Release: version or milestone

A CTO can then view a table of all accepted ADRs that have not yet been implemented, sorted by risk. That view exposes architecture debt—decisions made but not executed. For example, an ADR might say “Adopt event-driven communication” but the core services still use synchronous HTTP calls. The project board makes that gap visible and assignable.

GitHub Projects also supports iteration planning. You can slice by the upcoming quarter and see which architecture changes need to land before a deprecation date. This is far more actionable than a static architecture diagram that shows the target state but not the path to get there.

GitHub Project Field Purpose Example Value
Decision Status Track lifecycle of an ADR Implemented
Component Map decision to system module Order Service
Dependency Link blocking issue or ADR #42 or ADR-0012
Risk Level Prioritize implementation order High
Target Release Align with product roadmap v3.2
Pro Tip: Use the project board’s built-in linked PRs and linked issues columns to automatically show whether an architecture decision has corresponding implementation work. No more guessing if a design was actually built.

Security and Access Control for Design Repositories

A system design repository often contains the most sensitive information about your infrastructure: service topology, database schemas, security boundaries, and network paths. Leaving it public or open to every engineer is a serious operational risk. GitHub’s access controls let you define exactly who can read and modify design artifacts.

Use branch protection rules on the design repo’s main branch to require pull requests, status checks, and reviews from code owners. The CODEOWNERS file then restricts approval authority to specific individuals or teams. For example:

# CODEOWNERS
/adr/            @cto @lead-architect
/diagrams/       @platform-team @security-team
/rfc/            @principal-engineers
/contracts/      @api-guild
/runbooks/       @sre-team

When a PR modifies a file under /adr/, only the CTO and lead architect can approve. That prevents a junior engineer from merging an ADR that changes the primary database without architectural review.

For private repositories, use GitHub’s built-in secret scanning to catch accidentally committed credentials or internal hostnames. You can also restrict repository visibility to specific teams and require SAML SSO for organization members. The design repo should never be public unless you explicitly intend to publish it as an open source reference architecture.

Common Mistake: Teams fork public repos like donnemartin/system-design-primer to use as a template, but then forget to change the fork’s visibility to private. If your internal architecture details end up in that fork, they may be exposed. Always create a fresh private repo and copy only the structure, not the entire history or original README.

Metrics and Anti-Patterns: Measuring Design Governance

Without metrics, a system design repo becomes a graveyard of good intentions. As a CTO, you need leading indicators that tell you whether the architecture governance process is working. The following table lists the metrics that map directly to team velocity and technical debt.

Metric Target Signal If Off Target
ADR age No accepted ADR older than 6 months without a superseding decision Architecture drift or stale documentation
RFC decision cycle time Median < 72 hours from proposal to approve/reject Decision paralysis or lack of ownership
Unimplemented ADR count Less than 10% of accepted ADRs open longer than one quarter Architecture debt accumulating
Diagram update frequency Every diagram touched in at least one PR per release cycle Diagrams out of sync with code
PR review depth Average 2+ comments per design PR Rubber-stamping, no real review

You can compute most of these metrics using GitHub’s API and a simple script that runs weekly. For example, a Python script could query issues and PRs tagged with rfc and calculate median time from open to close. Store the results in a dashboard or a monthly report.

The most harmful anti-pattern is the ghost design repo: a beautiful repository set up in month one, abandoned by month three. Teams revert to whiteboard diagrams and hallway conversations. To prevent this, assign a single owner—usually a staff engineer or tech lead—who is responsible for the repo’s health, much like a code owner. That person reviews all design PRs, prunes stale artifacts, and reports metrics to leadership.

Another common failure is treating the design repo as a storage bucket rather than a decision system. If your team only adds files but never updates or deprecates them, you have created a museum. The governance process must include periodic review cycles where superseded ADRs are marked deprecated and outdated diagrams are removed or updated.

Important: Metrics are only useful if they trigger a response. If your unimplemented ADR count exceeds 10%, schedule a architecture sync to either remove or schedule the work. Otherwise, the design repo reports a false picture of system reality.

Continuing the System Design Journey

This guide covered the core mechanics of using GitHub for system design: repository structure, ADRs, diagram-as-code, RFC issues, Actions, Projects, and security. But each of these topics can go deeper. For a broader set of development and architecture topics, [Explore our complete Software Development directory for more guides.](/topics/topics-software-development/)

Adopting system design GitHub practices moves your architecture from tribal knowledge to a governed, versioned, and reviewable asset. The immediate payoff is fewer architecture re-litigations and faster onboarding. The long-term payoff is lower technical debt and a clearer path from design to implementation.

If your team already has a design repo but it has become stale, or you are considering setting one up, a structured audit can reveal the gaps. NR Studio offers architecture audits that examine your repositories, ADR flow, diagram freshness, and automation coverage. Contact us to see where your system design governance can improve.

NR Studio builds custom web apps, mobile apps, SaaS platforms, and internal tools for growing businesses. If you’re working through a technical decision, feel free to reach out — no commitment required.

References & Further Reading

Leave a Comment

Your email address will not be published. Required fields are marked *