# Regels bij het coderen Deze regels gelden voor nieuwe code en voor code die inhoudelijk wordt gewijzigd. Bestaande code hoeft niet mechanisch te worden herschreven zonder functionele reden. ## Algemeen ontwerp 1. Los het probleem op in de laag die eigenaar is van de invariant. 2. Houd de oplossing klein en samenhangend; bouw geen framework voor één geval. 3. Gebruik statische `require`-afhankelijkheden. Vermijd `dynamic-require` als middel om moduleontwerp uit te stellen. 4. Geef configuratie en betekenisvolle dependencies expliciet door. 5. Bewaar beveiliging en dataintegriteit server-side. 6. Maak een databasebewerking atomair wanneer haar afgeleide gegevens samen met de bron moeten veranderen. 7. Behoud backward compatibility van database, CMap-document en API tenzij een migratie expliciet is ontworpen. ## Racket-stijl Kies expliciete, goed leesbare constructies boven compacte combinaties van kleine idiomen. Gebruik benoemde tussenwaarden wanneer zij de reden van een stap duidelijk maken. Gebruik het Unicode-symbool `λ` voor anonieme procedures. Schrijf `cond`-clausules met blokhaken: ```racket (cond [(not page) #f] [(archived? page) (archive-result page)] [else (current-result page)]) ``` Vermijd constructies waarin `and`, falsy waarden en filtering tegelijk de datastructuur vormen. Schrijf de bedoelde keuze expliciet: ```racket (filter (λ (value) value) (list (if include-title? 'title #f) (if include-tags? 'tags #f))) ``` Gebruik mutatie wanneer die de toestand werkelijk modelleert en lokaal blijft. Verberg een eenvoudige `hash-set!` of `set!` niet achter een abstractie die geen domeinbetekenis toevoegt. ## Procedures en contractcommentaar Een publieke of niet-triviale procedure beschrijft waar nuttig: ```text goal : Waarom bestaat deze procedure? pre : Welke invoer en toestand worden verondersteld? post : Welke toestand is na succes veranderd? result : Welke waarde of fout krijgt de aanroeper? ``` Commentaar verklaart beslissingen, grenzen en afwijkend gedrag. Het herhaalt niet regel voor regel de code. Gebruik betekenisvolle namen zoals `valid-page-reference?`, `read-concept-map` en `replace-current-attachment-references!`. Een predicaat eindigt op `?`; een procedure die duurzame toestand wijzigt meestal op `!`. ## Module-interfaces Exporteer alleen wat een andere module gebruikt of als publieke API bedoeld is. Een opslagmodule accepteert domeinwaarden en `config`, niet een HTTP-request. Een HTTP-handler bouwt geen SQL. Maak een helper wanneer: - dezelfde invariant op meerdere plaatsen identiek moet blijven; - de naam domeinbetekenis toevoegt; - een pure, testbare grens ontstaat; - foutafhandeling daardoor op de juiste laag komt. Maak geen helper die slechts één aanroep doorgeeft zonder extra contract. ## SQL en transacties Gebruik queryparameters voor alle waarden. Dynamisch samengestelde SQL is alleen toegestaan voor vaste, door de code gekozen fragmenten zoals een bekende kolomlijst. Lees bij een optimistic-lock-write de rij met `FOR UPDATE`, controleer de basisversie en schrijf actuele toestand, historie en afgeleide indices binnen dezelfde transactie. Zet een normale domeinfout om in een herkenbare fout zoals `version-conflict`; verberg onverwachte databasefouten niet. Een migratie krijgt een nieuw oplopend nummer. Wijzig een reeds uitgebrachte migratie niet alsof installaties haar nog niet hebben uitgevoerd. ## JavaScript en DOM Gebruik de centrale `api()`-functie voor JSON-calls, zodat CSRF en uniforme foutafhandeling behouden blijven. Routeer via de centrale hashnavigatie; wijzig niet alleen de zichtbare view terwijl de URL achterblijft. Alle HTML uit Markdown gaat door DOMPurify. Gebruik `textContent` voor gewone tekst en bouw elementen expliciet. Als `innerHTML` noodzakelijk is, moet de bron en sanering in dezelfde functie zichtbaar zijn. Een nieuwe DOM-id wordt tegelijk in `index.html` en de bijbehorende code toegevoegd. Een nieuwe vertaalkey krijgt ten minste Engelse en Nederlandse standaardtekst. CMap-bewerkingen gebruiken de editor-API en eindigen als één herkenbare Undo-transactie. Verander niet rechtstreeks een grafisch element zonder het bijbehorende editorrecord en de serialisatie bij te werken. ## Fouten en logging Vang alleen uitzonderingen die op de huidige laag betekenisvol kunnen worden vertaald. Geef bij een technische fout component en veilige identifier mee. Log geen wachtwoord, databasecredential, sessietoken, CSRF-token of herstelcode. Browsermeldingen zijn begrijpelijk voor de gebruiker. Technische details mogen aanvullend in console of serverlog staan, maar niet in plaats van een concrete uitleg. ## Versie en release Bij iedere release: 1. verhoog `info.rkt`; 2. werk zichtbare frontenddiagnostiek en componentversies bij; 3. voeg een README-changelogitem toe; 4. pas relevante architectuurpagina's aan; 5. compileer Racket en draai tests; 6. controleer JavaScript, DOM-id's en vertaalkeys; 7. inspecteer het ZIP-archief op volledigheid en geheimen. Zie [Onderhoudbaarheid](racket-wiki:onderhoudbaarheid) voor de werkwijze en [Modulariteit](racket-wiki:modulariteit) voor de gewenste afhankelijkheidsrichting.