103 lines
5.1 KiB
Markdown
103 lines
5.1 KiB
Markdown
# 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.
|