Wenn du heute eine Notion-artige App baust, stehst du an einer Weggabelung: Markdown oder ein Block-System. Beide Optionen haben legitime Verfechter. Wir haben den schwereren Weg gewählt — einen vollständigen Block-Tree mit eigenen Datentypen pro Block — und ich will erklären, warum.
Markdown ist eine Lossy-Compression
Markdown ist großartig für lineare Text-Dokumente. Wenn dein Use-Case Blog-Posts oder README-Dateien ist, dann ist Markdown nahezu perfekt: Es ist plain-text-lesbar, in jeder Toolchain unterstützt, und der Round-Trip von Rohtext zu HTML ist verlustfrei.
Das Problem beginnt, wenn du Strukturen brauchst, die Markdown nicht abbildet. Eine Tabelle mit Inline-Bildern in Zellen? Markdown kann das nicht. Ein collapsible Toggle mit verschachtelten Headings? Nicht in vanilla Markdown. Eine Datenbank-View mit gefilterten Rows? Definitiv nicht. Sobald du diese Features willst, fängst du an, Markdown-Extensions zu erfinden — und sobald du das tust, hast du de facto ein eigenes Format, das nicht mehr Markdown ist, sondern eine Markdown-Dialekt-Variante.
Notion hat das früh erkannt. Sie speichern keine Markdown-Dokumente, sondern Block-Trees. Jeder Block hat einen Typ (Paragraph, Heading, Database-Reference, Embed, Toggle, Synced-Block), eigene Properties und potentiell Children. Dieses Modell ist drastisch mächtiger als Markdown — aber auch drastisch komplexer.
Was wir gewonnen haben
Slash-Menus funktionieren nur in einem Block-System sauber. Wenn du `/` tippst, will der Editor wissen: An welcher Block-Boundary stehst du? Welcher Block-Typ kann an dieser Position eingefügt werden? In einem Block-Tree ist das eine simple Tree-Operation. In Markdown müsstest du den Cursor-Kontext aus dem Plaintext rekonstruieren, was fragil ist.
Drag-and-Drop von Blocks. Im Block-Tree ist jeder Block ein eindeutiges Element mit stabiler ID. Du kannst einen Toggle-Block mit allen Children inklusive verschachteltem State aufnehmen und woanders hineinziehen. Der Tree kümmert sich um die Konsistenz. In Markdown wäre das ein Text-Range-Move, der das Parsing zerstören würde.
Real-Time-Collaboration. Wenn zwei User gleichzeitig im selben Dokument editieren, ist die kleinste Konflikt-Einheit ein Block. Wenn ich Block 4 ändere und du Block 7 änderst, gibt es keinen Konflikt. In Markdown wäre der gemeinsame Buffer ein Text-String, und jede Konflikt-Resolution müsste Zeichen-für-Zeichen über CRDT-Algorithmen passieren — was funktioniert, aber komplexer ist als Block-basierte Konflikte.
Database-Embeds. Unsere Doc-Pages können auf strukturierte Daten verweisen, eine Datenbank inline anzeigen, gefilterte Sub-Views einbetten. Im Block-Tree ist das ein Block-Typ mit einer Reference-ID. In Markdown wäre das ein Custom-Tag, der ein eigenes Parsing braucht.
Was wir verloren haben
Die Komplexität ist real. Unser Block-Tree-Modell hat 23 Block-Typen, jeder mit eigenem TypeScript-Type, eigener Serialisierung, eigenen Validation-Rules. Wenn wir einen neuen Block-Typ hinzufügen — sagen wir einen "Code-Sandbox-Block" — müssen wir Render-Code, Edit-Code, Serialize-Code und Migrate-Code schreiben. Das ist viel mehr Aufwand als ein Markdown-Extension-Token.
Interop ist schwieriger. Du kannst keinen Block-Tree einfach in ein anderes Tool exportieren. Wir bieten Markdown-Export, aber er ist verlustbehaftet — Database-Embeds werden zu Links, Synced-Blocks verlieren ihre Sync-Eigenschaft, custom Block-Properties gehen verloren. Das ist ein bewusster Kompromiss, aber er kostet uns User, die in andere Tools migrieren wollen.
Markdown-Round-Trip-Loss. Wenn ein User Markdown importiert, wir es in unseren Block-Tree konvertieren, der User es editiert, und dann wieder als Markdown exportiert — bekommt er nicht denselben Markdown-Text zurück. Wir normalisieren Whitespace, vereinheitlichen Bullet-Styles, konvertieren Setext-Headings zu ATX-Headings. Für Power-User, die ihren Markdown sehr spezifisch formatieren, ist das ein Reibungspunkt.
Die Lektion
Es gibt keine objektiv richtige Wahl zwischen Markdown und Block-System. Es hängt davon ab, ob dein Produkt linear-textuell ist oder strukturell-komplex. Für uns war klar: Wir bauen Notion-Konkurrenz, nicht Hashnode-Konkurrenz. Unsere User wollen Tabellen, Datenbanken, Toggles, Embeds — Features, die Markdown nicht sauber abbildet.
Aber wir wissen, dass diese Wahl Folgen hat. Wir investieren überproportional viel Engineering-Zeit in den Editor, weil ein Block-System nun mal viel Editor-Logik braucht. Das ist okay — der Editor ist unser zentrales Surface. Aber wenn du eine ähnliche Entscheidung triffst, sei dir der Investition bewusst.