Lá em dezembro de 2019, eu pedi que alguém se encarregasse de fornecer pacotes AppImage para o Darktable. O benefício óbvio teria sido possibilitar testes precoces, antes da versão, por parte de pessoas que não conseguem compilar o código-fonte por conta própria, de modo a quem sabe fornecer feedback antecipado e ajudar a depurar antes de lançar. Isso nunca foi uma prioridade, o que significa que estava tudo bem ter uma corrida para corrigir bugs tanto antes quanto depois da versão.
Eu não estava brincando quando disse que o Darktable era uma fábrica de burnout, gerenciada como as piores start-ups, exceto que ninguém vai fazer um exit e é basicamente uma perda seca para todos os envolvidos. Fico ainda mais irritado com isso porque muitas horas-homem foram desperdiçadas atulhando a UI com cosméticos, quando ter builds noturnas para uso geral teria melhorado tanto a qualidade do software quanto o conforto e a qualidade de vida de seus mantenedores. Esses objetivos obviamente não importavam.
Então, já que ninguém é melhor servido do que por seus próprios bugs, eu tive que fazer isso eu mesmo , e em meados de dezembro de 2022 o Ansel ganhou scripts de builds noturnas AppImage que o Darktable reutilizou um mês depois. Com as coortes de caras de TI “sem habilidades em matemática de pixels” rondando o hype do Darktable e perguntando como poderiam ajudar, é claro que o melhor gerenciamento de recursos possível era desviar horas-homem de um dos pouquíssimos caras capazes de matemática disponíveis para uma tarefa puramente de TI. Isso só me tomou algo em torno de 50 horas, porque ter um script funcionando localmente realmente não é o mesmo que sussurrar scripts YAML aos ouvidos do Github Action, e eu tive que aprender isso enquanto fazia. Já que o Ansel leva de 20 a 30 minutos para compilar (ou falhar) nas instâncias do Github Action, seu dia de trabalho é uma longa sessão entrecortada de multitarefa ineficiente, enquanto você espera que suas mudanças no script rendam um resultado remoto. Eu odeio programar, e me viro pela matemática e pela física, mas esse tipo de configuração braçal de servidor é realmente o pior tipo de programação que existe, na medida em que não é nem criativo nem cognitivamente desafiador, é apenas uma maneira demorada de contornar as limitações de design de APIs de terceiros.
Então, finalmente, o Ansel era um projeto de software semiorganizado, capaz de incluir uma base de usuários maior do que apenas os geeks fanáticos que menosprezam todos que não conseguem colocar o GCC e o CMake em ordem de compilação, com um grande botão “download” na página inicial que sempre apontaria para a build mais recente, sem que os usuários tivessem que descobrir qual era a mais recente. Porque um software de edição de fotos não deveria filtrar usuários com base em seu letramento computacional, quer isso venha do mundo Linux (o que realmente não é desculpa) ou existe algum memorando da Fundação do software livre que eu não recebi.
Mas a última grande questão era a falta de uma documentação para desenvolvedores. Lá em 2018, quando comecei a contribuir com o Darktable, tive muita dificuldade em entender como as entranhas estavam interligadas, apenas lendo o código. 7 anos depois, mesmo com toda a minha experiência, ainda tenho que fazer engenharia reversa através do código, usando tanto o grep quanto a minha intuição, porque o código não é modular, poucos lugares usam APIs, faltam comentários, e o Git blame linha por linha nem sempre permite voltar no histórico de mudanças de design quando alguém fez commit de mudanças (inúteis) de formatação de código.
Essa falta de documentação para desenvolvedores levou muitos recursos a serem implementados mais de uma vez, em vários lugares, por vários desenvolvedores (e às vezes até pelo mesmo, ao longo dos anos). No momento em que escrevo, ainda há 4 ou 5 maneiras diferentes, no Ansel, de gravar um histórico de imagem do banco de dados da biblioteca em um arquivo sidecar XMP. Algumas dessas maneiras são raramente usadas, portanto mal testadas, e bugs ali poderiam passar despercebidos por anos, até que alguém relate o bug superpeculiar escondido na floresta de opções. Então, para o mantenedor encarregado, é um jogo tolo de descobrir por que o XMP falha apenas em algumas circunstâncias, o que exige arqueologia na base de código para descobrir que ele não usa o método principal de gravação de XMP. O XMP é um exemplo específico, e ainda atual, mas havia muitos outros. Você entendeu a ideia.
Eu havia notado, por vários anos, que os arquivos de cabeçalho C do (antigo) Darktable tinham praticamente todos docstrings Doxygen . Essa é uma maneira muito preguiçosa de fazer documentação: execute o comando doxygen -g <config-file> contra o diretório do seu código-fonte, e o Doxygen vai construir para você um site estático em HTML de todas as APIs. Depois você pode despejar todos os arquivos HTML em um diretório de servidor web, e chamar isso de sua documentação de dev : a única parte tediosa é escrever o arquivo de configuração, o que você faz apenas uma vez. Então foi o que fiz: dev.ansel.photos. As APIs que reescrevi (como selection.h) são documentadas conforme eu avanço. Descrições de mais alto nível da arquitetura do software estão a caminho.
A grande vantagem do Doxygen é que ele também produz grafos de dependência de “módulos” e APIs. E é aí que você pode realmente ver por que eu chamo o código do Darktable de espaguete. Este é o grafo de dependência de accelerators.h, o backend de atalhos de teclado e MIDI:

