Inkan
↑↓ move↵ open
GitHub

Writing records · ADR

0007: GitHub-flavoured Markdown with diagrams and math

Date

Context#

Records are read in two places: rendered on the site and as source files on GitHub or GitLab. A syntax that renders on the site but shows up as raw text in a pull request slows down review. At the same time decisions need tables, diagrams, formulas and annotated code, which plain CommonMark does not give.

Decision#

Records are GitHub-flavoured Markdown (tables, task lists, strikethrough, autolinks, footnotes), plus the extensions GitHub and GitLab render themselves: > [!NOTE] alerts, ```mermaid diagrams and $ math. Zudoku and Docusaurus :::note admonitions work too and render as the same callout. Code blocks are highlighted by Expressive Code with GitHub-compatible meta strings. Everything on this page is produced by the Markdown source of this file; open “Raw Markdown” at the bottom to compare.

.mdx files are supported too, see MDX components without imports. The features below need no MDX.

How to use it#

Tables#

| Option | Replay | Ops cost | Verdict |
| --- | :---: | ---: | --- |
| Keep SQS | No | Low | Rejected |
OptionReplayOps costVerdict
Keep SQSNoLowRejected
KafkaYesHighAccepted
NATS JetStreamYesMediumShortlisted Dropped

Column alignment with :---: and ---: works. Inline formatting inside cells works too. A table wider than the page scrolls sideways and, while it does, is a focusable region named “Table 1 in <record title>”, “Table 2 in <record title>” and so on, so it can be reached and moved with the keyboard; a table that fits is left alone. Display math gets the same treatment as “Equation 1 in <record title>”.

Task lists#

- [x] Migration script written
- [ ] Consumers switched over
  • Migration script written
  • Topic retention agreed with Payments
  • Consumers switched over
  • SQS queues deleted

The boxes are read-only. A rehype plugin (rehype-task-labels) gives each one an aria-label of “Done” or “Not done” so screen readers announce the state.

Footnotes#

Kafka keeps messages for the configured retention[^retention].
[^retention]: Seven days by default; the ledger topics use 30.

Kafka keeps messages for the configured retention1. Footnotes are collected at the end of the page2.

Alerts#

> [!NOTE]
> Useful information the reader should know.

Note

Useful information the reader should know, even when skimming.

Tip

Helpful advice for doing things better.

Important

Key information the reader needs to achieve the goal.

Warning

Urgent information that needs attention to avoid problems.

Caution

Advises about risks or negative outcomes of an action.

The five GitHub alert types are supported. On GitHub and GitLab the same syntax renders the same boxes. GitLab’s custom titles work as well: write the title after the marker.

> [!tip] Start small
> One availability SLO per service tier is enough.

Start small

One availability SLO per service tier is enough.

Admonitions#

If your team writes Zudoku, Docusaurus or Starlight docs, the ::: block form renders as the same callout. The title goes in {title="..."} or in brackets.

:::danger{title="Do not"}
Never rewrite the decision of an accepted record. Supersede it.
:::
:::info[Bracket title]
Nested callouts use more colons on the outer block (`::::`).
:::

Do not

Never rewrite the decision of an accepted record. Supersede it.

Bracket title

Nested callouts use more colons on the outer block (::::).

TypeLabelStyled as
note, tip, info, important, warning, caution, dangertheir own namethemselves
sparklesNewimportant
rocketLaunchtip
settingsSettingsnote
zapHeads upwarning
lockSecuritydanger
megaphoneAnnouncementinfo

Every callout gets .markdown-alert.markdown-alert-<type>, the same markup GitHub uses, so one CSS rule styles both syntaxes. Only the ::: block form is parsed: word:name and :: in prose stay plain text. A ::: block with any other name is left for remark plugins, which claim it by setting data.hName; unclaimed ones keep their fences as text.

Note

On GitHub and GitLab, ::: blocks render as plain paragraphs. Prefer > [!NOTE] alerts when records are also read in the repository.

Mermaid diagrams#

```mermaid
flowchart LR
Payments --> Kafka --> Settlement
```
flowchart LR
  Payments["Payments<br/>producer"] --> Kafka["Kafka<br/>event bus"]
  Mobile["Mobile<br/>producer"] --> Kafka
  Kafka --> Settlement["Settlement<br/>consumer"]
  Kafka --> Analytics["Analytics<br/>consumer"]

Diagrams are rendered in the browser and follow the site’s light and dark theme. The Mermaid library is loaded only on pages that contain a diagram. Every diagram type Mermaid supports works: sequenceDiagram, stateDiagram-v2, erDiagram, gantt and so on.

Math#

Inline: the error budget is $1 - \text{SLO}$.
$$
\text{burn rate} = \frac{\text{errors observed}}{\text{errors allowed}}
$$

Inline: the error budget is 1−SLO1 - \text{SLO}, so a 99.9% SLO leaves 0.1%0.1\% of requests.

burn rate=errors observed in the windowerrors allowed in the window\text{burn rate} = \frac{\text{errors observed in the window}}{\text{errors allowed in the window}}

Formulas are typeset with KaTeX at build time; no script runs in the browser.

Code blocks#

Fenced code with a language gets syntax highlighting. The meta string after the language is read by Expressive Code:

```sql title="ledger_entries.sql" {2} del={3} ins={4}
CREATE TABLE ledger_entries (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
amount numeric(12, 2),
amount_minor bigint NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
```
ledger_entries.sql
CREATE TABLE ledger_entries (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
amount numeric(12, 2),
amount_minor bigint NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
MetaEffect
title="file.ts"File name tab above the block.
{2}, {2, 5-7}Highlight those lines.
ins={4}, del={3}Mark lines as added or removed, diff style.
"text"Highlight every occurrence of the text.
showLineNumbers=falseHide line numbers. They are on by default.
wrapSoft-wrap long lines. Off by default.

Code blocks without a language are rendered as plain text. ```text is the explicit form. Inline code like inkan.config.ts is just backticks. A block that scrolls sideways is a focusable region named after its title or language (“Code block 2: ledger_entries.sql”), so keyboard users can reach it and screen readers can tell the blocks apart.

Images#

![Anatomy of a record](./_images/record-anatomy.svg)

Anatomy of a record: front matter on top, Markdown body below

Relative image paths are resolved from the Markdown file, optimised at build time and copied to dist/. A folder whose name starts with _ is a good place for them: it is never treated as pages. Absolute URLs (https://...) are used as written.

Also supported#

  • Headings get an id from their text and a # anchor on hover, so [see Consequences](#consequences) works; the table of contents lists h2 and h3.
  • Strikethrough with ~~text~~, autolinked bare URLs such as https://example.com, and HTML comments <!-- like this -->, which are dropped.
  • Relative links to other .md files, see Link records by path.
  • Issue keys that become links, see Turn issue keys into links.
  • Raw HTML in Markdown is passed through as on GitHub. A placeholder that only looks like a tag, such as <project> or <record title>, is shown as written instead of disappearing.

Consequences#

  • One source renders in three places (site, GitHub, GitLab) with the same meaning, so review happens where the pull request is.
  • Expressive Code meta strings (title=, {2}) are ignored by GitHub; the block still renders there, without the decoration.
  • Math and Mermaid syntax inside a record is rendered by GitHub, but GitLab and GitHub differ in details. Keep diagrams simple when the repository view matters.
  • Mermaid runs in the browser. Readers without JavaScript see the diagram source.

Footnotes#

  1. Seven days by default; the ledger topics use 30. ↩

  2. Part of GitHub-flavoured Markdown, which Astro enables for all .md files. ↩

History 1

  1. ImplementedInkan maintainers9e5e535 commit diff