Ansel jest projektowany, a nie sklecony na kolanie. Hakerzy mogą czerpać przyjemność z „pracy" nad przyspieszaniem upadku Darktable poprzez zwiększanie jego długu technicznego .

Czym jest projektowanie?

Projektowanie to proces, w którym rozwijasz metodologię, aby dostarczyć techniczne rozwiązanie ludzkiego problemu. Proces projektowania ma na celu zbieżność ku najbardziej odpowiedniemu rozwiązaniu, przy jednoczesnym zwalczaniu naturalnej pokusy rzucenia się na pierwszy lub najwygodniejszy pomysł.

Bez wymagań i projektu programowanie jest sztuką dodawania błędów do pustego pliku tekstowego. — Louis Srygley
  1. Projektowanie zaczyna się od przypadku użycia: zdefiniowanego zadania do wykonania (na zdjęciu), przez zdefiniowanego użytkownika, w zdefiniowanym czasie. Jeśli nie ma przypadku użycia, to nie ma problemu do rozwiązania, więc trzymaj się z dala od swojego edytora kodu.
  2. Projektowanie wymaga znajomości docelowego użytkownika: wykształcenia/wyszkolenia, poziomu rzemiosła/mistrzostwa itd.
  3. Projektowanie wymaga zrozumienia potrzeb: w kontekście Ansela często będzie to wymagać pewnej wiedzy z historii sztuki i fotografii ciemniowej,
  4. Gdy problem i użytkownik są zrozumiani, projektowanie wymaga określenia:
    • oczekiwanych funkcjonalności rozwiązania,
    • zakresu rozwiązania (w którym miejscu cyklu życia obrazu znajduje się to rozwiązanie?),
    • ograniczeń i wymagań rozwiązania (obsługa jakiegoś standardu, umożliwienie przetwarzania n obrazów na jednostkę czasu itd.),
    • serii testów do przeprowadzenia, które potwierdziłyby jakość rozwiązania, aby ograniczyć nieproduktywne opinie, uprzedzenia i subiektywność w procesie walidacji.
  5. Dopiero wtedy mogą rozpocząć się makiety i burza mózgów, a następnie prototypy.

Czym projektowanie nie jest?

Projektowanie nie zajmuje się:

  • niejasnymi wymaganiami niezdefiniowanego, przyszłego lub wyfantazjowanego użytkownika,
  • „byłoby fajnie, gdyby…" (tak właśnie tworzy się niespójne kolekcje wtyczek),
  • tym, co ludzie lubią (dla wszystkiego, co ktoś lubi, znajdziesz kogoś, kto tego nienawidzi),
  • tym, co ludzie myślą, że chcą (często nie jest to to, czego potrzebują),
  • magicznymi technologicznymi hasłami-wytrychami, które „są przyszłością" i jako takie muszą być wpychane wszędzie, niezależnie od ich zasadności czy wykonalności (tak, mówię o AI, NFT, blockchainie itd.)

Czym jest dobre projektowanie

Dobre projektowanie jest:

  • minimalistyczne,
  • solidne,
  • odporne na przyszłość,
  • generyczne i uogólnione,
  • utrzymywalne przy ograniczonych zasobach,
  • oparte na nauce,
  • kompatybilne/interoperacyjne ze standardami branżowymi.

Ponieważ Ansel jest aplikacją opartą na przepływie pracy, dobre projektowanie uwzględnia też przepływ pracy jako całość oraz to, gdzie problem/rozwiązanie się w nim znajduje.

Jak wykonuje się dobre projektowanie?

Aby wspomóc proces projektowania, komunikacja powinna pozostawać zwięzła, skupiona na temacie, a osoby biorące udział w tym procesie powinny zadbać o właściwe zrozumienie teorii i tła technicznego związanego z zakresem problemu/rozwiązania.

Należy podkreślić, że choć projekt jest napędzany oprogramowaniem, nie wszystkie rozwiązania wymagają kodowania. Czasami (często?) wystarczy lepsza edukacja lub lepsza dokumentacja.

Celem zdrowego procesu projektowania jest uniknięcie zbyt wczesnego obciążania rozwiązań czyjąś ulubioną koncepcją projektową/technologią oraz uniknięcie zagubienia się w szczegółach technicznych, a zamiast tego stałe powracanie do podstawowych fundamentów i zasad tego, co robimy: obróbki potencjalnie dużych partii obrazów raw dla wszelkiego rodzaju mediów wyjściowych.

