Wat gebeurt er wanneer een bende amateurfotografen, die amateurontwikkelaars zijn geworden, vergezeld door een stel back-endontwikkelaars die bibliotheken voor ontwikkelaars ontwikkelen, besluit om zonder methode of structuur te werken aan industriële software voor eindgebruikers, waarvan de kerncompetentie (colorimetrie en psychofysica) zich ergens tussen een bacheloropleiding fotografie en een masteropleiding toegepaste wetenschappen bevindt, terwijl ze beloven om elk jaar 2 versies te leveren zonder projectmanagement? Dat alles, natuurlijk, in een project waar de oprichters en de eerste generatie ontwikkelaars verder zijn getrokken en gevlucht?
Raad eens!
Basisfuncties die achteruitgaan
De jaren 2020 zijn 40 jaar te laat om de interactieparadigma’s tussen gebruiker en computer opnieuw uit te vinden, of het nu gaat om hoe we een toetsenbord en een muis gebruiken om de interface aan te sturen, of om het gedrag van een bestandsverkenner. Sinds de jaren 1980 zijn alle computerapparaten voor het brede publiek geconvergeerd naar min of meer uniforme semantiek, waar de escape-toets de huidige toepassing sluit, dubbelklikken bestanden opent en het muiswiel de huidige weergave scrollt. Darktable1 schept er een misplaatst genoegen in om dat alles te negeren en de recente wijzigingen maken het erger: het is nu verplicht om de documentatie te lezen om taken uit te voeren die zo eenvoudig zijn als het sorteren van bestanden of het toewijzen van sneltoetsen aan GUI-acties.
Modulegroepen
Alles begint met de herziening van de modulegroepen , in 2020, die de beslissing verbergt om niet te beslissen over een uniforme modulevolgorde.

Sinds 2018 vecht ik om de grafische interface van Darktable op te schonen, en in het bijzonder de module-organisatie. Een grafische interface zou goede praktijken moeten bevorderen door de gereedschappen te ordenen in de typische volgorde waarin ze zouden moeten worden gebruikt. Slechte praktijken zijn die welke het risico op colorimetrische inconsistenties of op circulair bewerken vergroten, waarbij men moet teruggaan om later aangebrachte wijzigingen door te rekenen, ook al kunnen slechte praktijken in eenvoudige gevallen werken. In de context van beeldverwerking, een sterk technische taak waarbij veel dingen voor de eindgebruikers verborgen zijn onder de GUI, stellen goede praktijken ook slecht gekwalificeerde personen in staat om de software te gebruiken op een manier die de kans op fouten verkleint.
Deze gebruiksvolgorde wordt grotendeels bepaald door technische overwegingen zoals de volgorde van toepassing van modules in de pipelinesequentie en het gebruik van getekende en parametrische maskers, waarvan het effect afhangt van stroomopwaartse modules. Deze overwegingen negeren staat gelijk aan om problemen vragen, ook al is het de hete trend in de jaren 2010 en 2020 om te geloven dat digitale technologieën losgekoppeld van enige materiële realiteit werken, uitsluitend voor het geluk van de gebruiker.
De beperking die bijvoorbeeld wordt opgelegd aan het pipelineontwerp om een willekeurige volgorde van modulegebruik toe te staan, creëert wiskundig onoplosbare problemen met betrekking tot de berekening van de coördinaten van maskerknopen, en geen enkele geprogrammeerde oplossing is mogelijk (afgezien van het versoepelen van die beperking) omdat de wiskunde nee zei.
Behalve dat een aanzienlijk deel van de programmeur-gebruikers rond het project op Github en op de dev-mailinglijst overtuigd blijft dat er geen goede of slechte workflow bestaat, alleen persoonlijke voorkeuren, wat waarschijnlijk waar is wanneer je een kunst beoefent zonder tijdsdruk, budgetdruk of resultaatdruk. Als zodanig zouden modules naar believen herschikbaar moeten kunnen zijn, of het nu in de pipeline of in de workflow is. De verwarring komt voort uit het feit dat niet-destructief bewerken ten onrechte als asynchroon wordt gezien (wat bijna het geval zou zijn als we geen maskers of mengmodi zouden gebruiken), terwijl de pixelpipeline sequentieel is en dichter bij een laaglogica staat, zoals we die aantreffen in Adobe Photoshop, Gimp, Krita, enz.
De modulevolgorde in de GUI ontkoppelen van de pipelinevolgorde staat gelijk aan het mogelijk maken van elk gebruik, zelfs pathologisch gebruik, en dwingt tot het schrijven van pagina’s en pagina’s documentatie om te waarschuwen, uit te leggen wat te doen, hoe en waarom; documentatie die niemand zal lezen om vervolgens elke week op elk forum in een lus dezelfde vragen te stellen.
In dit verhaal verliest iedereen zijn tijd dankzij een interfaceontwerp dat zo flexibel probeert te zijn dat het standaard niet veilig en robuust kan worden gemaakt. In het menselijk lichaam heeft elk gewricht enkele vrijheidsgraden langs bepaalde assen; als elk gewricht 340° rond elke as van de 3D-ruimte kon draaien, zou de structuur instabiel zijn omdat ze te flexibel is, en niet in staat om met hoge belastingen te werken. De metafoor gaat op in industriële software. We zwemmen in de FLOSS-cargocultus , waar mensen graag de illusie van keuze hebben, dat wil zeggen dat ze veel opties krijgen aangeboden waarvan de meeste onbruikbaar of gevaarlijk zijn, ten koste van eenvoud (KISS ), en waar de meerderheid van de gebruikers de implicaties van elke optie niet begrijpt (en niet de minste behoefte heeft om het te begrijpen).
Bij gebrek aan een consensus over de modulevolgorde in de interface werd eind 2020 een ingewikkeld, broos en zwaar gereedschap geïntroduceerd om elke gebruiker de indeling van modules in tabbladen te laten configureren. Het biedt veel nutteloze opties en slaat de huidige indeling op in de database, met gebruik van de vertaalde naam van modules, wat betekent dat het veranderen van de UI-taal je je presets doet verliezen. Volledig configureerbaar laat het de gebruiker beslissen hoe hij zichzelf schade toebrengt, zonder enige gids voor goede praktijken. Deze rommel is gecodeerd met 4000 regels die vrolijk SQL-verzoeken mengen midden in GTK-interfacecode , en presets worden gemaakt via redundante compilermacro’s , terwijl modules altijd al een binaire vlag hebben gehad, waarmee hun standaardgroep kan worden ingesteld… modulair.
Belangrijker nog, het vervangt een eenvoudige en efficiënte functie, beschikbaar tot Darktable 3.2:

