Vraag Hoe beheer ik effectief een Clojure-codebasis?


Een collega en ik zijn Clojure-nieuwkomers. We zijn een paar maanden geleden begonnen met een project, maar ontdekten al snel dat we het moeilijk hadden met onze codebasis. Met 500 LOC hadden we eigenlijk geen idee waar te beginnen met het debuggen, toen het misging (wat vaak het geval was). In plaats van paren kregen functies lijsten, of nummers, of wat-is-jou.

Nu starten we een nieuw maar gerelateerd project en migreren veel van de oude code. Maar we raken opnieuw tegen een muur.

We vragen ons af hoe we een Clojure-project effectief kunnen beheren, vooral als we de bestaande code wijzigen?

Wat we hebben bedacht:

  • liberaal gebruik van eenheidstests
  • liberaal gebruik van pre-, post-voorwaarden
  • informele typeaangiften in functiecommentaren
  • gebruik defrecord / defstruct / defprotocol om een ​​gegevensmodel te implementeren, wat het testen echt zou vereenvoudigen

Maar post-, pre-omstandigheden lijken niet vaak te worden gebruikt. Unit-testen + opmerkingen zullen slechts zoveel helpen. En het lijkt erop dat Clojure-programmeurs geen formele datamodellen implementeren.

Krijgen we Clojure niet?  Hoe weten Clojure-programmeurs dat hun code robuust en correct is?


21
2017-10-18 19:35


oorsprong


antwoorden:


Ik denk dat dit eigenlijk een evoluerend gebied is - Clojure is niet echt lang genoeg geweest om alle best practices en bijbehorende tools voor het beheren van een grote codebasis nog te ontwikkelen.

Enkele suggesties uit mijn ervaring:

  • Structureer uw code op een "bottom-up" manier - over het algemeen heeft de manier waarop u uw code wilt structureren de "utility" -code bovenaan het bestand (of geïmporteerd vanuit een andere naamruimte) en de "bedrijfslogica" -code die deze hulpprogramma-functies gebruikt aan het einde van het bestand . Als dit moeilijk lijkt te zijn, dan is het waarschijnlijk een hint dat je code wat refactoring nodig heeft.

  • Tests als voorbeelden - Testcode in clojure werkt heel goed, zowel om uw code te controleren, maar ook als documentatie (bijv. "Wat voor soort parameter verwacht deze functie?"). Als u een fout hebt gemaakt, raadpleegt u uw tests om uw aannames te controleren en een paar nieuwe tests te schrijven om uit te wissen wat er mis gaat.

  • Houd functies eenvoudig en stel ze samen - Een soort uitbreiding van de "principe van enkele verantwoordelijkheid"naar functioneel programmeren. Ik beschouw meer dan 5-10 regels in een Clojure-functie als een belangrijke codegeur (als dit extreem lijkt, bedenk dan dat je waarschijnlijk net zoveel kunt bereiken in 5-10 regels clojure als je zou kunnen met 50- 100 regels Java / C #)

  • Pas op voor "imperatieve gewoonten" - toen ik Clojure voor het eerst ging gebruiken, schreef ik een heleboel pseudo-imperatieve code in Clojure. Een voorbeeld zou een for-lus emuleren met "dotimes" en wat resultaat verzamelen binnen een atoom. Dit kan pijnlijk zijn - het is niet idiomatisch, het is verwarrend en meestal is er een veel slimmere, eenvoudigere en minder foutgevoelige, functionele manier om het te doen. Dit vergt oefening, maar het is de moeite waard op de lange termijn ...

  • Debug op de REPL - meestal wanneer ik een probleem heb, is het coderen op de REPL de gemakkelijkste manier om het uit te spoelen. Over het algemeen betekent dit dat u enkele specifieke delen van het grotere algoritme uitvoert om aannames, enz. Te controleren.

  • Gemeenschappelijke functie van Refactor-functies - waarschijnlijk vindt u een aantal veel voorkomende elementen of structuren die in veel functies worden herhaald. Het is de moeite waard om dit in een functie of macro te plaatsen die u op andere plaatsen of projecten kunt hergebruiken - op die manier kunt u het veel rigoureuzer testen en de voordelen op meerdere plaatsen hebben. Bonuspunten als je het helemaal stroomopwaarts kunt krijgen in Clojure zelf! Als u dit goed genoeg doet, zal uw hoofdcodebasis bijzonder beknopt zijn en daarom gemakkelijk te beheren, met alleen de echt domeinspecifieke code.


9
2017-10-19 03:10



eenvoudige samenstelbare abstracties

"Het is beter om 100 functies op één datastructuur te laten werken dan om 10 functies op 10 datastructuren te laten werken." - Alan J. Perlis

Voor mij is het alles over het samenstellen van eenvoudige functies. Probeer elke functie te onderbreken in de kleinste eenheden die u kunt en vervolgens een andere functie te hebben die ze samenstelt om het werk te doen dat u nodig hebt. Je weet dat je in goede vorm bent, elke functie kan onafhankelijk worden getest. Als je te zwaar op de macro's gaat, kan het deze stap moeilijker maken omdat macro's anders zijn samengesteld.

D.R.Y Serieus, herhaal jezelf niet

beginnend met goed afgebroken functies in een reeks naamruimten; elke keer als ik ergens anders een van de samengestelde delen nodig heb, "hijst" ik die functie op tot een bibliotheek die is opgenomen door beide naamruimten. Op deze manier evolueren je veel gebruikte abstracties in de loop van het project naar 'net genoeg kader'. Het is heel moeilijk om dit te doen tenzij je echt discrete, samenstelbare abstracties hebt.


9
2017-10-18 20:12



Sorry om deze oude vraag op te lossen, de antwoorden van mikera en Arthur zijn uitstekend, maar het is iets waar ik me ook over heb afgevraagd terwijl ik Clojure heb leren kennen, en dacht dat ik zou vermelden hoe we bestanden ordenen.

Op dezelfde manier als ervoor zorgen dat elke functie één taak heeft, groeperen we gerelateerde functies in naamruimten om het navigeren door de code gemakkelijker te maken. We kunnen dus een naamruimte hebben voor functies die toegang bieden tot een bepaalde database of een verzameling HTTP-gerelateerde hulpprogramma's bieden. Hierdoor blijft elk bestand relatief klein en worden tests eenvoudiger te vinden. Het maakt ook refactoring veel eenvoudiger. Dit is nauwelijks iets nieuws, maar het is de moeite waard om in gedachten te houden.


1
2018-02-19 12:30