Markdown in /Src

  • Markdown is becoming source code, not documentation
  • That Markdown should be checked in to /src, next to the code it produces
  • Code and tests should be derived from that Markdown, rather than from ephemeral prompts (or at least prompt sessions should eventually turn into persisted Markdown)

In order to supplement my income as a professor at Montana State University, I do consulting on the side. I enjoy consulting and the act of writing code & helping build systems, both for their own sake and also because it keeps my skills relevant and allows me to teach students about the latest ideas in software development.

Obviously the biggest thing to happen in development in the last few years is agentic coding: using LLMs to generate code in lieu of hand coding. I have written a few essays on this topic:

In this essay I want to discuss an idea that is becoming increasingly clear to me as I work in companies that are prioritizing agentic coding:

Markdown is now source code, not documentation.

This is not a novel or particularly clever idea, of course.

In Markdown is the new source code, Hartley Brody writes:

It is starting to feel as if the application logic of the software is being defined and edited as markdown, and the actual code that is generated by the agent is sort of becoming a low-level implementation detail.

Now, as the essays above show, I am ambivalent about AI-generated code. However, my consulting work shows that organizations are headed in this direction, often at terrific speed.

What I want to do in the remainder of this essay is think about the ramifications of Markdown becoming, more and more, the source of truth for software systems.

There is a line of thinking, captured in the quote above, that LLMs are akin to compilers, taking high-level specifications and turning them into low-level implementations. In this view, we don’t need to look at the code an LLM generates, just as we don’t look at the machine code a compiler generates.

As I mention in Code is Cheap(er), I do not totally agree with this analogy for a few reasons, but the one relevant to this essay is: compiler workflows retain their original source code while LLM workflows typically do not.

Today, LLM-generated code is often created via a string of prompts fed into an agent as a developer builds out a feature. In practice, this means that the generated code is the closest thing we have to “ground truth” for that feature. There may be documentation for the feature stored elsewhere (e.g. Linear, Slack threads, wikis, etc.) but, so far as the codebase is concerned, the generated code is the source of truth.

My opinion is that, in professional agentic coding environments, we need to accept that LLM-generated code that emerges from ephemeral prompting sessions is not ideal, and begin moving towards capturing and checking in Markdown alongside generated code in the source directory.

Markdown has many nice properties that make it similar to traditional source code:

  • It is plain text and therefore diffable, greppable and reviewable in pull requests
  • LLMs read and write it natively
  • Humans can read and edit it without tools

And, in fact, it is already acting as source, to an extent, in AGENTS.md, specs, plans, TASK.md and so forth. We just haven’t standardized capturing that source yet.

In Markdown is the new source code, Brody says he keeps his Markdown files in .scratch/research/ and .scratch/plan/ as he works. I have adopted the convention of creating a /tmp directory for similar ephemeral needs.

My proposal is that we promote some of these files to a new directory, alongside our existing source code: /src/md

The Markdown captured in this proposed directory would be lower level than traditional design documents:

  • It contains architectural decisions
  • It contains source-level decisions
  • It contains low-level data design decisions

It is much closer to a specification (although it is not one) than a design document as traditionally managed by a project manager or designer.

I am a fan of locality, and I think that moving Markdown into /src has strong locality advantages:

  • Code modules would now include the Markdown that explains the intent of the code
  • There is no spooky “specification at a distance”, where the logic of why is elsewhere in a wiki/Notion/Confluence/Jira
  • Markdown in /src can be consumed by both humans and agents
  • Agents no longer need to look elsewhere to get context on a given codebase

Other sources of truth for the behavior of the system can still exist. These sources would provide higher-level and/or “process-oriented” documentation: high-level design documents, issues that need a resolution workflow and so forth.

But the core, current and static intended behavior of the system would increasingly be captured directly in Markdown in the source directory.

I have seen many people online saying that tests are the new specification (or always were). I think there is some truth to that.

However, tests are not a good mechanism for human/agent interaction:

  • They involve a lot of ceremony, often obscuring what they are testing
  • They are typically lower level than most humans want to deal with, particularly when understanding a system
  • Higher-level explanations such as Mermaid diagrams don’t fit naturally into them

I think the following division of labor makes sense:

  • Markdown sits in /src and is the specification(ish)
  • Tests sit in /test (or wherever) and are based on that Markdown, providing automated confirmation of correctness

Again, the core idea here is that, rather than generating code and tests from prompts, a developer would work on Markdown in the /src directory, from which the code and tests would be derived.

The Markdown in /src/md sits between a formal specification for the system and high-level design documents.

As with source code, there is a Complexity Budget associated with this Markdown. It will require thoughtful management to keep these documents clean, well-factored and at the right level of abstraction.

Developers should be expected to interact with both the Markdown and the derived code, so synchronizing the two (when appropriate) will become an important skill.

For example, developers will often do subtractive, constraining work on generated code, and those changes may need to be moved back into the Markdown.

I believe that agents should not be used to generate much content in /src/md. This directory should be mainly human authored and curated.

This is necessarily the weakest part of this essay because this is a new idea and I haven’t used it extensively yet. It is me thinking out loud and inviting discussion.

With that said, here is a possible /src/md standard:

src/
  md/
    README.md          # index of all md, entry point for agents
    TODO.md            # a list of general TODOs open for this module
    OVERVIEW.md        # a technical overview of this module
    features/FEATURE_1.md       # a set of feature-specific documents
    data/DATAMODEL_1.md         # descriptions of data models in the module
    api/API_1.md                # descriptions of APIs the module provides
    infrastructure/INFRASTRUCTURE_1.md   # descriptions of infrastructure used by the module

Here the features, data, api and infrastructure directories are all optional; the idea is to divide along different axes to best capture a solid working description of the module’s behavior directly in the /src/md folder.

As code gets cheaper to generate, what remains valuable is the intent behind the code: what it does, why it does it, and what it must not do.

Today that intent is often lost in ephemeral prompting sessions, or scattered across wikis, tickets and Slack threads.

I think that, in the name of locality, we should consider capturing this intent in Markdown and checking it in to /src, alongside the code it produces, where both humans and agents can find it.

I don’t know exactly what the right structure for something like /src/md is yet, and I expect my thinking will change as I (and others) get more experience with it.

But I am fairly confident that Markdown is becoming source code, and that we should increasingly treat it like source code.

(Even though, no, LLMs are not compilers :)

添加评论
点赞收藏
点踩分享查看原文
评论
?
参与讨论