- Artikel
- 08
- Categorie
- Notities
- Leestijd
- 14 min lezen
Content as code: de meest onderschatte manier om een robuuste website te bouwen
Waarom websitecontent opslaan als open, versiebeheerbare bestanden publiceren goedkoper, overdraagbaarder, betrouwbaarder en minder afhankelijk van klassieke CMS-platformen kan maken.
Op een bepaald moment in het leven van bijna elke institutionele website krijgt een redacteur de vraag om een datum aan te passen. Dat zou dertig seconden moeten duren. In plaats daarvan opent die een browser, meldt zich aan met tweestapsverificatie, zoekt in een administratief dashboard naar een contentitem waarvan de interne titel niet langer overeenkomt met de publieke titel, klapt een reeks geneste componenten open, vindt de juiste zin, past die aan, bewaart een concept, wacht op een preview, stelt vast dat die preview niet helemaal overeenkomt met de gepubliceerde pagina, dient de wijziging in en stuurt vervolgens een bericht naar iemand met publicatierechten. Misschien moet er nog een cache vernieuwd worden. Misschien moet er een geplande deployment draaien. De datum, die begon als twaalf tekens publieke informatie, krijgt plots het ceremonieel gewicht van een klein infrastructuurproject. Iedereen noemt dat normaal, omdat het systeem een enterprise-CMS is en enterprise-software ons heeft geleerd complexiteit te verwarren met ernst.
Diezelfde organisatie zal die website doorgaans ook toekomstbestendig noemen. Er is een leverancier, een onderhoudscontract, een rechtenmodel, een workflow, een reeks herbruikbare componenten, API's, een designsysteem en ergens in een presentatie een architectuurdiagram met enkele geruststellende pijlen. Maar de echte kennis van de organisatie (pagina's, toelichtingen, beleidsinformatie, definities, waarschuwingen en instructies waar mensen op vertrouwen) kan alleen gelezen en aangepast worden via de databank, het contentmodel en de interface van dat ene systeem. Zodra het contract afloopt, het platform end-of-life wordt, de integrator verdwijnt, het schema verandert of een nieuwe redesign een migratie vereist, verandert de zogenaamd toekomstbestendige content in een onderhandeling over gijzelaars. We hebben een kluis gebouwd met een touchscreen op de deur en feliciteren onszelf omdat het touchscreen afgeronde hoeken heeft.
Er bestaat een eenvoudiger model, en opvallend genoeg blijft het buiten softwaredocumentatie en kleinere publicatieteams onderbenut: content as code. De term klinkt technischer dan de praktijk is. Hij betekent niet dat elke beleidsredacteur JavaScript moet leren of dat een communicatiemedewerker voor de lunch een buildpipeline moet debuggen. Hij betekent dat de canonieke bron van redactionele websitecontent wordt opgeslagen in gewone tekstbestanden (meestal Markdown, vaak met bovenaan een klein blok gestructureerde metadata) binnen een repository met versiebeheer. Een buildproces zet die bestanden om in webpagina's. Code regelt presentatie en gedrag; content blijft content. Formulieren, transacties, authenticatie en databanken kunnen perfect blijven bestaan waar ze werkelijk nodig zijn. Het punt is niet om elke digitale dienstverlening te reduceren tot een statische brochure. Het punt is om duurzame publieke kennis niet langer op te sluiten in de tijdelijke toepassing waarmee ze vandaag wordt gepubliceerd.
Markdown is precies vanuit die scheiding ontworpen. John Gruber bracht het in 2004 uit, met belangrijke input van Aaron Swartz, als een leesbaar platte-tekstformaat dat tegelijk naar geldige HTML kon worden omgezet. De ontwerpfilosofie was helder: een Markdown-document moet begrijpelijk blijven voor er software aan te pas komt. Gruber maakte expliciet een onderscheid tussen een schrijfformaat en een publicatieformaat. Dat onderscheid bevat eigenlijk het hele argument. Een kop geschreven als ## Voorwaarden is al een kop voor een mens, een script, een zoekindex en een AI-systeem. Een link blijft zichtbaar een link. Een lijst blijft zichtbaar een lijst. De bron lost niet op in een woud van tags, databankvelden of geserialiseerde blokken. Ze is niet draagbaar omdat een leverancier belooft dat er ergens een exportknop bestaat; ze is draagbaar omdat ze begint in een vorm die leesbaar blijft met tools die eenvoudiger zijn dan de website zelf.
Het moderne CMS heeft die verhouding omgekeerd. Het begon als een manier om redacteurs te behoeden voor handmatig HTML schrijven, op zich een redelijk doel, en groeide vervolgens uit tot een omgeving waarin redacteurs vrijwel niets meer zien van de structuur die hun content werkelijk draagt. De grafische interface biedt een geruststellende simulatie: een toolbar, een tekstvak, misschien een stapel versleepbare kaarten. Maar het web is al lang geen omgeving meer waarin what you see is what you get echt opgaat. Wat een redacteur op een laptop ziet, is niet wat iemand op een smartphone ziet, wat een screenreader voorleest, wat een zoekmachine indexeert, wat een API teruggeeft of wat een assistent uit de pagina haalt. De GUI haalt complexiteit niet weg. Ze verplaatst die uit beeld en maakt de redacteur afhankelijk van wie begrijpt wat de interface verborgen houdt. Dat is geen democratisering. Het is een arbeidsverdeling die aangeleerde afhankelijkheid produceert.
Een grafische editor kan nochtans nuttig zijn, zolang hij een venster op open bestanden blijft en niet de plaats waar de kennis van de organisatie wordt opgesloten. Obsidian toont dat verschil opvallend goed. Het bewaart notities als Markdown-bestanden in een gewone lokale map, en die bestanden kunnen ook met andere teksteditors worden geopend of met Git worden beheerd. Obsidian zelf is propriëtaire software, wat het voorbeeld net interessanter maakt: de toepassing kan propriëtair zijn terwijl de inhoud structureel onafhankelijk blijft. De editor biedt preview, links, navigatie, zoeken en een aangename schrijfervaring, maar vraagt niet dat de gebruiker de onderliggende bestanden opgeeft. Als Obsidian morgen verdwijnt, worden de notities geen archeologisch materiaal. Ze blijven gewoon tekst. Dat is wat een goede interface hoort te doen: frictie verminderen zonder gevangenschap te produceren.
De tweede helft van content as code is Git, en daar worden organisaties vaak onnodig schuchter van. Elke professionele schrijver, redacteur en kenniswerker zou de basisconcepten van versiebeheer moeten kennen: repository, commit, branch, diff, merge en revert. Niet omdat iedereen plezier moet beleven aan een command line, en al helemaal niet omdat de command line moreel superieur zou zijn, maar omdat deze begrippen intussen tot elementaire institutionele geletterdheid behoren. We verwachten vandaag al dat redacteurs overweg kunnen met wijzigingen bijhouden, opmerkingen, documentgeschiedenis, bestandsnamen en goedkeuringen. Git biedt een striktere, betrouwbaardere en veel beter overdraagbare versie van diezelfde ideeën. Het registreert exact wat veranderde, wanneer, door wie en waarom. Een redacteur kan versies regel per regel vergelijken, een aanpassing testen zonder de gepubliceerde bron te overschrijven, verschillende gerelateerde wijzigingen samen beoordelen en terugkeren naar een vorige toestand zonder in een gedeelde map te moeten zoeken naar final_v7_echt-definitief.docx.
Git is bovendien opensourcesoftware, uitgebracht onder GPLv2, en de ontstaansgeschiedenis zou verplichte lectuur mogen zijn voor iedereen die leveranciersafhankelijkheid als een theoretisch risico beschouwt. Het Linux-kernelproject gebruikte ooit het propriëtaire versiebeheersysteem BitKeeper. In 2005 liep de verhouding tussen de Linux-gemeenschap en de commerciële ontwikkelaar van BitKeeper spaak en verdween de gratis toegang. Linus Torvalds en de gemeenschap bouwden daarop Git, met onder meer snelheid, eenvoud, gedistribueerd werken, duizenden parallelle branches en ondersteuning voor een project van de omvang van de Linux-kernel als ontwerpdoelen. Een van de meest universele samenwerkingstools in de hedendaagse informatica bestaat dus juist omdat een cruciaal project ontdekte dat toegang die door een bedrijf wordt verleend niet hetzelfde is als controle. Veel subtieler kan de les moeilijk zijn.
Voor redactioneel werk verandert Git samenwerking van een opeenvolging van documenten in een geschiedenis van beslissingen. Een redacteur stelt een wijziging voor. Een inhoudelijk expert reageert op een specifieke regel. Een jurist keurt een onderdeel goed en plaatst een vraag bij een ander. Een ontwikkelaar ziet meteen dat een inhoudelijke wijziging ook een aanpassing aan een template vereist. Automatische controles kunnen voor publicatie een gebroken link, ontbrekende metadata, een foutieve koppenstructuur of een vervallen reviewdatum signaleren. De discussie blijft gekoppeld aan de wijziging in plaats van verspreid te raken over e-mail, chat en vergaderverslagen. De gepubliceerde versie en de reden waarom ze de gepubliceerde versie werd, maken deel uit van hetzelfde institutionele geheugen. Dat is betere samenwerking, niet omdat softwareontwikkelaars ze hebben uitgevonden, maar omdat de methode expliciet is. De meeste kantoortools zijn goed in mensen laten praten rond een document. Versiebeheer is goed in bewaren wat ze uiteindelijk beslist hebben om eraan te veranderen.
Dat is geen theoretische workflow. GitLab behandelt documentatie als onderdeel van het product: wijzigingen aan documentatie lopen mee via issues en merge requests, redacteurs en ontwikkelaars reviewen ze samen en documentatie hoort tegelijk met relevante productwijzigingen te verschijnen. De bron van die documentatie staat in repositories naast de productcode en de publieke documentatiesite wordt gegenereerd met Hugo en als statische site uitgerold. Het belangrijke zit niet in GitLab of Hugo als merk. Beide kunnen vervangen worden. Het belangrijke is dat de redactionele bron, de reviewgeschiedenis, het softwaregedrag en het publicatieproces een gedeeld systeem van waarheid gebruiken. De documentatie wacht niet in een apart CMS tot iemand zich herinnert dat het product vorige dinsdag is veranderd.
Zodra content als bestanden wordt opgeslagen, wordt ze werkelijk modulair. Een pagina kan een bestand zijn. Een herbruikbare waarschuwing een ander. Metadata kan titel, eigenaar, canonieke URL, taal, reviewinterval, publicatiestatus en onderwerp vastleggen. Een mappenstructuur kan een informatiearchitectuur ondersteunen zonder te doen alsof een folderboom op zichzelf de informatiearchitectuur is. Links kunnen automatisch worden gecontroleerd. Gerelateerde content kan gegenereerd worden. Vertalingen kunnen aan elkaar gekoppeld worden. Een contentinventaris kan ontstaan zonder dat eerst bij een leverancier een databankexport moet worden aangevraagd. Klassieke CMS'en beweren ook modulair te zijn, maar bedoelen doorgaans dat content is opgedeeld in entiteiten die door dat CMS begrepen worden: nodes, blocks, fields, widgets, paragraphs, taxonomieën, relaties en interne ID's. Een API heft die afhankelijkheid niet op. Ze geeft je alleen een gedocumenteerde voordeur naar de abstractielaag van iemand anders. Een headless CMS verwijdert het hoofd; het verwijdert de lock-in niet.
Bestanden nodigen ook uit tot nuttige automatisering omdat hun structuur zichtbaar blijft. Een kort script kan elke pagina zoeken waarin een verouderd jaartal staat, alle content zonder eigenaar oplijsten, berekenen welke pagina's aan review toe zijn, gebroken interne links vinden, canonieke URL's vergelijken, een spreadsheet voor redacteurs genereren of een schone corpusversie maken voor zoek- en AI-toepassingen. Dat zijn geen exotische machinelearningprojecten. Het zijn alledaagse taken in Python, shellscripts of tientallen andere breed aangeleerde talen. Ik leer een collega liever een keer Markdown en vijf Git-handelingen dan dat ik diezelfde collega eerst de eigenaardigheden van Drupal, daarna WordPress en vervolgens een propriëtair headless platform aanleer, telkens met hun eigen jargon en migratiedrama. En ja, ik zou ook liever zien dat meer medewerkers Python leerden; R is volkomen respectabel, Excel is nuttig en Power BI heeft zeker zijn plaats, maar geen van die tools biedt dezelfde algemene hefboom als een taal die de echte bronbestanden van een organisatie kan lezen, transformeren en publiceren.
Het kostenverschil zit niet alleen in licenties. Een databasegedreven CMS vereist een draaiende applicatie, een databank, updates, beveiligingspatches, pluginbeheer, omgevingsbeheer, specialistische kennis, back-ups, monitoring en periodieke upgrades waarvan de officiële omschrijving opvallend vaak weinig lijkt op de uiteindelijke factuur. Een agentschap kan nodig zijn om het contentmodel aan te passen omdat het contentmodel intussen infrastructuur is geworden. Een tweede agentschap kan vervolgens nodig zijn om de interpretatie van het eerste agentschap van het eerste platform naar het tweede platform te migreren. Content as code schaft engineering of governance niet af, maar vermindert wel radicaal hoeveel machinepark nodig is om tekst te publiceren. Een build kan bestanden omzetten in statische HTML en assets die goedkoop te hosten, eenvoudig te cachen en moeilijk stuk te krijgen zijn tijdens runtime. Het dure werk verschuift naar zaken waarvoor betalen zinvol is: informatiearchitectuur, toegankelijkheid, schrijven, ontwerp, zoekfunctionaliteit en echte digitale dienstverlening.
Betrouwbaarheid volgt uit dezelfde vermindering van bewegende onderdelen. Een statische pagina hoeft niet bij elke bezoeker een contentdatabank te bevragen. Een mislukte build kan voor publicatie falen in plaats van nadat een gebruiker arriveert. Elke uitgerolde versie kan overeenkomen met een commit, en terugrollen kan betekenen dat je een bekende toestand opnieuw uitrolt in plaats van een databank te herstellen en te hopen dat geüploade bestanden, configuratie en plugins het over dezelfde geschiedenis eens zijn. De concepten zijn oud, saai en universeel: bestanden, hashes, tekst, mappen, transformaties, kopieën. Die saaiheid is een strategisch voordeel. Git werd ontworpen om een project van de omvang en snelheid van de Linux-kernel te dragen. Een methode die veranderingen in zo'n omgeving kan coördineren, is niet te fragiel voor een institutionele website.
Wanneer mensen zeggen dat een website moet schalen, bedoelen ze meestal verkeer, omdat verkeer zich goed leent voor architectuurdiagrammen en aanbestedingstaal. Content as code schaalt ook in die klassieke betekenis: vooraf gebouwde pagina's kunnen via caches en content delivery networks worden verspreid zonder dat een publicatiedatabank de hartslag van de publieke website wordt. Maar de interessantere vormen van schaal zijn organisatorisch en temporeel. Kunnen honderd medewerkers bijdragen zonder elkaars werk te overschrijven? Kunnen vijf talen dezelfde structuur delen zonder vijf afzonderlijke websites te worden? Kan dezelfde content hergebruikt worden in een website, e-mail, gedrukte gids, zoekindex, dataset en assistent? Kan een nieuwe leverancier de presentatielaag herbouwen zonder eerst tien jaar databankconventies te reverse-engineeren? Kan het materiaal nog geopend worden nadat het huidige framework, de leverancier en de domeinstrategie verdwenen zijn? Een systeem dat tien miljoen pageviews aankan maar een migratie van zes maanden nodig heeft om van platform te veranderen, is alleen in de minst interessante richting geschaald.
Hier wordt ook de AI-vraag concreet in plaats van modieus. Organisaties haasten zich om hun informatie leesbaar te maken voor assistenten, terwijl ze de canonieke versie blijven opslaan in systemen die geoptimaliseerd zijn voor visuele paginaopbouw. Vervolgens scrapen ze hun eigen gerenderde HTML, verwijderen navigatie, cookiebanners, gedupliceerde componenten en layoutrommel, proberen koppen opnieuw af te leiden, reconstrueren metadata en noemen het resultaat een AI-ready knowledge base. Het is een absurde lus. Markdown en andere schone tekstformaten scheiden betekenis en presentatie al van elkaar. Koppen, lijsten, links, citaten, code, metadata en relaties zijn expliciet. Dezelfde bron kan voor een mens worden gerenderd, voor zoekfunctionaliteit worden geïndexeerd, voor retrieval worden opgeknipt of door een assistent worden gelezen zonder eerst te moeten doen alsof een webpagina een screenshot is. AI is niet de reden om content as code te gebruiken. AI maakt alleen zichtbaar hoe verspild onze bestaande contentarchitectuur vaak is.
Voor Europese instellingen en overheden is het argument bovendien politiek. De Open Source Strategy van de Europese Unie plaatst open source in het hart van technologische soevereiniteit en koppelt het expliciet aan minder afhankelijkheid van niet-Europese propriëtaire technologie, meer controle, interoperabiliteit, herbruikbare publieke digitale bouwstenen en overheden die zelf als gebruiker en bijdrager optreden. Een overheid die publieke kennis in open tekstformaten bewaart en met open standaarden voor versiebeheer beheert, behoudt keuzes over hosting, leveranciers, jurisdictie en toekomstig hergebruik. GitHub is niet Git. Een organisatie kan GitHub gebruiken, GitLab, Forgejo, een andere aanbieder of een eigen server. De repository kan verhuizen. De bestanden hebben geen toestemming van een buitenlands platform nodig om bestanden te blijven.
Er is ook een cultureel voordeel dat moeilijker te meten is en belangrijker dan nog een vergelijking van features. Content as code verkleint de kunstmatige afstand tussen "business", content en technologie. Redacteurs leren dat een pagina een bron, metadata, afhankelijkheden en een publicatiegeschiedenis heeft. Ontwikkelaars ontmoeten content als een volwaardig onderdeel van het systeem in plaats van als vulmateriaal dat na de component library wordt ingevoerd. Analisten kunnen dezelfde bron inspecteren zonder een speciale export te moeten aanvragen. Ontwerpers zien welke varianten echt bestaan en welke ooit geïmproviseerd zijn in een rich-textveld. Frontend en backend voelen minder als twee planeten die via tickets met elkaar communiceren. Niet iedereen wordt ontwikkelaar, maar meer mensen begrijpen hoe het product werkelijk in elkaar zit. Systemen worden duurzaam wanneer kennis erover verspreid is; ze worden log wanneer elke gewone wijziging eerst een beroepsgrens moet oversteken.
Het sterkste bezwaar is reëel: Git kan onaangenaam zijn. De terminologie is historisch gegroeid, merge conflicts zijn intimiderend, foutmeldingen in de command line hebben soms het empathisch vermogen van een teleurgestelde wiskundige, en veel voorstanders van content as code leggen de workflow uit met de serene vanzelfsprekendheid van mensen die vergeten zijn hoe het woord "rebase" klinkt voor een normaal mens. Echt gelijktijdig samenwerken is vaak eenvoudiger in Google Docs of Microsoft 365. Grote mediabibliotheken, fijnmazige rechten, lokalisatieworkflows en embargo's vragen doordacht ontwerp. Een repository kan rommelig worden. Markdown kan misbruikt worden. Een slechte informatiearchitectuur blijft een slechte informatiearchitectuur, ook wanneer ze prachtig geversioneerd in bestanden staat. Geen van die problemen moet weggewuifd worden. Elke sporadische redacteur verplichten om in een terminal te werken en de frustratie vervolgens "empowerment" noemen, zou dezelfde fout van het klassieke CMS herhalen, alleen met een donkerder kleurenschema.
Maar dat bezwaar pleit voor betere redactionele interfaces, niet voor gesloten opslag. De grote ontwerpkans zit net in het loskoppelen van de bewerkingservaring van het canonieke formaat. Geef redacteurs een Obsidian-achtige toepassing, een browsereditor, formulieren voor metadata, een previewknop, opmerkingen, rechten en desnoods een grote geruststellende knop "Publiceren". Verberg commando's waar commando's geen waarde toevoegen. Hou de bron zichtbaar waar die zichtbaarheid iets leert. Noem een merge request desnoods gewoon een review. Laat teams in vertrouwde tools samen schrijven wanneer gelijktijdigheid echt belangrijk is en breng goedgekeurde content daarna naar de repository. Het niet-onderhandelbare punt is dat de GUI moet schrijven naar open, inspecteerbare bestanden en een standaardgeschiedenis moet bewaren. Gemak moet vervangbaar zijn. De content niet.
De overstap vraagt geen grondwetswijziging. Begin met een duurzaam deel van een site: richtlijnen, documentatie, beleidsinformatie, een kennisbank, projectpagina's of langlopende campagnecontent. Bewaar elke pagina in Markdown met een kleine, strikte set metadata. Zet de repository in Git. Voeg automatische controles toe voor links, koppen, metadata en reviewdata. Genereer voor elke voorgestelde wijziging een preview. Leer redacteurs alleen de paar Git-concepten die ze echt nodig hebben. Hou transactionele toepassingen waar transacties thuishoren, maar maak de verklarende content linkbaar, testbaar en onafhankelijk. Na de eerste redesign wordt het voordeel duidelijk: de frontend kan vervangen worden terwijl de content blijft. Na de eerste bulkcorrectie wordt het bijna gênant: een script doet in minuten wat het oude systeem veranderde in weken van gecoördineerd klikken.
Jaren nadat die eerste redacteur die datum aanpaste, zal de organisatie de website opnieuw ontwerpen. Het huidige CMS zal intussen legacy zijn geworden, zoals actuele platformen dat nu eenmaal betrouwbaar doen. Het designsysteem zal gedateerd ogen. Een leverancier zal een migratieanalyse voorstellen. In de content-as-codeversie van die toekomst opent iemand de repository, leest de bestanden, past de templates aan en bouwt de nieuwe site. De oude interface kan verdwijnen zonder het geheugen van de organisatie mee te nemen. Ergens in de bron is die datum nog altijd twaalf tekens lang. Ze is nog altijd leesbaar. Ze wacht nog altijd om aangepast te worden.