Potwierdza to fakt, że użytkownicy rzadko znają swoje własne potrzeby, a raczej potrzeby, które wyrażają, rzadko są źródłem tego, czego naprawdę chcą. Trudnym zadaniem projektowania jest przebicie się przez gałęzie, by dotrzeć do korzenia, ponieważ rozwiązanie problemu u korzenia zazwyczaj kończy się bardziej eleganckimi, generycznymi i minimalistycznymi rozwiązaniami.

Problemy są pierwsze

Pierwszym krokiem procesu projektowania Ansela jest zgłoszenie propozycji funkcji na Community. Propozycje funkcji zostały przeniesione z Githuba, ponieważ ta platforma jest nieprzyjazna dla nieprogramistów i osób nieanglojęzycznych (choć Community obsługuje tylko francuski i angielski).

Taka propozycja funkcji skupi się na problemie do rozwiązania i powstrzyma od proponowania jakiegokolwiek rozwiązania. Problem zostanie zdefiniowany w kategoriach zadań do wykonania w przepływie pracy fotografa lub oczekiwanego wizualnego efektu przetworzonego obrazu, czyli w kategoriach celu końcowego do osiągnięcia, a nie w kategoriach narzędzi czy szczegółów technicznych uważanych za potrzebne. Może to prowadzić do dyskusji, aby dokopać się do korzeni problemu, które zwykle są dobrze ukryte pod tym, co użytkownik uważa za swój problem .

Na tym etapie nie przyjmuje się żadnej propozycji rozwiązania.

Rozwiązania są drugie

Gdy definicja i zakres problemu zostaną uzgodnione między osobami zaangażowanymi w dyskusję, można proponować rozwiązania. Może być potrzebna dalsza dyskusja, aby ocenić wady i zalety każdego rozwiązania, prowadząca do przyjęcia najlepszego rozwiązania z zasady. Rozwiązania są definiowane przez swoje funkcjonalności (czyli to, co powinny robić), a nie przez swoją technologię czy środki (jak powinny to robić).

Na tym etapie nie przyjmuje się żadnej propozycji prototypu.

Przyjęte rozwiązania doprowadzą do utworzenia nowego zgłoszenia poddawanego selekcji na tablicy Kanban zarządzania projektem .

Mogą one być uwarunkowane zbadaniem aspektów teoretycznych i technicznych w celu oceny ich wykonalności, w którym to przypadku zostaną poddane selekcji do kolumny To research tablicy Kanban. Wyniki badań będą dodawane do pierwotnego zgłoszenia, dopóki wykonalność rozwiązania nie zostanie udowodniona. Gdy to nastąpi, zgłoszenie zostanie przeniesione do kolumny „To do" tablicy Kanban.

Przyjęte rozwiązania mogą zostać poddane selekcji bezpośrednio do kolumny To do, jeśli wymagają jedynie dobrze znanych narzędzi i technologii.

Idealnie punkty do przetestowania oraz procedura testowa walidująca prototyp powinny zostać spisane jeszcze przed posiadaniem działającego prototypu. Co najmniej testy powinny zapewnić, że nie wystąpiła regresja w powiązanych funkcjach i narzędziach.

Prototypy są trzecie

Pracowane będą tylko zgłoszenia poddane selekcji do kolumny „To do" tablicy Kanban zarządzania projektem , przeze mnie lub przez kogokolwiek chętnego się nimi zająć.

Prototyp rozwiązania zostanie zaproponowany w pull requeście gałęzi tematycznej powiązanej z pierwotnym zgłoszeniem. Gałęzie tematyczne muszą być rebasowane na gałęzi master, np. git rebase ustream master lub, jeśli aktualizujesz swoją gałąź lokalnie o nowe commity z mastera, wykonaj git pull upstream master --rebase albo globalnie skonfiguruj git , aby pobierał przez rebase zamiast merge. Zapewnia to utrzymanie czystej historii Twojej gałęzi przy minimalnym wysiłku i utrzymuje czystą również historię master, gdy Twój PR zostanie scalony.

Gdy pull request z prototypem zostanie zrecenzowany i jeśli spełnia standardy jakości kodu (patrz niżej), a jednocześnie odpowiada specyfikacjom przyjętego rozwiązania, zostaje zatwierdzony i automatycznie poddany selekcji do kolumny „To test/validating" tablicy Kanban zarządzania projektem .

Walidacja jest czwarta

