In december 2019 vroeg ik of iemand ervoor wilde zorgen dat er AppImage-pakketten werden aangeboden voor Darktable. Het voor de hand liggende voordeel zou zijn geweest dat vroeg testen mogelijk werd, vóór de uitgave, door mensen die de broncode niet zelf kunnen bouwen, om zo hopelijk vroege feedback te geven en te helpen met debuggen voordat er werd uitgegeven. Dit is nooit een prioriteit geweest, wat betekende dat het prima was om zowel vóór de uitgave als na de uitgave in een stormloop bugs te moeten fixen.
Ik maakte geen grapje toen ik zei dat Darktable een burn-outfabriek was, gemanaged als de allerslechtste start-ups, behalve dat niemand een exit zal maken en het in feite een puur verlies is voor iedereen die erbij betrokken is. Ik ben er des te bozer over omdat er heel veel manuren zijn verspild aan het volproppen van de UI met cosmetica, terwijl nightly builds voor algemeen gebruik zowel de kwaliteit van de software zouden hebben verbeterd als het comfort en de levenskwaliteit van de beheerders. Die doelen deden er blijkbaar niet toe.
Dus, aangezien je nooit beter bediend wordt dan door je eigen bugs, moest ik het zelf doen , en half december 2022 kreeg Ansel scripts voor AppImage nightly builds die Darktable een maand later hergebruikte. Met de horden IT’ers „zonder pixelwiskundevaardigheden" die rond Darktable’s hype rondzwierven en vroegen hoe ze konden helpen, was het natuurlijk het best mogelijke resourcemanagement om manuren van een van de zeer weinige beschikbare wiskundevaardige jongens af te leiden naar een puur IT-taak. Dat kostte me alleen maar zo’n 50 uur, want een lokaal werkend script hebben is echt niet hetzelfde als YAML-scripts in het oor van Github Action fluisteren, en ik moest het leren terwijl ik het deed. Aangezien Ansel 20 tot 30 minuten nodig heeft om te bouwen (of te falen) op Github Action-instanties, is je werkdag een lange, haperende sessie van inefficiënt multitasken, terwijl je wacht tot je scriptwijzigingen een resultaat op afstand opleveren. Ik haat programmeren, en ik red me met wiskunde en natuurkunde, maar dat soort ondergeschikte serverconfiguratie is echt het allerslechtste soort programmeren dat er is, in die zin dat het noch creatief noch cognitief uitdagend is, het is alleen een tijdrovende manier om de ontwerpbeperkingen van API’s van derden te omzeilen.
Dus, uiteindelijk was Ansel een semi-georganiseerd softwareproject, in staat om een gebruikersbasis op te nemen die groter was dan alleen de fanatieke geeks die neerkijken op iedereen die GCC en CMake niet aan het bouwen krijgt, met een grote „download"-knop op de voorpagina die altijd naar de nieuwste build zou wijzen, zonder dat gebruikers hoefden uit te zoeken welke de nieuwste was. Want een fotobewerkingsprogramma zou gebruikers niet mogen uitsluiten op basis van hun computervaardigheid, of dat nu uit de Linux-wereld komt (wat echt geen excuus is), of er is een memo van de Free Software Foundation die ik gemist heb.
Maar het laatste grote probleem was het ontbreken van een ontwikkelaarsdocumentatie. In 2018, toen ik begon bij te dragen aan Darktable, had ik het erg moeilijk om te begrijpen hoe de interne werking in elkaar zat, alleen door de code te lezen. Zeven jaar later moet ik, zelfs met al mijn ervaring, mij nog steeds al reverse-engineerend een weg door de code banen, met evenveel grep als intuïtie, omdat de code niet modulair is, weinig plekken API’s gebruiken, commentaar ontbreekt, en regel-voor-regel Git blame het niet altijd mogelijk maakt om terug te gaan in de geschiedenis van ontwerpwijzigingen wanneer iemand (nutteloze) wijzigingen in de code-opmaak heeft gecommit.
Dat gebrek aan ontwikkelaarsdocumentatie leidde ertoe dat veel functies meer dan eens werden geïmplementeerd, op verschillende plekken, door verschillende ontwikkelaars (en soms zelfs door dezelfde, in de loop der jaren). Op het moment van schrijven zijn er in Ansel nog steeds 4 of 5 verschillende manieren om een afbeeldingsgeschiedenis vanuit de bibliotheekdatabase naar een XMP-sidecarbestand te schrijven. Sommige van deze manieren worden zelden gebruikt en dus nauwelijks getest, en bugs daar zouden jarenlang onopgemerkt kunnen blijven, totdat iemand de superbizarre bug meldt die verstopt zit in het woud aan opties. Dan is het, voor de verantwoordelijke beheerder, een dwaas spelletje om uit te zoeken waarom XMP alleen in sommige omstandigheden faalt, wat archeologie in de codebase vereist om erachter te komen dat het de hoofdmethode voor het schrijven van XMP niet gebruikt. XMP is een specifiek, en nog altijd actueel, voorbeeld, maar er waren er vele andere. Je snapt het idee.
Ik had al enkele jaren opgemerkt dat de (oude) C-headerbestanden van Darktable vrijwel allemaal Doxygen-docstrings hadden. Dit is een zeer luie manier om documentatie te maken: voer het commando doxygen -g <config-file> uit op je broncodemap, en Doxygen bouwt een statische HTML-website van alle API’s voor je. Vervolgens kun je alle HTML-bestanden in een map van een webserver dumpen, en dat je dev docs noemen : het enige vervelende is het schrijven van het configuratiebestand, wat je maar één keer doet. Dus dat deed ik: dev.ansel.photos. API’s die ik heb herschreven (zoals selection.h) zijn gedocumenteerd naarmate ik vorderde. Beschrijvingen op hoger niveau van de softwarearchitectuur zijn in aantocht.
Het geweldige aan Doxygen is dat het ook afhankelijkheidsgrafieken produceert van „modules" en API’s. En dat is waar je daadwerkelijk kunt zien waarom ik Darktable’s code spaghetti noem. Dit is de afhankelijkheidsgrafiek van accelerators.h, de backend voor toetsenbord- en MIDI-sneltoetsen:

