Since school I have had a problem with notes. Taking legible notes during a lecture was never possible for me, so I would rewrite everything neatly in the evening. Digital notes, a tablet with a stylus, OneNote: I tried a lot. But none of it ever truly convinced me.

When my second son was born I had a month off and read Tiago Forte’s book: How to Build a Second Brain. PARA and CODE clicked immediately. Finally a framework that made sense. But the longer I worked with it, the clearer the problem became:

All of these approaches treat knowledge as text.

As a developer, my knowledge also lives in code.

PARA structures by purpose, not topic: not “where does this belong?” but “which project needs it?” CODE keeps information moving from capture to expression. Information serves a purpose, or it stays out. Right foundation. But only for text.

The Difference Between Describing and Understanding

PARA answers: “What do I use this for?” When developing, I am often more interested in: “What happens when I change this?”

Code is not an artifact, it is a thinking tool.

I have a note that says: use a tag type to distinguish iterators between containers. But six months later I could not reconstruct why.

// Why not just: using iterator = T*?
// std::iterator_traits<T*> is specialized, all STL algorithms work.
// But then vector<int>::iterator and static_array<int, 5>::iterator
// are the same type — the compiler cannot catch misuse across containers.
//
// Fix: a Tag parameter. Empty struct, private to the container.
// Zero runtime cost, the compiler erases it entirely.
// Each container gets a distinct iterator type. Misuse becomes a compile error.
template <typename T, typename Tag, bool IsConst>
class pointer_iterator { /* ... */ };

// Declared inside static_array — no outside type can share it:
class static_array {
    struct iterator_tag {};
    using iterator = pointer_iterator<T, iterator_tag, false>;
};

The comments are the note. The code is the proof. But that only works if the note and the code can actually live together.

Code as a First-Class Citizen

For a long time I had everything across separate repos: blog, journal, Zettelkasten, small projects. A note about a data structure and the corresponding code lived in different places. Today everything is in a monorepo. A thought and the code that proves it live in the same repository, often in the same commit.

Kepano, the CEO of Obsidian, called it “File over App.” Knowledge does not belong in a tool, it belongs in files you own and can read without anyone running a server. That applies to Markdown just as much as to code, with the difference that code can be executed.

Writing and Seeing

My current setup uses two tools, not one. Neovim for writing: fast, close to the code, with the movements I have known for years. Obsidian for the graph view: I see which notes are connected, which are isolated, where clusters form. That overview I cannot get in Neovim.

Obsidian Vault Graph View

For a long time I thought I had to choose: Obsidian or Neovim. Every time the question was: which tool is the right one?

The question was wrong. As long as my notes are plain Markdown in a Git repo, I can use both tools in parallel. Neovim writes, Obsidian shows, both work on the same files. The repo is the source, the tools are views on it.

At the start of this post I wrote that for a long time I could not name why none of the earlier systems worked. Today I can: because they made the tool the source, not the file.