Zatwierdzone pull requesty będą scalane wcześnie w gałęzi candidate lub dev do testów, w zależności od tego, czy mogą zepsuć historie edycji obrazów (poprzez dodanie nowych parametrów modułów lub zmianę schematu bazy danych). Ta gałąź będzie zawsze gałęzią master ze wszystkimi pull requestami oczekującymi na walidację na wierzchu. Ma to pomóc w testowaniu osobom, które niekoniecznie są zaznajomione z ręcznym scalaniem gałęzi git. W przeciwieństwie do gałęzi dev, candidate nie powinna psuć Twoich edycji.

Jeśli po pewnym czasie nie zostanie zgłoszony żaden błąd ani awaria, a prototyp poprawnie spełnia swój pierwotny cel, zostanie scalony w master, a powiązane zgłoszenie zostanie zamknięte i przeniesione do kolumny „Done" tablicy Kanban zarządzania projektem .

Jeśli prototyp okaże się niezadowalający, może zostać odrzucony i trzeba będzie opracować kolejny.

Wskazówki od doświadczonego projektanta

Nie wszystkie problemy oprogramowania są problemami kodowania

Wiele problemów nie wymaga więcej narzędzi (ani zabawek) ani więcej kodu. Więcej kodu i tak jest zawsze złe i należy go unikać, kiedy tylko możliwe. Bardzo często problemem użytkownika jest to, że nie widzą, jak nagiąć istniejące funkcje, by zaspokoić swoje potrzeby. Rozwiązuje się to edukacją, czyli lepszą dokumentacją i większą liczbą samouczków, a czasami lepszym interfejsem użytkownika.

Słuchaj, ale nie słuchaj użytkowników

Użytkownicy wyrażają to, czego chcą i co lubią, nigdy tego, czego potrzebują. I nie musisz ich słuchać, żeby wiedzieć, co to będzie:

  1. będą chcieć tego samego co ich sąsiad właśnie dostał,
  2. będą lubić to, do czego są przyzwyczajeni.

A potem, dla wszystkiego, co jeden lubi, znajdziesz innego, który tego nie lubi. Tak więc darktable’owy sposób rozwiązywania konfliktu polega na tym, by konfliktu nie rozwiązywać, lecz dać każdemu opcję, tryb, preferencję pozwalającą włączyć tę konkretną rzecz, którą lubi, tak jak ją lubi. Oznacza to więcej case w Twoim switch, więcej zagnieżdżonych if, więcej ścieżek kodu, które będziesz musiał teraz testować, debugować i utrzymywać w przyszłości, a następnie więcej preferencji przesłaniających inne w oknie preferencji. Zanim się obejrzysz, kod jest guzem, którego nikt już nie rozumie, a jego naprawianie tylko go komplikuje.

Gdy podrapiesz pod powierzchnią, odkryjesz, że to, czego ludzie naprawdę potrzebują, jest znacznie bliższe potrzebom innych ludzi niż to, co mówią, że chcą. Możesz więc pogodzić potrzeby znacznie łatwiej niż pragnienia i bez kompromisów. Ale wtedy musisz prześledzić korzenne potrzeby leżące pod wolą, a to wymaga umiejętności abstrakcyjnego myślenia i psychologii.

Projektanci interfejsów to niebezpieczni idioci

Każdy, kto widzi, skupia się i dba tylko o interfejs użytkownika, jest niebezpiecznym idiotą. Jeśli Twój graficzny interfejs jest skomplikowany, oznacza to znacznie więcej niż tylko „skomplikowany interfejs": oznacza to, że złożoność Twojego zaplecza dotarła do Twojego frontendu. Przekonałem się w trudny sposób, że złożoność interfejsu graficznego nigdy nie jest odrębna i nie da się jej rozwiązać oddzielnie od złożoności zaplecza i ogólnej architektury aplikacji. Interfejs graficzny nie jest równoległy do architektury zaplecza, jest jego zakończeniem.

Problemem projektantów interfejsów jest to, że zazwyczaj nie kodują, a jeśli już, to są słabi w programowaniu niskopoziomowym i architekturze oprogramowania. Skupiają się więc na tej odrobinie, którą widzą i rozumieją (typowy efekt latarni ) i produkują jedynie niewykonalne projekty, które kolidują z tym, czego oprogramowanie faktycznie potrzebuje do działania. Ponieważ ten interfejs graficzny tylko łączy dane wejściowe użytkownika z zapleczem, a jeśli potrzebujemy aż tylu widżetów, to dlatego, że architektura potrzebuje aż tylu danych wejściowych. Nie da się od tego uciec: aby usunąć widżety, musisz usunąć dane wejściowe, co oznacza, że Twoja architektura będzie musiała najpierw pracować z mniejszą liczbą stopni swobody. Zaczyna się to od uproszczenia zaplecza, co oznacza cuchnący refaktoring zakurzonego starego kodu, którego nikt już nie rozumie.

