Markdown in /src
TLDR
- Markdown wordt broncode, geen documentatie.
- Markdown zou gecontroleerd (checked in) moeten worden in
/src, direct naast de code die het produceert. - Code en tests zouden afgeleid moeten worden van die Markdown, in plaats van uit vluchtige prompts (of in ieder geval zouden prompt-sessies uiteindelijk moeten worden omgezet in persistente Markdown).
Introductie
Om mijn inkomen als professor aan de Montana State University aan te vullen, doe ik naast mijn werk consulting. Ik geniet van consulting en het schrijven van code en het helpen bouwen van systemen, zowel om die activiteiten zelf als omdat het mijn vaardigheden relevant houdt en me in staat stelt studenten te onderwijzen over de nieuwste ideeën in softwareontwikkeling.
Overduidelijk is het belangrijkste dat er de afgelopen jaren in softwareontwikkeling is gebeurd 'agentic coding': het gebruik van LLM's om code te genereren in plaats van handmatig coderen. Ik heb enkele essays over dit onderwerp geschreven:
- Yes, and…
- Code is Cheap(er)
- The University In The AI Era
- Working With AI: A Concrete Example
In dit essay wil ik een idee bespreken dat steeds duidelijker voor me wordt terwijl ik werk bij bedrijven die prioriteit geven aan agentic coding:
Markdown is nu broncode, geen documentatie.
Dit is natuurlijk geen nieuw of bijzonder vernuftig idee. In Markdown is the new source code schrijft Hartley Brody:
"Het begint erop te lijken alsof de applicatielogica van de software wordt gedefinieerd en bewerkt als markdown, en de uiteindelijke code die door de agent wordt gegenereerd een soort low-level implementatiedetail wordt."
Zoals de bovenstaande essays laten zien, ben ik ambivalent over door AI gegenereerde code. Mijn consultingwerk laat echter zien dat organisaties in deze richting gaan, vaak met een enorme snelheid. In het verdere verloop van dit essay wil ik nadenken over de gevolgen van het feit dat Markdown steeds vaker de 'source of truth' wordt voor softwaresystemen.
De ontbrekende broncode
Er is een denklijn, gevangen in het bovenstaande citaat, dat LLM's vergelijkbaar zijn met compilers: ze nemen high-level specificaties en zetten deze om in low-level implementaties. In deze visie hoeven we niet naar de code te kijken die een LLM genereert, net zoals we niet naar de machinecode kijken die een compiler genereert.
Zoals ik vermeld in Code is Cheap(er), ben ik het om een paar redenen niet volledig eens met deze analogie, maar het punt dat relevant is voor dit essay is: compiler-workflows behouden hun oorspronkelijke broncode, terwijl LLM-workflows dat doorgaans niet doen.
Tegenwoordig wordt door LLM's gegenereerde code vaak gecreëerd via een reeks prompts die aan een agent worden gevoerd terwijl een ontwikkelaar een feature uitbouwt. In de praktijk betekent dit dat de gegenereerde code het dichtstbijzijnde is dat we hebben aan een "ground truth" voor die feature. Er kan documentatie voor de feature elders bewaard zijn (bijv. Linear, Slack-threads, wiki's, etc.), maar voor zover het de codebase betreft, is de gegenereerde code de bron van waarheid.
Mijn mening is dat we in professionele agentic coding-omgevingen moeten accepteren dat door LLM's gegenereerde code die voortkomt uit vluchtige prompt-sessies niet ideaal is, en dat we moeten beginnen met het vastleggen en inchecken van Markdown naast de gegenereerde code in de source-directory.
Markdown als bron
Markdown heeft veel prettige eigenschappen die het vergelijkbaar maken met traditionele broncode:
- Het is platte tekst en is daarom diffable, greppable en reviewbaar in pull requests.
- LLM's lezen en schrijven het native.
- Mensen kunnen het lezen en bewerken zonder tools.
En in feite fungeert het al tot op zekere hoogte als bron in AGENTS.md, specificaties, plannen, TASK.md en dergelijke. We hebben alleen het vastleggen van die bron nog niet gestandaardiseerd.
In Markdown is the new source code zegt Brody dat hij zijn Markdown-bestanden in .scratch/research/ en .scratch/plan/ bewaart terwijl hij werkt. Ik heb de conventie overgenomen om een /tmp-directory aan te maken voor soortgelijke vluchtige behoeften.
Mijn voorstel is dat we sommige van deze bestanden promoten naar een nieuwe directory, naast onze bestaande broncode: /src/md.
De Markdown die in deze voorgestelde directory wordt vastgelegd, zou een lager niveau hebben dan traditionele ontwerpdocumenten:
- Het bevat architecturale beslissingen.
- Het bevat beslissingen op bronniveau.
- Het bevat low-level data-ontwerpbeslissingen.
- Het ligt veel dichter bij een specificatie (hoewel het er geen is) dan bij een ontwerpdocument zoals traditioneel beheerd door een projectmanager of ontwerper.
Localiteit
Ik ben een fan van localiteit, en ik denk dat het verplaatsen van Markdown naar /src sterke voordelen op het gebied van localiteit heeft:
- Code-modules zouden nu de Markdown bevatten die de intentie van de code uitlegt.
- Er is geen mysterieuze "specificatie op afstand", waarbij de logica van het 'waarom' elders in een wiki/Notion/Confluence/Jira staat.
- Markdown in
/srckan worden geconsumeerd door zowel mensen als agents. - Agents hoeven niet langer elders te zoeken om context te krijgen over een bepaalde codebase.
Hoe zit het met Linear/Wiki's/etc.?
Andere bronnen van waarheid voor het gedrag van het systeem kunnen nog steeds bestaan. Deze bronnen zouden hoger-niveau en/of "procesgeoriënteerde" documentatie bieden: high-level ontwerpdocumenten, issues die een resolutie-workflow vereisen, enzovoort.
Maar het kernmatige, huidige en statische beoogde gedrag van het systeem zou steeds vaker direct worden vastgelegd in Markdown in de source-directory.
Hoe zit het met tests?
Ik heb veel mensen online horen zeggen dat tests de nieuwe specificatie zijn (of dat altijd al waren). Ik denk dat daar enige waarheid in zit.
Echter, tests zijn geen goed mechanisme voor interactie tussen mens en agent:
- Ze vereisen veel ceremonie, waardoor vaak wordt versluierd wat er precies wordt getest.
- Ze zijn doorgaans lager-niveau dan de meeste mensen willen behandelen, vooral bij het begrijpen van een systeem.
- Hoger-niveau uitleg, zoals Mermaid-diagrammen, past niet natuurlijk in tests.
Ik denk dat de volgende arbeidsverdeling zinvol is:
- Markdown staat in
/srcen is de specificatie (soort van). - Tests staan in
/test(of waar dan ook) en zijn gebaseerd op die Markdown, waarbij ze zorgen voor geautomatiseerde bevestiging van correctheid.
Opnieuw is het kernidee hier dat een ontwikkelaar, in plaats van code en tests uit prompts te genereren, zou werken aan Markdown in de /src-directory, waaruit de code en tests vervolgens worden afgeleid.
Hoe Markdown in /src/md eruitziet
De Markdown in /src/md bevindt zich tussen een formele specificatie voor het systeem en high-level ontwerpdocumenten.
Net als bij broncode is er een 'Complexity Budget' verbonden aan deze Markdown. Er zal zorgvuldig beheer nodig zijn om deze documenten schoon, goed gefactoriseerd en op het juiste abstractieniveau te houden.
Van ontwikkelaars moet worden verwacht dat ze interactie hebben met zowel de Markdown als de afgeleide code, waardoor het synchroniseren van die twee (wanneer gepast) een belangrijke vaardigheid zal worden.
Bijvoorbeeld, ontwikkelaars zullen vaak subtractief en beperkend werk verrichten aan gegenereerde code, en die wijzigingen moeten mogelijk worden teruggevoerd in de Markdown.
Ik geloof dat agents niet gebruikt zouden moeten worden om veel inhoud in /src/md te genereren. Deze directory zou hoofdzakelijk door mensen worden geschreven en gecureerd.
Een voorgestelde /src/md-conventie
Dit is noodzakelijkerwijs het zwakste deel van dit essay, omdat dit een nieuw idee is en ik het nog niet uitgebreid heb gebruikt. Het is een vorm van hardop denken en een uitnodiging tot discussie.
Dat gezegd hebbende, is hier een mogelijke /src/md-standaard:
src/
md/
README.md # Index van alle md, instappunt voor agents
TODO.md # Een lijst met algemene TODO's open voor deze module
OVERVIEW.md # Een technisch overzicht van deze module
features/
FEATURE_1.md # Een set documenten specifiek voor features
data/
DATAMODEL_1.md # Beschrijvingen van datamodellen in de module
api/
API_1.md # Beschrijvingen van API's die de module biedt
infrastructure/
INFRASTRUCTURE_1.md # Beschrijvingen van infrastructuur gebruikt door de module
Hierbij zijn de directories features, data, api and infrastructure allemaal optioneel; het idee is om te verdelen langs verschillende assen om zo een solide werkomschrijving van het gedrag van de module direct in de /src/md-map vast te leggen.
Conclusie
Naarmate code goedkoper wordt om te genereren, blijft de intentie achter de code waardevol: wat het doet, waarom het dat doet en wat het niet mag doen.
Tegenwoordig gaat die intentie vaak verloren in vluchtige prompt-sessies, of is deze verspreid over wiki's, tickets en Slack-threads.
Ik denk dat we, in naam van localiteit, zouden moeten overwegen deze intentie vast te leggen in Markdown en deze in /src in te checken, naast de code die het produceert, waar zowel mensen als agents het kunnen vinden.
Ik weet nog niet precies wat de juiste structuur is voor iets als /src/md, en ik verwacht dat mijn denken zal veranderen naarmate ik (en anderen) meer ervaring hiermee krijgen.
Maar ik ben er vrij zeker van dat Markdown broncode wordt, en dat we het steeds meer als broncode moeten behandelen.
(Ook al zijn LLM's, nee, geen compilers :) )
Groetjes,