Isso mostra que o backend de aceleradores não é absolutamente modular: ele herda o software inteiro. Então qualquer mudança em outro lugar pode ter efeitos imprevistos ali, e vice-versa. O que é ainda mais preocupante, já que este arquivo tem a maior complexidade ciclomática de todo o software , o que o torna o arquivo mais desafiador de manter (eu nem me atrevo a dizer estender, a essa altura seria irresponsável). Mas ele também é completamente distorcido em termos de direção dos includes : atalhos são um bloco básico que deveria ser incluído (ou seja, herdado) nos lugares da GUI que implementarão atalhos (visões sala escura/mesa de luz, sliders e comboboxes, módulos). Em vez disso, o grafo (e o cabeçalho #include do arquivo) mostra que os atalhos também estão incluindo (herdando) seus “filhos”, então temos uma dependência dupla e essa é a pior maneira possível de fazê-lo.
É como construir uma casa : a casa deveria ter ciência de suas paredes, as paredes deveriam ter ciência de seus tijolos. Por quê ? Porque a casa é feita de paredes, e as paredes são feitas de tijolos, então cada subcomponente determina a natureza e o comportamento do conjunto, portanto o conjunto tem que conhecer seus componentes imediatos. Você não faz os tijolos terem ciência da casa, porque eles não vão mudar sua natureza dependendo do conjunto ao qual pertencem, e isso seria um design terrivelmente falho. Da mesma forma que é um nível desnecessário de microgerenciamento fazer a casa ter ciência dos tijolos: uma vez que ela tem ciência de suas paredes, cabe às paredes dar conta do comportamento de seus tijolos, e talvez repassar informações relevantes à casa. Linguagens orientadas a objetos têm maneiras embutidas (e obrigatórias) de lidar com tudo isso de forma limpa. Mas é C, então você pode fazer a porra que quiser fazer. Não significa que seja uma boa ideia, não significa que você deveria. E, bem, C não ser uma linguagem intrinsecamente orientada a objetos não é um obstáculo para usar padrões de objeto, herança e modularidade. É só que o desenvolvedor não terá nenhuma ajuda da sintaxe da linguagem para fazê-lo.
Este é o mesmo arquivo após a minha reescrita completa do backend de atalhos:

Isso mostra claramente que o novo manipulador de atalhos é um invólucro fino sobre os atalhos nativos do Gtk, ele não conhece o resto do software e não se importa. Podemos olhar o arquivo accelerators.c também:

Agora, há muitos includes ali de darktable.h, que é usado apenas para obter os auxiliares de depuração (eles deveriam ser refatorados para fora desse arquivo), e inclui muita porcaria desnecessária também. De qualquer forma, accelerators.c tem ciência apenas do Gtk/Gdk, o que significa que ele é um módulo de verdade: está completamente isolado do resto do software. Widgets da GUI que implementam atalhos declararão seu caminho de atalho, como Ansel/Global/Menu/File/Import, suas teclas padrão e uma referência (ponteiro) a si mesmos. Ações sem widget que têm atalhos declararão uma função de callback e dados de entrada para o manipulador de atalhos, em vez de um ponteiro para um widget.
Nós salvamos/restauramos as associações de caminhos e teclas de/para o arquivo keyboardrc, e é isso. Não há uma janela para definir atalhos na GUI no momento, mas fazer uma só terá que listar (iterar sobre) os caminhos conhecidos e suas teclas associadas. Quando o manipulador de atalhos captura uma combinação conhecida de teclas:
- para as ações vinculadas a widgets, ele enviará um sinal
activatedo Gtk ao widget relevante, e esse widget fará a sua própria coisa através de um callback (que é o mesmo callback que trata os cliques, então o código é uniforme entre cliques e ativação por teclado), - para as ações sem widget, ele chamará diretamente a função de callback declarada sobre os dados declarados.
De ambas as formas, o módulo accelerators se comunica com o resto da aplicação através de uma interface que troca 4 campos de dados, de maneira completamente opaca. Enquanto a interface não mudar, mudanças podem ser feitas em todos os lugares: elas permanecerão encerradas dentro de seu módulo. Com as habilidades adequadas, essa lógica poderia ter sido estendida para suporte a dispositivos MIDI. Mas essa é a diferença entre engenharia e prototipar provas de conceito que nunca deveriam chegar à produção.
Isso é, infelizmente, uma marca registrada do jeito Darktable de fazer as coisas: código de GUI está incrustado em todos os lugares, até em código SQL. E vice-versa, código SQL é encontrado em muitos lugares da GUI (mesa de luz, gerenciamento de histórico, marcação de imagens, etc.). Os grafos de dependência, embutidos no Doxygen, são um efeito colateral muito bacana que mostra a profundidade do problema e torna o espaguete muito óbvio, porque, aparentemente, os problemas não existem até que você realmente os veja com seus próprios olhos.
Mas não para por aí. Enquanto eu escrevia meu próprio arquivo de configuração do Doxygen, recebi um erro: o Doxygen relatou 2 arquivos de configuração e ficou confuso. Acontece que o Darktable tinha tudo interligado para geração automatizada de documentação para desenvolvedores desde 2010 . Por que isso nunca foi posto em produção, e de fato hospedado em algum servidor, não é meramente prioridades erradas: é negligência.
É claro, não ajuda que o cara dono do domínio darktable.org não seja o mesmo que gerencia o servidor onde ele é de fato hospedado. E o cara que tem os direitos de commit reais sobre o site darktable.org é ainda outro. A coisa mais urgente a fazer, quando você é um projeto de código aberto que fatura zero dólar de receita, é reproduzir todos os erros do mundo corporativo, desde a pressão para lançar merda mal-acabada numa frequência irresponsável, até a pulverização de responsabilidades entre “serviços” que na verdade não se comunicam uns com os outros (ou com grandes atrasos). Porque, em software (seja de código aberto ou não), os erros são feitos para serem reproduzidos. E já que o software tomou conta do mundo, até em lugares que não precisavam dele, isso diz muito sobre o mundo em que vivemos.
A única coisa que eu ainda realmente não entendo, porém, é : por que a urgência ? Por que continuar trabalhando tão mal, dado que não há pressão nem incentivo financeiro para tal ? O código aberto é (poderia ter sido) o único lugar onde poderíamos de fato trabalhar direito e tomar o tempo necessário para produzir qualidade de longo prazo. E mesmo ali, a miopia do capitalismo tomou conta.
Se os usuários ao menos soubessem em que estado de merda profunda este projeto caiu e como todas as horas-homem investidas nele estão ativamente piorando-o…
Note
A documentação para desenvolvedores agora é gerada automaticamente e enviada para dev.ansel.photos todo domingo à meia-noite, a partir dos comentários (docstrings) no código.Translated from English by : Claude. In case of conflict, inconsistency or error, the English version shall prevail.