Problemów interfejsu graficznego nie rozwiązuje się rysunkami i makietami, problemy interfejsu graficznego rozwiązuje się rozwiązując problemy zaplecza. Ale wtedy potrzebujesz ludzi, którzy rozumieją oba poziomy, a oni mogą być dla Ciebie zbyt drodzy.

Zadawaj sobie 36 razy dziennie pytanie, jaki problem próbowałeś rozwiązać

Bardzo łatwo zgubić się w szczegółach technicznych, programując w języku niskopoziomowym i walcząc z zewnętrznymi bibliotekami czy API, ale czasami rozwiązanie jest proste i eleganckie, a Ty dałeś się ponieść zbyt daleko we wskaźniki i blokady wątków. Zawsze wracaj do początkowego problemu, który masz przed sobą — to Twoja lina ratunkowa do prostoty.

Jaki jest problem? Kto się z nim mierzy? Kiedy? Jak często? Robiąc co?

Najlepsza droga to najprostsza droga do Twojego rozwiązania: proste technologie, mało kodu, niewiele warstw.

Udokumentuj swój kiepski projekt

Wiele razy całkowicie przerabiałem projekt podczas jego dokumentowania, ponieważ to właśnie wtedy, gdy próbujesz go wyjaśnić, uświadamiasz sobie, że jest zbyt skomplikowany, by go wyjaśnić, co oznacza, że jest zbyt skomplikowany, by go zrozumieć. Jeśli nie potrafisz wyjaśnić swojego projektu w kilku akapitach albo Twoja dokumentacja ma zbyt wiele „jeśli to, to tamto", zazwyczaj są dwa powody:

  1. Twój interfejs graficzny nie eksponuje istotnych informacji tam, gdzie użytkownik ich potrzebuje, więc musisz linkować połowę dokumentacji w swoim wyjaśnieniu, aby przekierować użytkowników do wszystkiego, co muszą wiedzieć lub sprawdzić przed użyciem tej jednej rzeczy, którą dokumentowałeś. Rozwiązaniem jest przywrócenie istotnych informacji tam, gdzie są potrzebne.
  2. Twój interfejs graficzny ma zbyt wiele tacek, zwijanych elementów, zachowań kontekstowych, przypadków użycia lub ukrytych preferencji, a pokrycie wszystkich przypadków zmusza Cię do napisania powieści. Rozwiązaniem jest zlinearyzowanie przepływu pracy, może usunięcie opcji lub podzielenie funkcji.

Interfejs graficzny to sposób, w jaki użytkownicy kontrolują zaplecze, ale to również miejsce, gdzie dowiadują się o istniejących funkcjach i o tym, co robią. Dokumentacja powinna dostarczać kontekstu, wytycznych i odniesień dotyczących tego, jak robimy różne rzeczy, ale interfejs graficzny powinien sam wyjaśniać, co robi.

Oczywiście istnieje tu oczywiste ograniczenie: w aplikacji fotograficznej użytkownicy muszą rozumieć fotografię i jej język, który obejmuje takie rzeczy jak zakres dynamiczny, gamut barwny, mapowanie tonalne itd. Interfejs graficzny powinien być samoobjaśniający się w kwestii tego, jak ma być używany, a nie usuwać potrzeby nauczenia się rzemiosła (co powinno być zrobione i jak).

Projektowanie jest procesem iteracyjnym

Aplikacja to wirtualny świat, w którym jedna mała zmiana może na nowo uporządkować to, jak reszta ekosystemu dostosowuje się wokół niej. Dlatego każda zmiana projektowa może wywołać potrzebę zmiany innych rzeczy wokół (refaktoryzacji narzędzi, przesunięcia widżetów, przycięcia funkcji). Co następnie może wywołać potrzebę ponownego skorygowania początkowej zmiany. To proces krok po kroku, w którym głupotą jest choćby próbować zrobić wszystko idealnie na każdym kroku; liczy się to, by każdy krok ulepszał środowisko względem poprzedniego.

Czasami (prze)projektowanie nie może być zrobione małymi krokami, lecz dużymi skokami: to wtedy, gdy przerabiasz architekturę. Te skoki popsują wiele rzeczy wokół siebie, co jest w porządku, jeśli nowsza architektura jest ogólnie prostsza i solidniejsza, oraz jeśli dasz jej trochę czasu na dojście do siebie, zanim znów sięgniesz po młot. Ale stworzy to stan przejściowy, w którym nowy projekt będzie wydawał się gorszy od poprzedniego. Mówi nam to, że to, jak projekt jest postrzegany, nie jest prawidłowym kryterium: jakość projektu należy oceniać względem jego celów i ewaluować za pomocą obiektywnych miar, a nie odczuć i szybkich testów.