Eén klik op de modulenaam schakelt hem in, een tweede voegt hem toe aan de kolom favorieten, een derde verbergt hem voor de interface. Alles maakt presets mogelijk en slaat de huidige indeling als eenvoudige tekst op in het darktablerc-bestand. Eenvoudig en robuust, gecodeerd over 688 regels leesbare en goed gestructureerde code, was de functie dus niet amusant genoeg voor de dilettante-ontwikkelaar van middelbare leeftijd, en het was dringend nodig om hem te vervangen door een labyrintisch systeem.
Sneltoetsen
In 2021 werd toegevoegd wat ik de grote MIDI-turducken noem. Het doel is om de interface van sneltoetsen (al uitgebreid in 2019 om „dynamische sneltoetsen" te ondersteunen, waarmee muis- en toetsenbordacties kunnen worden gecombineerd) uit te breiden, om MIDI-apparaten en… videospelcontrollers te ondersteunen.
Eind 2022, dat wil zeggen anderhalf jaar na deze functie, in de enquête die ik heb uitgevoerd , bezit minder dan 10% van de gebruikers een MIDI-apparaat, en slechts 2% gebruikt het met Darktable. Ter vergelijking met de 45% van de gebruikers die een grafisch tablet (Wacom-achtig) bezit, waarvan de ondersteuning in Darktable nog steeds zo gebrekkig is dat slechts 6% het gebruikt. Los van het slechte prioriteitsbeheer, wat ik hier niet tolereer zijn de randeffecten die door deze wijziging worden geïntroduceerd en de globale kosten die het had, om te beginnen met het feit dat het geen door de gebruiker gedefinieerde sneltoetsen importeert uit versies ouder dan 3.2, en het maakt het configureren van nieuwe sneltoetsen verschrikkelijk ingewikkeld.
Vóór de grote turducken kon slechts een beperkte lijst GUI-acties worden gekoppeld aan toetsenbord- of gemengde (toetsenbord + muis) sneltoetsen. Deze lijst werd handmatig samengesteld door ontwikkelaars. De grote MIDI-turducken maakt het mogelijk om elke GUI-actie aan sneltoetsen te koppelen, waarbij gebruikers een lijst van enkele duizenden configureerbare vermeldingen wordt gepresenteerd, waarin het moeilijk is om de enige 3 te vinden die je echt nodig hebt, en de tekstzoekmachine is te basaal om nuttig te zijn:

Merk het gebruik van „effecten" op, waarover de documentatie geen hulp is. Alleen door deductie (omdat de code ook niet becommentarieerd is ) kwam ik er uiteindelijk achter dat het emulaties zijn van typische desktopinteracties (muis en toetsenbord) bedoeld om te worden gebruikt met MIDI-apparaten en gamepadcontrollers (maar je moet me nog steeds uitleggen wat Ctrl-Toggle, Right-activate of Right-Toggle betekenen in termen van typische desktopinteractie).
Wat onaanvaardbaar is, is dat het gebruik van het numerieke toetsenblok door het ontwerp kapot is, met name om genummerde beoordelingen (sterren) toe te kennen aan miniaturen in de lichttafel. De toetsmodificaties (numlock en capslock) worden namelijk niet correct gedecodeerd door het ding, en cijfers worden verschillend behandeld naargelang ze worden ingevoerd via het typische „tekst"-toetsenbord of via het numerieke toetsenblok. Dus de 1 van het numerieke toetsenblok wordt gedecodeerd als Keypad End, ongeacht de staat van de numlock. Zo moest ik cijfersneltoetsen configureren op een Frans BÉPO-toetsenbord en de configuratie dupliceren voor het numerieke toetsenblok:

Je hoeft alleen maar te onthouden dat Shift+" en Kp End beide 1 betekenen en eraan te denken om alle sneltoetsen te dupliceren voor het numerieke toetsenblok en de rest van het toetsenbord. Kortom, we breken een basisverwachting van de gebruiker, en we sturen ontwerpcritici naar de hel. De regressie wordt op alle Darktable-forums vermeld, maar lijkt niemand te storen.
De oplossing van deze bug deze functie is in Ansel gemaakt en de toetsen van het numerieke toetsenblok worden rechtstreeks in de code aan standaardtoetsen toegewezen , voor een totaal van 100 regels code inclusief commentaar. Deze correctie doen was inderdaad heel moeilijk: ik las de Gtk-documentatie en nam hun voorbeeld regel voor regel over. 2 jaar wachten daarop…
De kers op de taart is dat we, nog een keer, 1306 regels heldere en gestructureerde code hebben vervangen door een monstruositeit van bijna 4400 regels , met pareltjes zoals:
- de
while-lus des doods (bron ):
1 gboolean applicable;
2 while((applicable =
3 (c->key_device == s->key_device && c->key == s->key && c->press >= (s->press & ~DT_SHORTCUT_LONG) &&
4 ((!c->move_device && !c->move) ||
5 (c->move_device == s->move_device && c->move == s->move)) &&
6 (!s->action || s->action->type != DT_ACTION_TYPE_FALLBACK ||
7 s->action->target == c->action->target))) &&
8 !g_sequence_iter_is_begin(*current) &&
9 (((c->button || c->click) && (c->button != s->button || c->click != s->click)) ||
10 (c->mods && c->mods != s->mods ) ||
11 (c->direction & ~s->direction ) ||
12 (c->element && s->element ) ||
13 (c->effect > 0 && s->effect > 0 ) ||
14 (c->instance && s->instance ) ||
15 (c->element && s->effect > 0 && def &&
16 def->elements[c->element].effects != def->elements[s->element].effects ) ))
17 {
18 *current = g_sequence_iter_prev(*current);
19 c = g_sequence_get(*current);
20 }- De
switchcasedieifbevat, genest op 2 niveaus (bron ):
1 switch(owner->type)
2 {
3 case DT_ACTION_TYPE_IOP:
4 vws = DT_VIEW_DARKROOM;
5 break;
6 case DT_ACTION_TYPE_VIEW:
7 {
8 dt_view_t *view = (dt_view_t *)owner;
9
10 vws = view->view(view);
11 }
12 break;
13 case DT_ACTION_TYPE_LIB:
14 {
15 dt_lib_module_t *lib = (dt_lib_module_t *)owner;
16
17 const gchar **views = lib->views(lib);
18 while(*views)
19 {
20 if (strcmp(*views, "lighttable") == 0)
21 vws |= DT_VIEW_LIGHTTABLE;
22 else if(strcmp(*views, "darkroom") == 0)
23 vws |= DT_VIEW_DARKROOM;
24 else if(strcmp(*views, "print") == 0)
25 vws |= DT_VIEW_PRINT;
26 else if(strcmp(*views, "slideshow") == 0)
27 vws |= DT_VIEW_SLIDESHOW;
28 else if(strcmp(*views, "map") == 0)
29 vws |= DT_VIEW_MAP;
30 else if(strcmp(*views, "tethering") == 0)
31 vws |= DT_VIEW_TETHERING;
32 else if(strcmp(*views, "*") == 0)
33 vws |= DT_VIEW_DARKROOM | DT_VIEW_LIGHTTABLE | DT_VIEW_TETHERING |
34 DT_VIEW_MAP | DT_VIEW_PRINT | DT_VIEW_SLIDESHOW;
35 views++;
36 }
37 }
38 break;
39 case DT_ACTION_TYPE_BLEND:
40 vws = DT_VIEW_DARKROOM;
41 break;
42 case DT_ACTION_TYPE_CATEGORY:
43 if(owner == &darktable.control->actions_fallbacks)
44 vws = 0;
45 else if(owner == &darktable.control->actions_lua)
46 vws = DT_VIEW_DARKROOM | DT_VIEW_LIGHTTABLE | DT_VIEW_TETHERING |
47 DT_VIEW_MAP | DT_VIEW_PRINT | DT_VIEW_SLIDESHOW;
48 else if(owner == &darktable.control->actions_thumb)
49 {
50 vws = DT_VIEW_DARKROOM | DT_VIEW_MAP | DT_VIEW_TETHERING | DT_VIEW_PRINT;
51 if(!strcmp(action->id,"rating") || !strcmp(action->id,"color label"))
52 vws |= DT_VIEW_LIGHTTABLE; // lighttable has copy/paste history shortcuts in separate lib
53 }
54 else
55 fprintf(stderr, "[find_views] views for category '%s' unknown\n", owner->id);
56 break;
57 case DT_ACTION_TYPE_GLOBAL:
58 vws = DT_VIEW_DARKROOM | DT_VIEW_LIGHTTABLE | DT_VIEW_TETHERING |
59 DT_VIEW_MAP | DT_VIEW_PRINT | DT_VIEW_SLIDESHOW;
60 break;
61 default:
62 break;
63 }- De geneste
switchcasevan de demon, met additieve clausules stiekem verborgen (bron ):
1case DT_ACTION_ELEMENT_ZOOM:
2 ;
3 switch(effect)
4 {
5 case DT_ACTION_EFFECT_POPUP:
6 dt_bauhaus_show_popup(widget);
7 break;
8 case DT_ACTION_EFFECT_RESET:
9 move_size = 0;
10 case DT_ACTION_EFFECT_DOWN:
11 move_size *= -1;
12 case DT_ACTION_EFFECT_UP:
13 _slider_zoom_range(bhw, move_size);
14 break;
15 case DT_ACTION_EFFECT_TOP:
16 case DT_ACTION_EFFECT_BOTTOM:
17 if((effect == DT_ACTION_EFFECT_TOP) ^ (d->factor < 0))
18 d->max = d->hard_max;
19 else
20 d->min = d->hard_min;
21 gtk_widget_queue_draw(widget);
22 break;
23 default:
24 fprintf(stderr, "[_action_process_slider] unknown shortcut effect (%d) for slider\n", effect);
25 break;
26 }Programmeurs begrijpen waar ik het over heb; voor de anderen, weet gewoon dat ik niet meer dan jij begrijp wat dit doet: het is klotecode, en als er niet meerdere bugs in verstopt zitten, zal het puur geluk zijn. Bugs jagen in dit klotehol is archeologie op de bodem van het riool, temeer daar Darktable geen ontwikkelaarsdocumentatie heeft en, bij gebrek aan zinvolle commentaren in de code, elke wijziging van de bovengenoemde code noodzakelijkerwijs zal beginnen met een reverse-engineeringfase die steeds moeilijker wordt naarmate de tijd verstrijkt.
Het echte probleem met dit soort code is dat je het niet kunt verbeteren zonder het min of meer volledig te herschrijven: om het te repareren moet je het eerst begrijpen, maar de reden waarom het gerepareerd moet worden is juist dat het niet begrijpelijk en op de lange termijn gevaarlijk is. We noemen dat technische schuld . Kortom, al het werk dat in deze functie is geïnvesteerd, zal extra werk creëren omdat het onredelijk is om dat soort code midden in een codebase van enkele honderdduizenden regels te houden en te verwachten dat het niet op een dag in ons gezicht ontploft.
Het is des te belachelijker in de context van een opensource-/vrije toepassing waar het merendeel van het personeel niet-opgeleide programmeurs zijn. Slimme ontwikkelaars schrijven code die begrijpelijk is voor idioten, en andersom.
Collectiefilters
Tot Darktable 3.8 werden de collectiefilters, boven aan de lichttafel, gebruikt om de weergave van een collectie tijdelijk te beperken. De collectie is een extractie uit de fotodatabase op basis van bepaalde criteria, waarvan de meest voorkomende het extraheren van de inhoud van een map is (die Darktable „filmroll" noemt om iedereen in de war te brengen, want een filmroll is eigenlijk de inhoud van een map die als een platte lijst wordt weergegeven in plaats van een boomstructuur - veel mensen denken ten onrechte dat Darktable geen bestandsbeheerder heeft).
Aangezien ik meer dan tien jaar Darktable-gebruiker ben, heb ik een database van meer dan 140.000 vermeldingen. Een collectie extraheren uit deze 140.00 foto’s is een langzame operatie. Maar mijn mappen bevatten zelden meer dan 300 foto’s. De foto’s filteren die bijvoorbeeld 2 sterren of meer scoren, in een collectie van 300 bestanden, is snel omdat het een subset van 300 elementen is. En overschakelen van het ene filter naar het andere is ook snel. Het filter is slechts een gedeeltelijke of volledige weergave van een collectie, geoptimaliseerd voor een snel en tijdelijk start-and-go-gebruik.
Onder het voorwendsel van het refactoren van de filtercode, die alles bij elkaar 550 regels besloeg, maakte de chef Guust Flater er een roeping van om dit model kapot te maken om collectiefilters te veranderen in basale collecties, door middel van meer dan 6.000 regels code , de talloze bugfixes die alleen maar meer regels toevoegden niet meegerekend2. Dat alles, zoals gebruikelijk, zeer configureerbaar en redundant met de klassieke collectiesmodule , die daar bleef, en bediend door pictogrammen die zo cryptisch zijn dat ze er teksttooltips bij moesten toevoegen bij het zweven om te verduidelijken wat ze betekenen..
In deze kwaliteitscode zullen we de eindeloze while vinden onder de switch case in de if in de if in de for (bron ):
1for(int k = 0; k < num_rules; k++)
2 {
3 const int n = sscanf(buf, "%d:%d:%d:%d:%399[^$]", &mode, &item, &off, &top, str);
4
5 if(n == 5)
6 {
7 if(k > 0)
8 {
9 c = g_strlcpy(out, "<i> ", outsize);
10 out += c;
11 outsize -= c;
12 switch(mode)
13 {
14 case DT_LIB_COLLECT_MODE_AND:
15 c = g_strlcpy(out, _("AND"), outsize);
16 out += c;
17 outsize -= c;
18 break;
19 case DT_LIB_COLLECT_MODE_OR:
20 c = g_strlcpy(out, _("OR"), outsize);
21 out += c;
22 outsize -= c;
23 break;
24 default: // case DT_LIB_COLLECT_MODE_AND_NOT:
25 c = g_strlcpy(out, _("BUT NOT"), outsize);
26 out += c;
27 outsize -= c;
28 break;
29 }
30 c = g_strlcpy(out, " </i>", outsize);
31 out += c;
32 outsize -= c;
33 }
34 int i = 0;
35 while(str[i] != '\0' && str[i] != '$') i++;
36 if(str[i] == '$') str[i] = '\0';
37
38 gchar *pretty = NULL;
39 if(item == DT_COLLECTION_PROP_COLORLABEL)
40 pretty = _colors_pretty_print(str);
41 else if(!g_strcmp0(str, "%"))
42 pretty = g_strdup(_("all"));
43 else
44 pretty = g_markup_escape_text(str, -1);
45
46 if(off)
47 {
48 c = snprintf(out, outsize, "<b>%s</b>%s %s",
49 item < DT_COLLECTION_PROP_LAST ? dt_collection_name(item) : "???", _(" (off)"), pretty);
50 }
51 else
52 {
53 c = snprintf(out, outsize, "<b>%s</b> %s",
54 item < DT_COLLECTION_PROP_LAST ? dt_collection_name(item) : "???", pretty);
55 }
56
57 g_free(pretty);
58 out += c;
59 outsize -= c;
60 }
61 while(buf[0] != '$' && buf[0] != '\0') buf++;
62 if(buf[0] == '$') buf++;
63 }en andere if genest over 2 niveaus binnen switch case die nodig zijn om de sneltoetsen te ondersteunen (bron ).
Deze laatste rotzooi was de druppel die de emmer deed overlopen en me Ansel deed forken. Ik weiger te werken aan een tikkende tijdbom in een team dat het probleem niet ziet en in hun vrije tijd met code speelt. Coderen mag hen dan amuseren, mij niet. En de troep repareren die is gemaakt door onverantwoordelijke kinderen die twee keer zo oud zijn als ik, vooral wanneer ze dingen kapotmaken die ik 3 of 4 jaar geleden heb opgeschoond, maakt me woedend.
Lichttafel
De lichttafel onderging 2 bijna volledige herschrijvingen, de eerste begin 2019 en de tweede eind 2019, die veel betwistbare functies toevoegden zoals de culling-weergave .
Kort gezegd is de culling-modus verdeeld in 2 submodi: dynamisch en statisch, die het aantal afbeeldingen verschillend beheren. Veel gebruikers hebben 4 jaar later het verschil nog steeds niet begrepen. We hebben dus de standaardweergave (bestandsbeheerder), de zoombare lichttafel (die niemand gebruikt), de statische culling, de dynamische culling, en de voorbeeldmodus (een enkele beeldvullende foto).
Vervolgens worden er meer weergaveopties toegevoegd aan miniaturen in de lichttafel, waarmee overlays kunnen worden gedefinieerd: basale permanente overlays, uitgebreide EXIF-permanente overlays, hetzelfde maar alleen bij zweven, en tenslotte de getimede zweefoverlays (met een configureerbare timer).
De UI-code die miniaturen en hun overlays rendert, moet dus rekening houden met 5 verschillende weergaven en 7 weergavevarianten , dat wil zeggen 35 mogelijke combinaties. De code die zorgt voor het correct herschalen van miniaturen heeft dus een totaal van 220 regels nodig.
Maar daar houdt het niet op, want de code die de miniaturen-GUI rendert wordt ook gedeeld met de „filmstrip"-onderbalk, wat het aantal mogelijke combinaties in miniatuurrendering feitelijk op 36 brengt. Vermenigvuldigd met 3 GUI-thema’s met verschillende basiskleuren, maakt dat 108 sets CSS-instructies om de GUI volledig vorm te geven… waarvan er veel werden vergeten in de grafische herziening van Darktable 4.0 , en hoe zou het anders kunnen?
In Darktable 2.6 hadden we 4193 regels voor het geheel, dat alleen de bestandsbeheerder-, zoombare lichttafel- en beeldvullende voorbeeldweergaven had, met slechts 2 modi van miniatuuroverlays (altijd zichtbaar of zichtbaar bij zweven):
- 2634 regels views/lighttable.c voor de lichttafel en de miniatuurrendering,
- 1124 regels in libs/tools/filmstrip.c voor de filmstripbalk, die de lichttafelcode voor de miniatuurrendering gedeeltelijk dupliceert,
- 435 regels in libs/tools/global_toolbox.c , voor het knopmenu waarmee miniatuuroverlays kunnen worden in- of uitgeschakeld.
Na Darktable 3.0 en de toevoeging van culling-modi komen we op 6731 regels:
- 5149 regels views/lighttable.c ,
- 1177 regels libs/tools/filmstrip.c ,
- 405 regels libs/tools/global_toolbox.c .
Na Darktable 3.2 en de toevoegingen van de 7 varianten van zeer configureerbare overlays en enige coderefactoring komen we op 8380 regels:
- 1463 regels in views/lighttable.c ,
- 1642 regels in dtgtk/culling.c , waar de culling-weergavefuncties werden losgekoppeld,
- 2447 regels in dtgtk/thumbtable.c , waar de miniatuurcontainers voor de lichttafel en de filmstrip worden beheerd,
- 1736 regels in dtgtk/thumbnail.c , waar de miniaturen zelf worden beheerd,
- 169 regels in dtgtk/thumbnail_btn.c , waar de specifieke miniatuurknoppen worden gedeclareerd,
- 115 regels in libs/tools/filmstrip.c ,
- 808 regels in libs/tools/global_toolbox.c .
In Darktable 4.2, na de correctie van veel bugs, komen we op een totaal van 9264 regels:
- 1348 regels in views/lighttable.c ,
- 1828 regels in dtgtk/culling.c ,
- 2698 regels in dtgtk/thumbtable.c ,
- 2093 regels in dtgtk/thumbnail.c ,
- 166 regels in dtgtk/thumbnail_btn.c ,
- 109 regels in libs/tools/filmstrip.c ,
- 1022 regels in libs/tools/global_toolbox.c .
Het aantal regels (vooral in code die er een misplaatst genoegen in schept om de beste programmeerpraktijken te negeren) is een directe indicator van de moeilijkheid om daar iets te debuggen, maar ook een indirecte indicator (in het specifieke geval van GUI-code) van de CPU-belasting die nodig is om de software te draaien.
Inderdaad, als je darktable -d sql start en je zweeft over een miniatuur in de lichttafel, krijg je in de terminal:
1140.8252 [sql] darktable/src/common/image.c:311, function dt_image_film_roll(): prepare "SELECT folder FROM main.film_rolls WHERE id = ?1"
2140.8259 [sql] darktable/src/common/image.c:387, function dt_image_full_path(): prepare "SELECT folder || '/' || filename FROM main.images i, main.film_rolls f WHERE i.film_id = f.id and i.id = ?1"
3140.8271 [sql] darktable/src/common/metadata.c:487, function dt_metadata_get(): prepare "SELECT value FROM main.meta_data WHERE id = ?1 AND key = ?2 ORDER BY value"
4140.8273 [sql] darktable/src/common/metadata.c:487, function dt_metadata_get(): prepare "SELECT value FROM main.meta_data WHERE id = ?1 AND key = ?2 ORDER BY value"
5140.8275 [sql] darktable/src/common/metadata.c:487, function dt_metadata_get(): prepare "SELECT value FROM main.meta_data WHERE id = ?1 AND key = ?2 ORDER BY value"
6140.8277 [sql] darktable/src/common/metadata.c:487, function dt_metadata_get(): prepare "SELECT value FROM main.meta_data WHERE id = ?1 AND key = ?2 ORDER BY value"
7140.8279 [sql] darktable/src/common/metadata.c:487, function dt_metadata_get(): prepare "SELECT value FROM main.meta_data WHERE id = ?1 AND key = ?2 ORDER BY value"
8140.8280 [sql] darktable/src/common/metadata.c:487, function dt_metadata_get(): prepare "SELECT value FROM main.meta_data WHERE id = ?1 AND key = ?2 ORDER BY value"
9140.8282 [sql] darktable/src/common/metadata.c:487, function dt_metadata_get(): prepare "SELECT value FROM main.meta_data WHERE id = ?1 AND key = ?2 ORDER BY value"
10140.8284 [sql] darktable/src/common/tags.c:635, function dt_tag_get_attached(): prepare "SELECT DISTINCT I.tagid, T.name, T.flags, T.synonyms, COUNT(DISTINCT I.imgid) AS inb FROM main.tagged_images AS I JOIN data.tags AS T ON T.id = I.tagid WHERE I.imgid IN (104337) AND T.id NOT IN memory.darktable_tags GROUP BY I.tagid ORDER by T.name"
11140.8286 [sql] darktable/src/common/tags.c:635, function dt_tag_get_attached(): prepare "SELECT DISTINCT I.tagid, T.name, T.flags, T.synonyms, COUNT(DISTINCT I.imgid) AS inb FROM main.tagged_images AS I JOIN data.tags AS T ON T.id = I.tagid WHERE I.imgid IN (104337) AND T.id NOT IN memory.darktable_tags GROUP BY I.tagid ORDER by T.name"
12140.9512 [sql] darktable/src/common/act_on.c:156, function _cache_update(): prepare "SELECT imgid FROM main.selected_images WHERE imgid=104337"
13140.9547 [sql] darktable/src/common/act_on.c:156, function _cache_update(): prepare "SELECT imgid FROM main.selected_images WHERE imgid=104337"
14140.9550 [sql] darktable/src/common/act_on.c:288, function dt_act_on_get_query(): prepare "SELECT imgid FROM main.selected_images WHERE imgid =104337"
15140.9552 [sql] darktable/src/libs/metadata.c:263, function _update(): prepare "SELECT key, value, COUNT(id) AS ct FROM main.meta_data WHERE id IN (104337) GROUP BY key, value ORDER BY value"
16140.9555 [sql] darktable/src/common/collection.c:973, function dt_collection_get_selected_count(): prepare "SELECT COUNT(*) FROM main.selected_images"
17140.9556 [sql] darktable/src/libs/image.c:240, function _update(): prepare "SELECT COUNT(id) FROM main.images WHERE group_id = ?1 AND id != ?2"
18140.9558 [sql] darktable/src/common/tags.c:635, function dt_tag_get_attached(): prepare "SELECT DISTINCT I.tagid, T.name, T.flags, T.synonyms, COUNT(DISTINCT I.imgid) AS inb FROM main.tagged_images AS I JOIN data.tags AS T ON T.id = I.tagid WHERE I.imgid IN (104337) AND T.id NOT IN memory.darktable_tags GROUP BY I.tagid ORDER by T.name"wat betekent dat er 18 SQL-verzoeken tegen de database worden gedaan om afbeeldingsinformatie op te halen, en die elke keer worden uitgevoerd wanneer de cursor over een nieuwe miniatuur zweeft, zonder reden aangezien de metadata sinds de vorige zweefbeweging niet zijn veranderd.
In Ansel is het me, door de meeste opties te verwijderen, gelukt om 7 verzoeken te besparen, wat nog steeds geen gedupliceerde verzoeken voorkomt maar de timings toch enigszins verbetert (tijdstempels zijn de cijfers aan het begin van elke regel):
112.614534 [sql] ansel/src/common/image.c:285, function dt_image_film_roll(): prepare "SELECT folder FROM main.film_rolls WHERE id = ?1"
212.615225 [sql] ansel/src/common/image.c:356, function dt_image_full_path(): prepare "SELECT folder || '/' || filename FROM main.images i, main.film_rolls f WHERE i.film_id = f.id and i.id = ?1"
312.616499 [sql] ansel/src/common/metadata.c:487, function dt_metadata_get(): prepare "SELECT value FROM main.meta_data WHERE id = ?1 AND key = ?2 ORDER BY value"
412.616636 [sql] ansel/src/common/metadata.c:487, function dt_metadata_get(): prepare "SELECT value FROM main.meta_data WHERE id = ?1 AND key = ?2 ORDER BY value"
512.616769 [sql] ansel/src/common/metadata.c:487, function dt_metadata_get(): prepare "SELECT value FROM main.meta_data WHERE id = ?1 AND key = ?2 ORDER BY value"
612.616853 [sql] ansel/src/common/metadata.c:487, function dt_metadata_get(): prepare "SELECT value FROM main.meta_data WHERE id = ?1 AND key = ?2 ORDER BY value"
712.616930 [sql] ansel/src/common/metadata.c:487, function dt_metadata_get(): prepare "SELECT value FROM main.meta_data WHERE id = ?1 AND key = ?2 ORDER BY value"
812.617007 [sql] ansel/src/common/metadata.c:487, function dt_metadata_get(): prepare "SELECT value FROM main.meta_data WHERE id = ?1 AND key = ?2 ORDER BY value"
912.617084 [sql] ansel/src/common/metadata.c:487, function dt_metadata_get(): prepare "SELECT value FROM main.meta_data WHERE id = ?1 AND key = ?2 ORDER BY value"
1012.617205 [sql] ansel/src/common/tags.c:635, function dt_tag_get_attached(): prepare "SELECT DISTINCT I.tagid, T.name, T.flags, T.synonyms, COUNT(DISTINCT I.imgid) AS inb FROM main.tagged_images AS I JOIN data.tags AS T ON T.id = I.tagid WHERE I.imgid IN (133727) AND T.id NOT IN memory.darktable_tags GROUP BY I.tagid ORDER by T.name"
1112.617565 [sql] ansel/src/common/tags.c:635, function dt_tag_get_attached(): prepare "SELECT DISTINCT I.tagid, T.name, T.flags, T.synonyms, COUNT(DISTINCT I.imgid) AS inb FROM main.tagged_images AS I JOIN data.tags AS T ON T.id = I.tagid WHERE I.imgid IN (133727) AND T.id NOT IN memory.darktable_tags GROUP BY I.tagid ORDER by T.name"Het probleem is dat de broncode SQL-commando’s nest binnen functies die de GUI tekenen, en het ontwarren van deze puinhoop door de verschillende lagen die zijn geërfd van „refactoring" (verondersteld om de code te vereenvoudigen, maar eigenlijk niet dus) is opnieuw archeologie. En als het probleem was opgelost toen de code 6700 regels over 3 bestanden besloeg, zouden we 4 jaar later niet zoeken naar de oorzaken in 2500 extra regels die nu verspreid zijn over 7 verschillende bestanden (de .h-bestanden niet meegerekend).
We bevinden ons in het schoolvoorbeeld waar „refactoring" de code in feite complexer maakte en waar het samenvoegen van miniatuurcode tussen filmstrip en lichttafel alleen maar meer interne if (vertakkingen) toevoegde, genest op meerdere niveaus, wat de structuur nog complexer maakt, alleen om blindelings het codehergebruikprincipe te volgen, dat hier in conflict is met het modulariteitsprincipe , dat een bekwame ontwikkelaar met [overerving](https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming) zou hebben opgelost, want ook al is het niet makkelijk om te doen in C, het is perfect mogelijk (Darktable gebruikt dit principe zelfs in code uit 2009-2010).
Cosmetica neemt het over van stabiliteit
Darktable 4.2 introduceert het voorbeeld van stijlen in de donkere kamer. Dat zou geweldig zijn als stijlen niet diep gebroken waren, wanneer ze worden gebruikt met een niet-standaard pipelinevolgorde en meerdere module-instanties. Het probleem is dat een schone en langetermijnoplossing de theorie van gerichte grafen inhoudt, en dat is waar we onze geliefde copy-paste-codeplassers kwijtraakten.
In dezelfde geest hebben we grote inconsistenties bij het kopiëren en plakken van historie in de overschrijf-modus wanneer ook standaard gebruikerspresets worden gebruikt (vooral in de witbalansmodule). Maar het is veel leuker om de interface vol te kakken, dus het zal daar lange tijd blijven staan.
Darktable 3.6 en 3.8 introduceerden veel varianten van het histogram: vectorscoop, verticale golfvorm, geavanceerde en exotische kleurruimtes. Behalve dat als je darktable -d perf in de terminal start en de donkere kamer opent, je een heleboel
zult zien elke keer dat je de cursor in het venster beweegt (en zelfs niet over het histogram). Het is het histogram dat opnieuw wordt getekend bij elke interactie tussen cursor en venster. Hetzelfde probleem treft veel aangepaste grafische widgets en de oorzaak ervan is niet geïdentificeerd. Merk op dat het Ansel niet treft, dus de oorzaak zou ergens verborgen moeten zitten in de 23.000 regels code die ik heb verwijderd.
Twee keer probeerde ik de puinhoop die deze functie is geworden te refactoren, maar elke keer werd er een nieuwe, urgentere functie doorgeduwd die mijn werk ongeldig maakte. Ik heb het gewoon opgegeven.
De rottende staat van het histogram is zodanig dat een volledige herschrijving minder tijd zou kosten dan een refactoring, temeer omdat het histogram veel te laat in de pipeline wordt bemonsterd, in de schermkleurruimte, wat de definitie van een histogramkleurruimte nietig maakt aangezien de gamut hoe dan ook in de schermkleurruimte wordt geklipt. Maar raad eens… Darktable 4.4 zal nog meer opties hebben, met de mogelijkheid om kleurharmonieën te definiëren (fundamenteel voor geeks die op nummer schilderen en histogrammen bewerken).
Het blijft zo dat er, telkens wanneer je de cursor beweegt, een groot aantal nutteloze herberekeningen voor niets worden gestart. Hoe erg is het? Ik kreeg het idee om het CPU-gebruik van mijn systeem te meten wanneer het inactief is, met het Linux-gereedschap powertop. Het protocol is vrij eenvoudig: een laptop (CPU Intel Xeon Mobile 6e generatie), werkend op batterij in powersave-modus, achtergrondverlichting op minimum ingesteld, open de app en raak 4 min lang niets aan, monitor dan het globale CPU-verbruik van het systeem zoals gerapporteerd door powertop tijdens de 5e minuut:
- Basissysteem (geen app geopend behalve
powertopdie in een terminal draait): 3,0 tot 3,5% CPU - Ansel:
- geopend op lichttafel: 2,9 tot 3,4% CPU,
- geopend op donkere kamer: 3,8 tot 4,5% (voor het terugzetten van modulegroepen naar Darktable 3.2),
- geopend op donkere kamer: 3,0 tot 3,5% (na het terugzetten van modulegroepen),
- Darktable:
- geopend op lichttafel: 6,6 tot 7,1% CPU,
- geopend op donkere kamer: 30,9 tot 44,9% CPU (nee, het is geen kommafout),
Ik begrijp niet wat Darktable berekent wanneer we het open laten staan zonder de computer aan te raken, want er is niets te berekenen. Darktable in de lichttafel verbruikt op zichzelf net zoveel als het hele systeem (Fedora 37 + KDE-desktop + wachtwoordbeheerder en Nextcloud-client die op de achtergrond draaien), en het verbruikt 10 keer zoveel als het hele systeem wanneer het in de donkere kamer wordt geopend.
Dit alles wijst op zeer buggy grafische-interfacecode. In Ansel heb ik een groot deel van de vuile code verwijderd, zonder verder iets te optimaliseren, en deze cijfers valideren alleen maar mijn keuze: vuile code verbergt problemen die niet detecteerbaar zijn door het te lezen, en we kunnen simpelweg niet doorgaan op dit pad.
Ik ben blijkbaar de enige die vindt dat het onaanvaardbaar is om de pixelpipeline een derde tot de helft van het CPU-vermogen te ontnemen om een stomme interface te tekenen. Hoe je het ook wendt of keert, er is geen geldige reden waarom software die open blijft staan zonder dat ernaar wordt omgekeken de computer in een broodrooster verandert, temeer omdat we geen Russisch gas meer kopen.
Tegen onszelf werken
Wij zijn fotografen. Het feit dat we een computer nodig hebben om te fotograferen is een nieuwigheid (20 jaar oud), gekoppeld aan de digitale beeldtechnologie die om allerlei redenen (goede en slechte) een 160 jaar oude, gekende en beheerste technologie verving. In het proces is het feit dat we een computer en software nodig hebben om beelden te produceren pure en simpele overhead . Mensen die geen verstand van computers hebben dwingen om ze te gebruiken voor taken die ze voorheen perfect handmatig konden uitvoeren, is ook een vorm van onderdrukking, en dit verhullen als een soort technische vooruitgang is een vorm van psychologisch geweld.
Software impliceert ontwikkeling, onderhoud, documentatie en projectmanagement. Dat zijn meerdere lagen overhead bovenop de vorige. Toch zou het feit dat de mankracht in opensourceprojecten geen compensatie vraagt, niet mogen verhullen dat de tijd die aan de software wordt besteed (verloren?), aan het gebruik ervan, de ontwikkeling ervan, het onderhoud ervan, op zich een niet-terugvorderbare kost is.
De weinige voorbeelden hierboven geven een overzicht van de complexering van de broncode, maar ook van de degradatie ervan in de loop van de tijd wat kwaliteit betreft, omdat basale en robuuste functies worden vervangen door spaghetticode , verwarrend en stiekem gebugd. Achter deze kwestie van leesbaarheid schuilt het echte probleem: het bemoeilijken van de onderhoudbaarheid op middellange termijn, wat een sombere toekomst voor het project belooft, met instemming van de beheerder.
In de 4 jaar dat ik fulltime aan Darktable werk, is 2022 het eerste jaar dat ik mezelf praktisch niet in staat vind om de oorzaak van de meeste interfacebugs te identificeren, omdat de werklogica zeer verhuld is geworden en de code onbegrijpelijk. Het aantal opgeloste bugs neemt ook constant af, zowel in absolute waarde als in verhouding tot de samengevoegde pull requests, terwijl het volume van het codeverkeer ongeveer constant blijft (noot 1: de volgende tellingen van regels code omvatten alleen C/C++/OpenCL en generatieve XML-bestanden en sluiten commentaren uit 3) (noot 2: het aantal geopende issues wordt geteld voor de levensduur van de vorige versie):
- 3.0 (december 2019, een jaar na 2.6)
- 1049 issues geopend , 66 issues gesloten / 553 pull requests samengevoegd (12%),
- 398 bestanden gewijzigd, 66 k invoegingen, 22 k verwijderingen, (netto: +44 k regels),
- 3.2 (augustus 2020)
- 1028 issues geopend , 92 issues gesloten / 790 pull requests samengevoegd (12%),
- 586 bestanden gewijzigd, 54 k invoegingen, 43 k verwijderingen (netto: +2 k regels),
- 3.4 (december 2020)
- 981 issues geopend , 116 issues gesloten / 700 pull requests samengevoegd (17%),
- 339 bestanden gewijzigd, 46 k invoegingen, 23 k verwijderingen (netto: +23 k regels),
- 3.6 (juni 2021)
- 759 issues geopend , 290 issues gesloten / 954 pull requests samengevoegd (30%),
- 433 bestanden gewijzigd, 53 k invoegingen, 28 k verwijderingen (netto: +25 k regels),
- 3.8 (december 2021)
- 789 issues geopend , 265 issues gesloten / 571 pull requests samengevoegd (46%),
- 438 bestanden gewijzigd, 41 k invoegingen, 21 k verwijderingen (netto: +20 k regels),
- 4.0 (juni 2022)
- 632 issues geopend , 123 issues gesloten / 586 pull requests samengevoegd (21%),
- 359 bestanden gewijzigd, 30 k invoegingen, 15 k verwijderingen (netto: +15 k regels),
- 4.2 (december 2022)
- 595 issues geopend , 60 issues gesloten / 409 pull requests samengevoegd (15%),
- 336 bestanden gewijzigd, 14 k invoegingen, 25 k verwijderingen (netto: -11 k regels),
- (verwijderingen zijn grotendeels te wijten aan het verwijderen van het SSE2-pad in de pixelcode, wat de prestaties van typische Intel i5/i7-CPU’s benadeelt ten gunste van AMD Threadripper-CPU’s),
- 4.4 (juni 2023)
- 500 issues geopend , 97 issues gesloten / 813 pull requests samengevoegd (12%),
- 479 bestanden gewijzigd, 57 k invoegingen, 41 k verwijderingen (netto: +16 k regels),
Om de vergelijking makkelijker te maken, laten we ze op jaarbasis herrekenen:
- 2019: 1049 nieuwe issues, 66 gesloten, 88 k wijzigingen, +44 k regels,
- 2020: 2009 nieuwe issues, 208 gesloten, 166 k wijzigingen, +25 k regels,
- 2021: 1548 nieuwe issues, 555 gesloten, 143 k wijzigingen, +45 k regels,
- 2022: 1227 nieuwe issues, 183 gesloten, 84 k wijzigingen, +4 k regels.
Het lijkt erop dat ik niet de enige ben die de bugs van 2022 veel moeilijker aan te pakken vindt, want er zijn er veel minder opgelost vergeleken met 2021, en 2023 laat tot nu toe dezelfde trend zien. De verhouding tussen pull requests (daadwerkelijk verricht werk) en gesloten issues (daadwerkelijk opgeloste problemen) is gewoon belachelijk.
Tussen Darktable 3.0 en 4.0 groeide de GUI-code met 53 %, van 49 k naar 75 k regels4 (commentaar en witregels niet meegerekend), en bereikte 79 k regels in 4.4. De belabberde kwaliteit ervan daargelaten, betwijfel ik ten zeerste of het de bruikbaarheid van de software met 53 % heeft verbeterd. Sterker nog, ik ben er behoorlijk van overtuigd dat het tegendeel waar is. In Ansel heb ik de GUI-code tot nu toe teruggebracht tot 53 k regels terwijl ik nauwelijks functionaliteit heb weggehaald.
Dit alles is gewoon te veel te snel voor een stel hobbyisten die ’s avonds en in het weekend werken, zonder structuur en planning. Het Darktable-team werkt zichzelf tegen door meer hooi op zijn vork te nemen dan het aankan: het ondersteunt te veel verschillende opties, het produceert code waarvan het resultaat van te veel omgevingsvariabelen afhangt, en waarmee op te veel verschillende manieren geïnterageerd kan worden. Dat alles om te vermijden dat er ontwerpkeuzes gemaakt moeten worden die sommige gasten zouden kunnen beledigen door functies en beschikbare opties te beperken. Aan de eindgebruikerskant leidt dit tot contextuele bugs die onmogelijk op andere systemen te reproduceren zijn, en dus helemaal niet op te lossen.
Het is simpel: het verrichte werk kost steeds meer werk, en het onderhoud is niet gegarandeerd, zoals de afname van gesloten issues laat zien, omdat het gewoon te veel is. In een bedrijf is dit het moment waarop je het bloeden moet stelpen voordat de kluizen leeg zijn. Maar een team van amateurs dat geen resultaat hoeft te leveren, kan een oneindige hoeveelheid verlies verdragen. Alleen wordt het werk dat door het werk gecreëerd wordt met de tijd vervelender, frustrerender en moeilijker, en de eindgebruikers worden gegijzeld door een bende egoïstische klootzakken en zullen daarvoor betalen in de vorm van GUI-complexiteit, nutteloze CPU-belasting, en de noodzaak om minstens één keer per jaar opnieuw te leren hoe je basistaken met de software uitvoert.
Eigenlijk verwacht ik dat het huidige team van massavernietiging gaandeweg minder en minder vrije tijd zal vinden om aan het project bij te dragen, naarmate ze beseffen dat ze zichzelf in een doodlopende straat met een oplegger klem hebben gereden, en hun rotzooi aan de volgenden overlaten. Maar hoe eerder ze opgeven, hoe minder schade ze zullen aanrichten.
De uitspatting van opties en voorkeuren, dé strategie waarmee Darktable ontwerpmeningsverschillen (niet) beheert, creëert superspecifieke gebruikssituaties waarin geen enkele gebruiker dezelfde opties heeft ingeschakeld en waarin het onmogelijk is om bugs in een andere omgeving te reproduceren. En gebruikers vragen om het configuratiebestand darktablerc bij bugrapporten te voegen zou ook niet helpen, aangezien dat bestand momenteel 1287 regels telt die vrijwel onleesbaar zijn.
Rare en nauwelijks reproduceerbare bugs stapelen zich op, zelfs op System 76 -computers die specifiek voor Linux ontworpen zijn, waar we geen driverproblemen kunnen aanvoeren. Veel inconsistente en willekeurige bugs die ik heb gezien tijdens het geven van bewerkingslessen staan niet op de bug tracker, en het is nogal duidelijk dat ze ergens verscholen zitten in de wirwar van de if- en switch case-uitspatting die sinds 2020 in alarmerend tempo wordt toegevoegd.
Deze vreemde en contextuele bugs kunnen alleen opgelost worden door de besturingsstroom van het programma te vereenvoudigen en dus door het aantal gebruikersparameters te beperken. Maar de meute geeks die met hun armen op het project staat te wapperen wil er niet van horen en, erger nog, de bug-„fixes" voegen over het algemeen alleen maar meer regels toe om pathologische gevallen afzonderlijk af te handelen.
Darktable lijdt in feite aan meerdere problemen:
- Een harde kern van tamelijk middelmatige ontwikkelaars die veel vrije tijd hebben om willekeurige dingen te doen, gedreven door de allerbeste bedoelingen maar zich niet bewust van de schade die ze aanrichten (middelmatige mensen zijn altijd het meest beschikbaar),
- De inschikkelijkheid van de beheerder, die vuile code doorlaat om aardig te zijn,
- Een kritiek gebrek aan vaardigheden in zuivere wiskunde, algoritmiek, signaalverwerking, kleurwetenschap en abstract denken in het algemeen, die nodig zijn boven op de pixelverwerkingscode om functies te vereenvoudigen en te factoriseren,
- Een verachtelijke gewoonte om te „ontwikkelen" door code te kopiëren en plakken die elders in het project of in andere FLOSS-projecten wordt gevonden, die mogelijk een andere pipeline-architectuur gebruikten, maar zonder die dienovereenkomstig aan te passen (aanpassen impliceert begrijpen, en dat is te veel gevraagd…),
- Een keiharde weigering om functies te snoeien om ruimte te maken voor nieuwe en een zeker evenwicht te bewaren,
- Een steekproefbias, waarbij de enige gebruikers die via Github met de ontwikkeling interacteren programmeurs en Engelstalig zijn. Feit is dat het grote publiek niet begrijpt wat een code forge is en dat het moeilijk is om niet-programmeurs aan te moedigen een Github-account aan te maken om bugs te melden. We hebben het over een gebruikerssteekproef die voor meer dan 44 % uit programmeurs bestaat en voor meer dan 35 % uit universitair afgestudeerden (respectievelijk 6 % en 15 % in de algemene bevolking).
- Een ontwikkelstijl in geforceerde mars, zonder planning of dialoog, waarbij elke Github-gebruiker discussies kan vervuilen met een ongeschoolde mening over lopend werk. Feit is dat beeldverwerking er makkelijk uitziet en onschadelijk is, zozeer dat iedereen die een logaritme kan berekenen zich competent voelt. Maar de fundamentele fouten in de colorimetrieketen van Darktable zijn er om ons dagelijks aan het tegendeel te herinneren.
- Een gebrek aan projectbrede prioriteiten wat betreft welke functies te refactoren, te stabiliseren of uit te breiden: alle projecten zijn tegelijkertijd open, zelfs als ze met elkaar in conflict zijn.
- Een hoeveelheid activiteit (e-mails en meldingen) die onmogelijk te volgen is, tussen commentaren, off-topic discussies, bugs die het niet zijn, codewijzigingsvoorstellen, daadwerkelijke codewijzigingen die je eigen lopende werk kunnen beïnvloeden, wat betekent dat je overal en altijd bovenop moet zitten; er is veel te lezen, heel weinig om te behouden, discussie omwille van de discussie belemmert de productiviteit en het gebrek aan werkstructuur is de belangrijkste oorzaak van dat alles,
- Niet-blokkerende bugs die haastig verstopt worden voordat we ze helemaal begrepen hebben, in plaats van ze echt op te lossen en bij de wortel aan te pakken, wat problemen op de lange termijn verplaatst of zelfs verergert zonder sporen na te laten in enige vorm van documentatie,
- Een releaseschema dat we koste wat het kost aanhouden, zelfs wanneer het niet realistisch is, terwijl niemand het ons oplegt behalve wijzelf,
- Codewijzigingen die op elk moment overal kunnen plaatsvinden, wat betekent dat we op drijfzand werken en dat we net zo snel en net zo slecht als de anderen moeten werken om niet achter te blijven op het volume en de frequentie van de wijzigingen (commits),
- Nieuwe functies die de bruikbaarheid verslechteren en het gebruik compliceren zonder een duidelijk probleem op te lossen, die dienen als recreatieve projecten voor ontwikkelaars die niet geschoold zijn in ontwerp/engineering.
Maar het meest woedend makend is deze koppigheid om eenvoudige en functionele functies te vervangen door gedrochten van overengineering, bestemd om afwijkend en marginaal gebruik te behagen terwijl ze iedereens leven moeilijker maken met eindeloze lijsten van ondoordachte opties. De beste plek om een boom te verbergen is midden in het bos, en velen hebben dat nog steeds niet geleerd.
Agitatie verwarren met activiteit
Elke kiezer kritiseert graag de ontsporing die er, in de politiek, uit bestaat om omstandigheidswetten uit te vaardigen, slecht geschreven, om de publieke opinie te sussen na een bijzondere gebeurtenis, om te tonen dat we handelen, terwijl er al soortgelijke wetten bestaan die niet of niet volledig worden toegepast bij gebrek aan middelen. Dat noemen we agitatie: het ziet eruit als actie, het klinkt als actie, het heeft de kosten van actie, maar het leidt tot niets tastbaars of praktisch.
Het team van amateurs zonder projectmanagement dat zich opwindt over Darktable produceert alleen maar toekomstige problemen. In het verleden werd Darktable eens per jaar uitgebracht met ongeveer 1500 tot 2000 commits voorsprong op de vorige versie. Dat is nu het volume aan wijzigingen dat in 6 maanden wordt bereikt. Een „werk"-volume dat zo snel toeneemt zonder te leiden tot teamworkmethoden, met inbegrip van duidelijke prioriteiten voor elke release en taakverdeling, en zonder softwarekwaliteitscontrole op basis van objectieve metrieken (aantal stappen of verstreken tijd om een bepaalde taak uit te voeren), dat zijn alleen maar gasten die op elkaars tenen staan terwijl ze hun eigen agenda pushen zonder zorg voor anderen, noch voor het project, noch voor gebruikers.
Darktable is de computerclub van de middelbare school geworden, waar geeks hun lol beleven. Het is globaal een samenvatting van alle ergste verhalen van IT-bedrijven, met het verschil dat het project geen cent verdient, wat het urgent maakt om ons af te vragen waarom we onszelf dat opleggen: er is geen winst om te delen, maar iedereen deelt de kosten. Het is een chaotische en toxische werkomgeving die alleen maar burn-out zou produceren als de deeltijdamateurs resultaten moesten leveren en voltijds moesten werken. Als de enige voltijdse gast eraan laat ik het aan jou over om je de hoeveelheid stress en verloren energie voor te stellen die het kost om bij te blijven met de permanente kakofonie, alleen maar om zeker te zijn dat ik de 2 % die daadwerkelijk relevant voor mij is niet mis in de hoeveelheid ruis die door ongereguleerde discussies wordt geproduceerd.
Aan de gebruikerskant prijzen we de bruisende activiteit van het Darktable-project (ja, er is beweging), zonder te beseffen dat het op hol slaan van de commits geen activiteit is maar agitatie, en in het bijzonder technische schuld die wij-weten-niet-wanneer door wij-weten-niet-wie afbetaald zal moeten worden. De schoonheid van een project waarin niemand verantwoordelijkheid hoeft te nemen voor zijn geklungel omdat niemand voor niets aansprakelijk is: het staat in de GNU/GPL-licentie. We kunnen dus met totale straffeloosheid het werk van de vorigen verpesten.
We hebben het begin van een kwaliteitscontrole, via de integratietests, die de perceptuele fout meten over referentiebeeldbewerkingen, maar ze lokken geen enkele reactie uit wanneer we een gemiddelde delta E-fout van 1,3 zien (klein) terwijl de aard van de wijziging een strikt nul delta E had moeten hebben. Als de enige test slaagt (omdat we één enkel SDR-beeld van een studiofoto testen), wordt er geen vraag gesteld over de vraag of de theorie deugdelijk en robuust is. We hebben de test omgezet in een kwijting, zolang de metriek maar onder de validatiedrempel blijft…
Bij de release van Darktable 4.0 — Geektable — zag ik op Youtube mensen die begonnen te klagen dat deze release niet erg spannend was. Na jarenlang mensen te hebben gedoseerd met superlatieve releases, volgepropt met nieuwe functies die we geen tijd hebben om fatsoenlijk te testen (6-8 gasten die elke 6 maanden 38-50 k regels uitpissen terwijl ze alleen ’s avonds en in het weekend werken, droom je nog?), hebben we ze verslaafd gemaakt aan de overvolle kerstboom om te verzekeren dat, de dag dat we verantwoordelijk beginnen te worden en stabiele versies (en dus saaie) uitbrengen, dat tegen ons gebruikt zal worden.
De reden voor dit frenetieke releasetempo is dat de pull requests ouder dan 3 maanden systematisch in conflict zijn met de master-branch, gezien die elke maand voor „algemene tests" door elkaar wordt geschud. Maar de zeldzame gebruikers die de master-branch bouwen hebben geen idee wat ze in het bijzonder moeten testen, tenzij ze de commitgeschiedenis van Git ontleden, wat impliceert dat ze zowel de C-taal als de impact van de wijzigingen in de praktijk op de software begrijpen.
Om langlevende branches te beperken, die onvermijdelijk in conflict met de master zullen eindigen, hebben we een briljante oplossing gevonden: we brengen 2 versies per jaar uit, waardoor ontwikkeling in geforceerde mars op basis van onafgemaakte en nauwelijks geteste code een manier van leven wordt, zonder ooit te beseffen dat het kernprobleem ten eerste het gebrek aan planning is, maar ook dat bijdragers beginnen te coderen voordat ze klaar zijn met het definiëren van het op te lossen probleem (wanneer er een echt probleem op te lossen is, en niet gewoon een gast die wakker werd met „het zou cool zijn als…"), waarbij ze parallel aan beide delen werken zonder communicatie.
Het stof heeft geen tijd om neer te dalen of we schudden de codebasis alweer door elkaar, zonder stabilisatiefasen af te dwingen waarin we alleen bugs opruimen (en ik heb het niet over de maand feature-freeze voorafgaand aan de release, maar over releases die uitsluitend gewijd zijn aan het opschonen van code). De bug tracker implodeert in de 3 weken volgend op elke release, omdat een aanzienlijk deel van de gebruikers alleen de voorgebouwde pakketten gebruikt, die samenvallen met de kerst- en zomervakantie, waarin ik persoonlijk betere dingen te doen heb na de toch al stressvolle sprint die de maand voorafgaand aan de release is.
Sinds 2021, wanneer ik mijn Git-repository bijwerk met de laatste wijzigingen van Darktable master, vraag ik me altijd af wat ze deze keer kapot hebben gemaakt. We breken sneller dan we repareren, en meestal breken de reparaties iets anders. De enige gebruikers die Darktable stabiel vinden zijn eigenlijk degenen die er een heel basaal gebruik van maken, wat ironisch is voor een app waarvan het verkoopargument is dat hij geavanceerd is.
En dan wordt me voorgeschoteld dat het gratis werk is alsof dat een excuus zou zijn. Maar het is eigenlijk een verzwarende omstandigheid: waarom leggen we onszelf zulke werkomstandigheden op als het niet eens winstgevend is??? Naast het feit dat dit gratis werk mij een post-release burn-out per jaar oplevert, kost het steeds meer aan onderhoud en is het onderhoud steeds verachtelijker om te doen. Het is geen gratis werk, het is erger: het is werk dat kost zonder te betalen.
Hoe dan ook, het werk wordt geleverd door mensen met beperkte tijd en energie. Als de hulpbron beperkt is, kap dan met de onzin: we zitten in dezelfde rentabiliteitsbeperkingen als een bedrijf, minus de sociale bijdragen, behalve dat onze ruilmunt tijd is en die is niet terug te vorderen. Zonder prioriteitenbeheer worden we ingehaald door technische schuld die we niet de middelen zullen hebben om te onderhouden.
Waar wachten we op om gelukkig te zijn?
Het feit dat Darktable een stoomwals is die op hol geslagen is en zonder bestuurder, die een toenemende hoeveelheid werk genereert, is een slecht teken voor een 15 jaar oud project. Normaal gesproken vertraagt een volwassen project omdat het compleet genoeg is om bruikbaar te zijn en omdat de mensen die eraan werken hun kruissnelheid en efficiënte werkmethoden hebben gevonden.
Ik ben er sinds 2018 voltijds mee bezig, voor een maandinkomen tussen 800 en 900 €, en het is een understatement om te zeggen dat het slecht betaald is om de rampzalige gevolgen te verdragen van ongeorganiseerde amateurs die hun lol proberen te beleven ten koste van de kwaliteit van het eindproduct en de bruikbaarheid ervan door computer-Dreuzels. Bovendien worden Dreuzels ook veracht, uit principe.
Als ik een maand lang onder een steen kruip om een perceptuele kleurruimte te ontwikkelen, dan is het, wanneer ik eruit kom, om het nieuwe labyrintische systeem te ontdekken dat het view-model-controller-paradigma nog een beetje meer schendt, en te horen te krijgen dat ik te laat kom om me ertegen te verzetten. Als ik in augustus 3 weken vakantie neem, dan is het om te ontdekken dat de beheerder (nog een keer) mijn review heeft omzeild op een wiskundige wijziging aan de voornoemde kleurruimte, die vereist dat je er rustig voor gaat zitten en nadenkt, dit alles omdat… we snel moesten gaan? Voor welke noodsituatie precies?
Ik kreeg de melding waarschijnlijk ergens tussen de 2234 e-mails die Github me tussen januari en augustus 2022 stuurde (in 2021 waren het er 4044), om nog maar te zwijgen van gebruikers die me overal pingen, op Youtube, Reddit, Matrix, Github, Telegram, rechtstreeks per e-mail, en voorheen op pixls.us (693 e-mails in 2022, 948 in 2021). Dit alles voor mensen die volkomen uit de pas lopen en niet beseffen dat ik dit de hele week doe, dat fotografie misschien hun hobby is maar mijn werk, en dat ik het gewoon zou waarderen als mensen me met rust laten tijdens weekends en vakanties. Je kunt raden dat de meest vervelende niet degenen zijn die mijn werk financieel steunen. Mensen respecteren werk alleen als ze er een hoge prijs voor gefactureerd hebben gekregen.
Ik heb geen tijd om onderzoeker en ontwerper te zijn, boven op secretaresse, terwijl ik technische baby-sitting doe voor een team van Guust Flaters die zowel opgeleid als in de gaten gehouden moeten worden omdat ze niet in staat zijn om:
- een ontwikkelingsplanning te maken met een lijst van prioriteiten van nieuwe functies om aan te werken,
- een specificatieboek van behoeften en problemen te leveren, met een echt gebruiksscenario, voordat ze zich op hun code-editor storten en van alles doen om een nieuwe functie te ontwikkelen op zoek naar een probleem om op te lossen.
- de onderhoudskosten van de wijziging in te schatten voordat ze de bionische baconpomp op omgekeerde osmose uitvinden die alleen op even dagen werkt als Jupiter uit fase is met Saturnus,
- de uitgaven te beperken en de verliezen te beknotten wanneer ze zichzelf klem rijden in ontwerp-doodlopende-straten die regressies introduceren die erger zijn dan de hypothetische verwachte voordelen,
- de verantwoordelijkheid op zich te nemen en een release uit te stellen als de code overduidelijk niet klaar is (of ik heb er niets van begrepen en de aandeelhouders zullen om onze koppen vragen als we te laat uitbrengen???).
Management (of teammanagement) is overhead dat wat werk kost, maar het Darktable-team heeft een schaal bereikt waarop het gebrek aan management eigenlijk meer werk kost, vooral omdat geen van de projectoprichters nog in het team zit en de oorspronkelijke ontwerpblauwdrukken elke keer dat er iets veranderd moet worden gereverse-engineerd moeten worden met grep in de code. Dat was houdbaar met een klein team waarin iedereen elkaar kende, maar Darktable is tijdens de Covid-lockdowns een druk bezocht project geworden en deze manier van werken is niet houdbaar met het huidige personeel.
Ga toch gewoon naar de code kijken! Vergelijk de branch darktable-2.6.x met darktable-4.2.x, bestand voor bestand, en veel plezier!
Alles wat ik tot nu toe heb gehoord zijn stereotiepe zinnen als „het is zoals bij veel andere opensourceprojecten" en „er is niets wat we eraan kunnen doen". Mensen zijn bang van de hoeveelheid werk die een fork is (ik kreeg e-mails die me probeerden te overtuigen dat het de productiviteit deelde), zonder zich te realiseren hoeveel middelen er momenteel verspild worden door het Darktable-project en de permanente stress om je werk te moeten baseren op een instabiele codebasis die voortdurend door elkaar wordt geschud. Tot nu toe heeft Ansel me minder vermoeidheid gekost en heb ik een aanzienlijk aantal problemen opgelost, waaronder sommige die al sinds 2016 gemeld werden zonder tekenen van interesse van de verdomde „community".
Bovendien levert Ansel automatisch gebouwde nightly-pakketten voor Linux (.AppImage) en Windows (.exe), om echte algemene tests mogelijk te maken, ook door mensen die de software niet zelf kunnen bouwen. Ik heb daar al in 2019 om gevraagd , maar blijkbaar hebben geeks betere dingen te doen, en moest ik zelf 70 uur investeren om het voor elkaar te krijgen. De operatie is nu al een succes en maakte het mogelijk om binnen enkele dagen Windows-bugs op te lossen die weken zouden hebben gekost om in Darktable op te sporen. (En Darktable graaide 3 weken later mijn AppImage-buildscript weg zonder behoorlijke bronvermelding, maar dat is een detail.)
Nu we het over productiviteit hebben, laten we in herinnering brengen dat de lichttafel sinds 2018 bijna helemaal twee keer is herschreven (en de laatste versie is niet beter of sneller) en dat de grote wijziging aan de collectiefilters die in april 2022 werd geïntroduceerd een andere soortgelijke wijziging heeft overschreven (maar met slechts 600 regels in plaats van 6000) die in februari 2022 werd geïntroduceerd (de februari-versie is degene die in Ansel zit). We kunnen het woord „productiviteit" fatsoenlijk niet uitspreken wanneer het werk van de ene bijdrager letterlijk eerder werk van een andere uitwist binnen een tijdsbestek van één maand, uit puur gebrek aan projectmanagement. Dat heet op elkaars tenen staan.
Dus wat doen we om het probleem op te lossen? In stilte lijden? In ontkenning leven? Doorgaan met het repareren van dingen die een ander het volgende jaar kapot zal maken wanneer we niet opletten? De Dreuzel maar laten geloven, ter lengte van fotoforums, dat opensource net zo goed is als propriëtaire software, terwijl we als joker achter de hand houden dat het gratis is dus dat je geen recht hebt om te klagen? Is dat niet een beetje te makkelijk en oneerlijk, deze dubbele taal?
Zou je niet willen stoppen met gewoontes voor ervaring te laten doorgaan en fatalisme met wijsheid te verwarren, maar het probleem liever bij de kern aanpakken? Denk je niet dat jij en ik iets beters verdienen dan software ontworpen door amateurs wier enige talent is dat ze vrije tijd hebben en het zich kunnen veroorloven om gratis te werken sinds ze naar het management zijn doorgeschoven en de kinderen naar de universiteit zijn?
Of ik heb me vanaf het begin vergist, en gaat opensource erover om overgecompliceerde tools te geven aan geeks die ze niet echt nodig hebben, terwijl we de rest van de wereld proberen te overtuigen dat opensource geen hypernich voor ontwikkelaars is?
Vier jaar werk om hier te komen
Na 4 jaar voltijds aan Darktable gewerkt te hebben voor 70 % van het minimumloon, en 2 jaar de chronische ontevredenheid gedragen te hebben dat ik mijn naam bevlekte door aan stront bij te dragen, heb ik Ansel geforkt en ga ik niet terug.
In 4 jaar heb ik deze software iets gebracht wat er schrijnend aan ontbrak: een uniforme workflow, gebaseerd op een set modules die ontworpen zijn om samen te werken, maar die elk op een apart aspect inwerken, waar de Darktable-modules eerder een verzameling ongelijksoortige plugins waren. We hebben het over:
- filmic,
- tone equalizer,
- de fysisch-accurate onscherpte-module,
- beide versies van de color balance,
- color calibration, inclusief de GUI om te profileren met color checkers rechtstreeks in de donkere kamer en de witbalans met behulp van CIE-standaarden,
- de negadoctor-module om filmnegatieven te inverteren op basis van Kodak Cineon,
- de diffuse en verscherp-module voor het toevoegen en verwijderen van onscherpte op basis van thermische diffusie,
- de geleide laplaciaanse reconstructie van hooglichten.
Ik heb ook meer fundamentele tools ontwikkeld die de basis vormen voor de vorige modules:
- een oplosser voor 4e-orde anisotrope partiële differentiaalvergelijkingen in wavelet-ruimte voor diffuse en verscherp,
- een aanpassing van de vorige als de geleide laplaciaan voor reconstructie van het RGB-signaal door gradiëntpropagatie,
- een perceptueel kleurverschijningsmodel dat rekening houdt met het Helmholtz-Kohlrausch-effect in de verzadigingsberekening, om het „fluo"-effect te beperken dat typisch gepaard gaat met intense verzadigingsinstellingen, in color balance,
- een theoretische hulp bij het ontwikkelen van het exposure-invariant guided filter (EIGF), in de tone equalizer,
- een oplosser voor lineaire vectorvergelijkingen volgens de Choleski-methode,
- diverse interpolatiemethoden van orde 2, 3 en radiaalgebaseerd.
In de GUI heb ik met name gedaan:
- de stijldeclaratie refactoren, waarbij ik de styling uit de C-code haalde om ze naar het CSS-stylesheet te mappen, wat het mogelijk maakt om meerdere thema’s voor de UI te hebben, inclusief door de gebruiker gedefinieerde,
- de voorbeeldweergavemodus focus-peaking en ISO 12 646 kleurbeoordelingsmodus introduceren,
- het kleurvocabulaire in de globale kleurenkiezer introduceren, waarmee de gekozen kleur benoemd kan worden aan de hand van zijn chromaticiteitscoördinaten, gericht op kleurenblinde fotografen.
Daarna schreef ik tientallen documentatiepagina’s in 2 talen, publiceerde ik artikelen en tientallen uren video op YouTube om te demonstreren hoe je modules gebruikt, in welke context en voor welk voordeel, inclusief snelle bewerkingen met slechts 3 tot 5 modules om 75 tot 80 % van de foto’s te verwerken, ongeacht hun dynamisch bereik. In de opensourcewereld bestaat, behalve misschien voor projecten die door stichtingen ondersteund worden (zoals Krita en Blender), dit niveau van ondersteuning en documentatie gewoon niet, en het zijn niet de ontwikkelaars zelf die dit werk verzorgen.
Ondanks dit alles heb ik nooit meer dan 240 donateurs gehad, te vergelijken met ruwweg 1800 unieke respondenten die deelnamen aan de Darktable-enquêtes van 2020 en 2022 , en die verklaren tussen 500 en 1000 €/jaar aan fotografie te besteden.
Ik ga niet toekijken hoe ze de bruikbaarheid van deze software vernietigen terwijl ik mezelf probeer te overtuigen dat het vooruitgang is en dat er niets aan te doen is. In plaats van vooruitgang is het de waanbeeld-visie op vooruitgang van een stel vijftigers-dilettanten. Het is nu 2 jaar dat ik geduldig mijn mond heb gehouden, aardig probeerde te zijn, maar kijkend naar de degradatie van basisfuncties, gecompliceerd om te voldoen aan de grillen van gekke programmeurs, had ik eerder verachtelijk moeten worden. Aardig doen loste niets op, want de trend zette niet alleen door, maar versnelde, en er zal geen besef komen vóór het punt van geen terugkeer. We kunnen niet van degenen die de problemen hebben veroorzaakt verwachten dat ze degenen zijn die ze oplossen.
Dus als ik moet werken voor een „community" die er vooral in zit voor het abonnementsvrije aspect van de software, en die besloten heeft dat mijn werk geen minimumloon waard is, wel, dan doe ik het op mijn voorwaarden en volgens mijn normen.
Wat functies betreft heeft Darktable er al te veel en moeten we snoeien. Ik gebruik het nu 10 jaar en het zat toen al volgepropt met slecht ontworpen spul. De uitdaging is nu om de functies op een slimme manier te presenteren, en om vervelende bugs op te lossen voordat deze idioten er nieuwe introduceren, of ze zelfs op hun eigen speciale manier oplossen: door het stof onder het tapijt te vegen. Waarbij we in gedachten houden dat de pipeline van Darktable 15 jaar oud is, en dat we hem niet veel meer kunnen optimaliseren dan dat, gezien hij al flink gemarteld is om een volledige herschrijving te vermijden (en een volledige herschrijving biedt geen voordeel als we Gtk als grafische backend moeten behouden, aangezien dat de belangrijkste prestatie-bottleneck is).
De oplossingen die Darktable nodig heeft impliceren dat er code en opties verwijderd worden, niet dat er altijd meer bij komt. Robuustheid heeft die prijs. Het Darktable-team doet precies het tegenovergestelde zonder van zijn fouten te leren.
Met Ansel wil ik een manier om dit werk vredig af te maken, zodat Linux-gebruikers een betrouwbaar, consistent en performant instrument hebben voor hun artistieke fotografie. Voordat ik overstap naar Vkdt omdat het huidige ontwerp zijn grenzen laat zien.
Translated from English by : Claude. In case of conflict, inconsistency or error, the English version shall prevail.
By the way, I can no longer bare the attempt of being different for the sake of it by writing “Darktable”, proper noun, without initial capital. It’s childish, it’s neither funny or disruptive, and it makes a mess of freedesktop.org menus where the capital is anyway added to follow the standard. ↩︎
The fact that bugfixes systematically add more lines of code instead of modifying existing lines is a a concerning smell that the programming logic is bad and induces too many particular cases. Rigorous programmers always try to keep their code as generic as possible to avoid spaghetti code . ↩︎
You don’t need to trust me, the command to reproduce the stats is
git diff release-3.4.0..release-3.6.0 --shortstat -w -G'(^[^\*# /])|(^#\w)|(^\s+[^\*#/])' -- '*.c' '*.h' '*.cpp' '*.xml.in' '*.xsl' '*CMakeLists.txt' '*.cl'↩︎git checkout release-3.0.0 & cloc $(git ls-files -- 'src/views' 'src/gui' 'src/bauhaus' 'src/dtgtk' 'src/libs')↩︎