# Docs as Code

Course: [Markdown](https://www.devtutorials.ai/learn/markdown) · Section: Markdown for Teams
Source: https://www.devtutorials.ai/learn/markdown/team-docs/docs-as-code

> Treat docs like code. Lint a docs page with fourteen problems, lay out a docs folder, write a README that answers the first five questions, write one sentence per line for clean diffs, and add Mermaid diagrams and alerts.

## A Docs Page With Fourteen Problems

The order-support team keeps its docs in the same repository as the code. Ravi adds a page about refunds in a hurry. It renders, more or less. But the team's checks fail on his pull request.

````md runnable title="A page the checks reject"
# Refunds
## How to refund
#### Steps
1. Open the order
2. Approve
**Limits**

Refunds over 5000 need approval. See https://example.com/policy for details.
```
npm run refund -- --order A-1042
```
![](refund-flow.svg)

For more, [click here](docs/limits.md).
# Contact
````

Open the **Checks** tab. It lists 14 problems, each with its line number: headings with no blank lines around them, a jump from level 2 to level 4, a code block with no language hint, an image with no alt text, a bare web address, vague link text, two level-1 headings.

None of these stops the page from showing. But each one makes the page harder to read, harder to link to, or harder for an AI agent to follow. A **linter** finds them for you, on every change.

In this lesson, you will learn **docs as code**: treating docs like code, with files in the repo, reviews, and automatic checks.

**In simple words:** docs as code means your docs live next to your code, change in the same pull requests, and are checked by the same kind of tools.

## TL;DR

- **Docs as code** means docs are Markdown files in the repository. They are reviewed in pull requests and checked by tools, like code.
- A **linter** reads a file and reports problems by rule. The standard tool for Markdown is **markdownlint**. Each rule has an id, like `MD001`.
- A good **README** answers five questions: what is it, why use it, how to install it, how to use it, and where to get help.
- Write **one sentence per line** in docs. Diffs then show exactly which sentence changed.
- **Mermaid** draws diagrams from text in a code block. **Alerts** make note and warning boxes. Both work on GitHub and most docs sites.

## A Real-World Example

Think about a restaurant kitchen. Recipes are written down in a consistent format instead of being remembered by only one person. When a recipe changes, someone reviews it before the updated version is used. Regular checks also help identify missing or incorrect information.

Docs as code applies a similar process to documentation. Documentation is stored in files, often alongside the source code, and managed using the same development practices.

When documentation changes:

- The changes are recorded in version control.
- Other team members can review them.
- Automated tools can check for common problems.
- Approved changes can be published through a repeatable process.

A linter is an automated tool that checks documentation against a set of rules. It can find issues such as broken links, incorrect heading order, inconsistent formatting, and missing information.

## What You Will Learn

In this lesson, you will learn:

1. What docs as code means, and how a docs folder is laid out.
2. What a README must answer.
3. How a Markdown linter works, and the most useful rules.
4. Why one sentence per line makes reviews easier.
5. How to draw diagrams with Mermaid.
6. How to write note and warning boxes with alerts.

## Cheat Sheet

````md runnable title="Docs as code in one file" dialect="docs"
# Order Support

Answers customer questions about orders, refunds and shipping.

> [!NOTE]
> Needs Node 20 or later.

## Install

```bash
npm install
```

## How a refund works

```mermaid
flowchart LR
  A[Request] --> B{Over ₹5,000?}
  B -- No --> C[Approve]
  B -- Yes --> D[Manager approves]
```
````

This box starts on the **Docs site** flavour, so the note and the diagram are drawn. Open the Checks tab: this file has no problems.

## Docs Next to the Code

### Why Docs Live in the Repo

When docs live in a wiki or a shared drive, they drift. The code changes, and nobody updates the page. Months later, the docs describe an app that no longer exists.

When docs live **in the repository**, three things change:

- A pull request can change the code and the docs together. A reviewer sees both.
- Every version of the docs matches a version of the code. Old releases keep their old docs.
- AI coding agents can read the docs. An agent sees files in the repo. It usually cannot see your wiki.

### A Simple Docs Folder

Most projects use a layout like this:

```text title="The order-support docs folder"
order-support/
├── README.md            what it is, install, quick start
├── CONTRIBUTING.md      how to propose a change
├── CHANGELOG.md         what changed in each release
├── AGENTS.md            instructions for AI coding agents
└── docs/
    ├── index.md         the start page, with links to everything
    ├── refunds.md
    ├── shipping.md
    └── images/
        └── refund-flow.svg
```

Keep names short, in lowercase, with hyphens instead of spaces. Keep pictures in an `images` folder next to the pages that use them. Give the folder an `index.md` that links to every page, so people and agents have one place to start.

## The README

### The First Five Questions

The **README** is the first file anyone opens. It is shown on the front page of the repository. A good README answers five questions, in this order:

1. **What is it?** One or two sentences.
2. **Why would I use it?** The problem it solves.
3. **How do I install it?** The exact commands.
4. **How do I use it?** The smallest working example.
5. **Where do I get help?** Docs, issues, a contact.

````md runnable title="A README that answers all five"
# Order Support

A small service that answers customer questions about orders, refunds and shipping.

## Why use it

Support staff find the refund and shipping rules in one place, and the rules match the code.

## Install

```bash
npm install
```

## Usage

```bash
npm start
```

Then open <http://localhost:3000>.

## Get help

Read the [docs](docs/index.md) or open an issue.
````

Open the Outline tab. The title answers "what is it", and the four sections answer the other four questions. The Checks tab is clean.

## Linting Markdown

### What a Linter Does

A **linter** is a tool that reads a file and reports problems, one per rule. It does not change the meaning of your text. It checks the form: blank lines, heading levels, missing alt text.

The standard Markdown linter is **markdownlint**. It runs in VS Code, on the command line, and in CI, the checks that run on every pull request. Each rule has an id and a name. The Checks tab in this course uses the same ids, for a small set of the most useful rules.

| Rule | Name | What it catches |
|---|---|---|
| `MD001` | heading-increment | a heading that skips a level, like `##` to `####` |
| `MD004` | ul-style | mixed bullet marks in one file |
| `MD009` | no-trailing-spaces | stray spaces at the end of a line |
| `MD012` | no-multiple-blanks | two or more blank lines in a row |
| `MD018` | no-missing-space-atx | `#Title` with no space |
| `MD022` | blanks-around-headings | a heading with no blank line before or after it |
| `MD025` | single-h1 | more than one level-1 heading |
| `MD031` | blanks-around-fences | a code block with no blank line around it |
| `MD032` | blanks-around-lists | a list with no blank line before it |
| `MD034` | no-bare-urls | a web address that is not in `< >` or a link |
| `MD036` | no-emphasis-as-heading | a bold line used as a heading |
| `MD040` | fenced-code-language | a code block with no language hint |
| `MD045` | no-alt-text | an image with empty alt text |
| `MD059` | descriptive-link-text | link text like "click here" |

Go back to Ravi's page at the top. Fix one problem at a time, and watch the Checks count fall. Can you get it to zero?

The real markdownlint has about 50 rules, a config file, and an automatic fix mode. The Checks tab here is a small subset that uses the same rule ids, so the names you learn here match what you will see in your editor.

### Choosing Rules for a Team

A team turns rules on and off in a config file at the root of the repo. Here is a small one:

```json title=".markdownlint.jsonc"
{
  "default": true,
  "MD013": false,
  "MD033": { "allowed_elements": ["details", "summary", "kbd"] }
}
```

`"default": true` turns every rule on. `MD013` is the line-length rule; many teams turn it off. `MD033` warns about HTML in Markdown; this config allows three useful tags.

The point of a linter is not a perfect score. It is that every page follows the same rules, without anyone having to remember them.

Lint rules also help AI agents. A file with one level-1 heading, no skipped levels and a language hint on every code block is a file an agent can split into sections and copy commands from without guessing.

## One Sentence per Line

Markdown joins lines inside a paragraph, as you learned in the Headings lesson. Docs writers use that on purpose. They put **one sentence on each line**.

The page looks exactly the same. But the diff of a change becomes much clearer:

```diff title="A diff when the paragraph is one long line"
-Refunds are allowed within 30 days. Damaged items need a photo. Refunds over ₹5,000 need approval.
+Refunds are allowed within 14 days. Damaged items need a photo. Refunds over ₹5,000 need approval.
```

```diff title="A diff when there is one sentence per line"
-Refunds are allowed within 30 days.
+Refunds are allowed within 14 days.
 Damaged items need a photo.
 Refunds over ₹5,000 need approval.
```

In the first diff, the reviewer has to hunt for the change in a long line. In the second, only the changed sentence is marked.

```md runnable title="One sentence per line renders as one paragraph"
Refunds are allowed within 30 days.
Damaged items need a photo.
Refunds over ₹5,000 need approval.
```

The Rendered tab shows one normal paragraph.

## Diagrams With Mermaid

**Mermaid** is a way to draw diagrams from text. You write the diagram in a code block with the language hint `mermaid`. GitHub and most docs sites draw it as a picture.

Because the diagram is text, it lives in the repo, it can be reviewed in a diff, and an AI agent can read it.

````md runnable title="A flowchart" dialect="docs"
```mermaid
flowchart TD
  A[Customer asks for a refund] --> B{Has a receipt?}
  B -- Yes --> C[Approve the refund]
  B -- No --> D[Ask for the order number]
  D --> C
```
````

`flowchart TD` means a flowchart drawn top to down. Each box has an id and a label: `A[Customer asks for a refund]`. `{ }` makes a diamond, for a question. `-->` draws an arrow, and `-- Yes -->` adds a label to it.

Change a label and watch the diagram redraw. Switch to **GitHub (GFM)**: you see the code, because the GFM spec itself does not draw diagrams. GitHub's website does.

````md runnable title="A sequence diagram" dialect="docs"
```mermaid
sequenceDiagram
  Customer->>Support: Asks for a refund
  Support->>Billing: Checks the payment
  Billing-->>Support: Payment found
  Support-->>Customer: Refund approved
```
````

A **sequence diagram** shows messages between people or systems, in time order. `->>` is a message. `-->>` is a reply.

Keep diagrams small: five to ten boxes. A big diagram is hard to read as a picture and very hard to read as text. If a diagram needs thirty boxes, split it, or describe it in a list.

## Alerts

**Alerts** are coloured boxes for notes and warnings. They are a blockquote whose first line is a type in square brackets. GitHub and many docs sites support five types.

```md runnable title="The five alert types" dialect="docs"
> [!NOTE]
> Refunds are paid back to the original card.

> [!TIP]
> Most refunds are approved within one day.

> [!IMPORTANT]
> Every refund needs an order number.

> [!WARNING]
> Refunds over ₹5,000 need a manager's approval.

> [!CAUTION]
> A refund cannot be undone once it is paid.
```

Use them rarely. A page with five warnings has no warnings: readers stop seeing them. One or two per page, for things that really matter, is plenty.

## Where Docs as Code Is Used

- **Open-source projects:** README, CONTRIBUTING and docs folders, checked in CI.
- **Product docs sites:** built from Markdown in the repo on every release.
- **Internal teams:** runbooks and how-tos next to the services they describe.
- **AI-assisted teams:** agents read the docs folder and update docs in the same pull request as the code.

## Common mistakes

| Symptom | Cause | Fix |
|---|---|---|
| The linter reports `MD022` on every heading | No blank lines around headings | Put a blank line before and after each heading |
| `MD001` heading-increment | A heading skips a level | Go down one level at a time |
| `MD040` fenced-code-language | A code block has no language hint | Add `bash`, `json`, `text` … after the opening fence |
| A review shows a whole paragraph as changed | The paragraph is one long line | Write one sentence per line |
| A Mermaid diagram shows as code | The app does not draw Mermaid, or the hint is missing | Use the hint `mermaid`; check the app supports it |
| A Mermaid diagram shows an error | A typo in the diagram text | Check arrows and brackets; fix one line at a time |
| Docs describe last year's app | Docs live outside the repo | Move them into the repo and update them in the same pull request |

## Quick Check

## Exercises

## What to Remember

- Docs as code: docs are files in the repo, changed in pull requests, checked by tools.
- A README answers five questions: what, why, install, use, help.
- A linter checks form by rule. markdownlint rules have ids like `MD001` and `MD040`.
- Write one sentence per line, so diffs show the exact change.
- Mermaid draws small diagrams from text in a `mermaid` code block.
- Alerts make note and warning boxes. Use one or two per page.

## Further Reading

- [markdownlint rules](https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md): every rule, with examples.
- [Mermaid docs](https://mermaid.js.org/intro/): flowcharts, sequence diagrams and more.
- [Make a README](https://www.makeareadme.com): what a good README contains.

Next: **Frontmatter Basics**, where you write the YAML block at the top of a file, and fix the small mistakes that break it.