A czasami niektóre kroki to błędy i należy je cofnąć. Błąd utopionych kosztów  nie powinien być używany do uzasadniania, że jakieś przeprojektowanie należy zachować, bo jego wykonanie kosztowało wiele pracy. Należy oczekiwać, że nie wszystkie badania i rozwój trafiają do produkcji.

Sesje w stylu „uderz kreta" oznaczają, że Twoja architektura wyczerpała swoje możliwości

Czy to ciągle tworzysz nowe błędy podczas naprawiania starych, czy ciągle tworzysz przypadki brzegowe rozszerzając jakąś funkcję, wszystko to wskazuje w tym samym kierunku: Twojej architektury nie da się już bardziej nagiąć, ponieważ przerosła swoje wymagania projektowe. Może być tak, że zaplecze stało się zbyt zawiłe, albo tak, że istniejąca architektura naprawdę nie była zaplanowana do tego, co próbujesz nią osiągnąć, ale w obu przypadkach będziesz musiał przerobić architekturę i przestać kombinować na miejscu. W przeciwnym razie tylko dokładasz dług techniczny.

Ale wtedy koszt rozwoju zmienia skalę i ten sobotnio-popołudniowy projekt może stać się projektem na cały miesiąc.

Najlepsze praktyki to wytyczne, a nie reguły

Najlepsze praktyki pomagają wyrobić zdrowe nawyki i pisać czysty kod, chyba że nie rozumiesz problemu, który próbowały rozwiązać, i używasz ich poza ich zakresem ważności. W takim przypadku stają się kultem cargo: próbą naśladowania skutków w nadziei, że magicznie naprawi to również przyczyny.

Pierwsze, co przychodzi na myśl, to ponowne wykorzystanie/współdzielenie kodu. Jeśli ponowne wykorzystanie tego samego kodu dla (pozornie podobnych) funkcji prowadzi do zbyt wielu wewnętrznych rozgałęzień (zagnieżdżonych if, switch/case itd.), aby obsłużyć wszystkie możliwe ścieżki, to co zyskujesz na objętości kodu, tracisz na złożoności cyklomatycznej, a przy okazji Twoje funkcje nie są tak podobne, jak myślałeś.

Ponadto duplikowanie kodu może być punktem wyjścia do lokalnej optymalizacji duplikatu później: gdy masz przed sobą całą procedurę, możesz dostrzec kroki, które można zbuforować lub sfaktoryzować. Podczas gdy jeśli procedura to tylko nieprzejrzyste, wysokopoziomowe, wielokrotnego użytku metody API, to tracisz zdolność dostrzegania i usuwania zbędnych obliczeń. Istnieje więc zasada ponownego wykorzystania/współdzielenia danych (czyli buforowania obliczonych danych, które zostaną użyte później bez zmian, aby oszczędzić cykle procesora), którą ponowne wykorzystanie/współdzielenie kodu może uczynić niemożliwą, ponieważ zaciemnia i abstrahuje cykl życia danych.

Staje się to krytyczne w pętlach pikselowych: chcesz zwinąć wszystkie operacje pikselowe do tej samej pętli, aby zapłacić cenę wejścia/wyjścia pamięci tylko raz. Oznacza to, że będziesz musiał ponownie zaimplementować tę samą korektę afiniczną ($y = a * x + b$) w każdej używającej jej pętli, zamiast mieć metodę wielokrotnego użytku, która robi dokładnie to w swojej własnej pętli.

Nie usztywniaj się

Jeśli czujesz się przytłoczony jakimiś tajemniczymi i losowymi błędami, które ciągle się pojawiają i których nie potrafisz zrozumieć, nie walcz dalej na oślep, tylko zrób krok wstecz. Następnie wyposaż kod w pomocnicze narzędzia debugujące lub wyższego poziomu menedżery, które śledzą stany wewnętrzne i dają Ci mapę wartości danych oprogramowania w każdym istotnym punkcie jego cyklu życia. Jest to szczególnie krytyczne w konfiguracjach asynchronicznych, gdzie kilka wątków tworzy, uzyskuje dostęp lub oblicza rzeczy równolegle na różnych osiach czasu, a rzeczywista kolejność sekwencji zależy od kontekstu wykonawczego.


Translated from English by : Claude. In case of conflict, inconsistency or error, the English version shall prevail.