Dat laat je zien dat de accelerators-backend absoluut niet modulair is: hij erft de hele software. Dus elke wijziging elders kan onvoorziene effecten daar hebben, en andersom. Wat des te zorgwekkender is, aangezien dit bestand de hoogste cyclomatische complexiteit van de hele software heeft, wat het het meest uitdagende bestand maakt om te onderhouden (ik durf niet eens uitbreiden te zeggen, op dat punt zou het onverantwoord zijn). Maar het is ook volledig verwrongen wat betreft de richting van includes : sneltoetsen zijn een basisblok dat opgenomen (oftewel geërfd) zou moeten worden in de GUI-plekken die sneltoetsen implementeren (donkere kamer-/lichttafelweergaven, schuifregelaars en keuzelijsten, modules). In plaats daarvan tonen de grafiek (en de #include-header van het bestand) dat sneltoetsen ook hun „kinderen" opnemen (erven), dus we hebben een dubbele afhankelijkheid en dat is de slechtst mogelijke manier om het te doen.
Het is als het bouwen van een huis : het huis zou zich bewust moeten zijn van zijn muren, de muren zouden zich bewust moeten zijn van hun bakstenen. Waarom ? Omdat het huis gemaakt is van muren, en de muren gemaakt zijn van bakstenen, dus elke subcomponent bepaalt de aard en het gedrag van het geheel, en daarom moet het geheel zijn onmiddellijke componenten kennen. Je maakt bakstenen niet bewust van het huis, want ze zullen hun aard niet veranderen afhankelijk van het geheel waartoe ze behoren, en dat zou een verschrikkelijk gebrekkig ontwerp zijn. Net zoals het een onnodig niveau van micromanagement is om het huis bewust te maken van de bakstenen: zodra het zich bewust is van zijn muren, is het aan de muren om rekening te houden met het gedrag van hun bakstenen, en misschien relevante informatie door te geven aan het huis. Objectgeoriënteerde talen hebben ingebouwde (en verplichte) manieren om dat allemaal netjes af te handelen. Maar het is C, dus je kunt doen wat je maar wilt. Betekent niet dat het een goed idee is, betekent niet dat je het zou moeten doen. En, tja, het feit dat C geen intrinsiek objectgeoriënteerde taal is, vormt geen obstakel voor het gebruik van objectpatronen, overerving en modulariteit. Het is alleen zo dat de ontwikkelaar geen hulp krijgt van de taalsyntaxis om dat te doen.
Dit is hetzelfde bestand na mijn volledige herschrijving van de sneltoetsen-backend:

Dat laat duidelijk zien dat de nieuwe sneltoetsen-handler een dunne wrapper is rond de native sneltoetsen van Gtk, hij kent de rest van de software niet en dat interesseert hem niet. We kunnen ook naar het bestand accelerators.c kijken:

Nu zijn er daar veel includes van darktable.h, dat alleen wordt gebruikt om de debug-helpers te krijgen (die zouden weggerefactored moeten worden uit dat bestand), en die ook een hoop onnodige rommel bevat. Hoe dan ook, accelerators.c is zich alleen bewust van Gtk/Gdk, wat betekent dat het een echte module is: het is volledig geïsoleerd van de rest van de software. GUI-widgets die sneltoetsen implementeren zullen hun sneltoetspad declareren, zoals Ansel/Global/Menu/File/Import, hun standaardtoetsen, en een verwijzing (pointer) naar zichzelf. Widgetloze acties die sneltoetsen hebben, zullen een callback-functie en invoergegevens aan de sneltoetsen-handler declareren, in plaats van een pointer naar een widget.
We slaan de paden- en toetsenassociaties op/herstellen ze naar/uit het keyboardrc-bestand, en dat is het. Er is op dit moment geen venster om sneltoetsen in de GUI te definiëren, maar er een maken hoeft alleen maar bekende paden en hun bijbehorende toetsen op te sommen (er overheen te loopen). Wanneer de sneltoetsen-handler een bekende toetsencombinatie opvangt:
- voor de aan een widget gekoppelde acties zal het een
activateGtk-signaal naar de relevante widget sturen, en die widget zal zijn eigen ding doen via een callback (die dezelfde is als de callback die klikken afhandelt, dus de code is uniform tussen klikken en toetsenbordactivering), - voor de widgetloze acties zal het rechtstreeks de gedeclareerde callback-functie aanroepen over de gedeclareerde gegevens.
Op beide manieren communiceert de accelerators-module met de rest van de applicatie via een interface die 4 datavelden uitwisselt, op een volledig ondoorzichtige manier. Zolang de interface niet verandert, kunnen er overal wijzigingen worden gemaakt: ze blijven binnen hun module besloten. Met de juiste vaardigheden had deze logica uitgebreid kunnen worden naar ondersteuning voor MIDI-apparaten. Maar dat is het verschil tussen engineering en het prototypen van proofs of concept die nooit in productie zouden mogen belanden.
Dit is helaas een handelsmerk van Darktable’s manier van werken: GUI-code zit overal ingebakken, zelfs in SQL-code. Andersom wordt SQL-code op veel GUI-plekken aangetroffen (lichttafel, geschiedenisbeheer, afbeeldingen taggen, enz.). Afhankelijkheidsgrafieken, ingebouwd in Doxygen, zijn een heel mooi neveneffect dat de diepte van het probleem toont en de spaghetti heel duidelijk maakt, want blijkbaar bestaan problemen niet totdat je ze daadwerkelijk zelf ziet.
Maar het houdt daar niet op. Terwijl ik mijn eigen Doxygen-configuratiebestand aan het schrijven was, kreeg ik een fout: Doxygen meldde 2 configuratiebestanden en was in de war. Het blijkt dat Darktable alles klaar had staan voor geautomatiseerde generatie van ontwikkelaarsdocumentatie sinds 2010 . Waarom het nooit in productie is genomen, en daadwerkelijk op een of andere server is gehost, is niet louter verkeerde prioriteiten: het is nalatigheid.
Het helpt natuurlijk niet dat de man die het darktable.org-domein bezit niet dezelfde is als degene die de server beheert waar het daadwerkelijk wordt gehost. En de man die daadwerkelijk commit-rechten heeft over de darktable.org-website is weer een ander. Het meest dringende wat je moet doen, wanneer je een opensource-project bent dat nul dollar inkomsten genereert, is om alle fouten van de bedrijfswereld te reproduceren, van de druk om halfbakken rotzooi uit te geven met een onverantwoorde frequentie, tot het versnipperen van verantwoordelijkheden over „diensten" die niet echt met elkaar communiceren (of met grote vertragingen). Want in software (of het nu opensource is of niet) zijn fouten bedoeld om gereproduceerd te worden. En aangezien software de wereld heeft overgenomen, zelfs op plekken waar het niet nodig was, zegt dat veel over de wereld waarin we leven.
Het enige wat ik echt nog steeds niet snap, is: waarom de urgentie ? Waarom zo slecht blijven werken terwijl er geen druk noch financiële prikkel is om dat te doen ? Opensource is (had kunnen zijn) de ene plek waar we daadwerkelijk fatsoenlijk zouden kunnen werken en de nodige tijd zouden kunnen nemen om kwaliteit op de lange termijn te produceren. En zelfs daar heeft de kortzichtigheid van het kapitalisme de overhand gekregen.
Als gebruikers eens wisten in wat voor diepe ellende dit project is beland en hoe alle erin geïnvesteerde manuren het actief slechter maken…
Note
De ontwikkelaarsdocumentatie wordt nu elke zondag om middernacht automatisch gegenereerd en geüpload naar dev.ansel.photos, vanuit de commentaren (docstrings) in de code.Translated from English by : Claude. In case of conflict, inconsistency or error, the English version shall prevail.