Ansel wird designt, nicht gehackt. Hacker mögen es genießen, “zu arbeiten” an dem beschleunigten Untergang von Darktable, indem sie seine technische Schulden erhöhen.
Was ist Design?
Design ist ein Prozess, bei dem eine Methodik entwickelt wird, um eine technische Lösung für ein menschliches Problem zu finden. Der Designprozess soll zur passendsten Lösung führen und gleichzeitig dem natürlichen Drang widerstehen, sich vorschnell auf die erste oder bequemste Idee zu stürzen.
Ohne Anforderungsdefinition und Design ist Programmierung die Kunst, Bugs in eine leere Textdatei einzufügen.
- Design beginnt mit einem Use-Case: eine definierte Aufgabe, die von einem bestimmten Benutzer in einem bestimmten Zeitrahmen erreicht werden soll. Gibt es keinen Use-Case, dann gibt es kein zu lösendes Problem, also halte dich von deinem Code-Editor fern.
- Design erfordert Kenntnisse über den Zielbenutzer: Ausbildung/Training, Niveau von handwerklichem Können/Mastership, etc.
- Design erfordert, die Bedürfnisse zu verstehen: Im Kontext von Ansel wird oft Kunstgeschichte und Kenntnisse der Dunkelkammerfotografie benötigt,
- Sobald das Problem und der Benutzer verstanden sind, muss Design spezifizieren:
- die erwarteten Funktionalitäten der Lösung,
- den Umfang der Lösung (wo in der Lebensdauer eines Bildes steht die Lösung?),
- die Einschränkungen und Anforderungen der Lösung (Unterstützung von Standards, Möglichkeit zur Verarbeitung von n Bildern pro Zeiteinheit, etc.),
- eine Reihe von Tests, die zur Validierung der Qualität der Lösung abgeschlossen werden müssen, um unproduktive Meinungen, Vorurteile und Subjektivität im Validierungsprozess zu begrenzen.
- Erst dann können die Entwürfe und das Brainstorming beginnen, gefolgt von Prototypen.
Was ist Design nicht?
Design befasst sich nicht mit:
- vagen Anforderungen eines undefinierten, zukünftigen oder fantasierten Benutzers,
- “es wäre cool, wenn…” (so erstellt man inkonsistente Plugins-Sammlungen),
- was die Leute mögen (für alles, was jemand mag, wird man jemand finden, der es hasst),
- was die Leute denken, dass sie wollen (es ist oft nicht das, was sie brauchen),
- magischen Tech-Buzzwords, die “die Zukunft sind” und daher überall eingebaut werden müssen, unabhängig von ihrer Relevanz oder Machbarkeit (ja, ich spreche von KI, NFT, Blockchain etc.)
Was ist gutes Design?
Gutes Design ist:
- minimalistisch,
- robust,
- zukunftssicher,
- generisch und verallgemeinert,
- mit begrenzten Ressourcen wartbar,
- durch Wissenschaft informiert,
- kompatibel/interoperabel mit Industriestandards.
Da Ansel eine Workflow-basierte Anwendung ist, berücksichtigt gutes Design auch den Workflow als Ganzes und wo sich das Problem/die Lösung darin befindet.
Wie wird gutes Design erstellt?
Um den Designprozess zu unterstützen, sollte die Kommunikation prägnant und fokussiert bleiben, und die an diesem Prozess beteiligten Personen sollten sicherstellen, dass sie ein angemessenes Verständnis der Theorie und des technischen Hintergrunds im Bezug auf den Problem- und Lösungsscope haben.
Es sollte betont werden, dass, obwohl das Projekt softwaregesteuert ist, nicht alle Lösungen das Coden beinhalten. Manchmal (oft?) reicht eine bessere Ausbildung oder Dokumentation aus.
Der Zweck eines gesunden Designprozesses ist es, zu vermeiden, dass die Lösungen zu früh mit einem bevorzugten Design/Technik beeinflusst werden und sich in den technischen Details zu verlieren, sondern immer zu den Grundprinzipien zurückzukehren: die Nachbearbeitung möglicherweise großer Stapel von Rohbildern für alle Arten von Ausgabemedien.
Dies wird dadurch gestützt, dass Benutzer selten ihre eigenen Bedürfnisse kennen, oder vielmehr, die geäußerten Bedürfnisse selten der Kern dessen sind, was sie eigentlich wollen. Die schwierige Aufgabe des Designs besteht darin, durch die Äste zu schneiden, um zur Wurzel zu gelangen, da die Lösung des eigentlichen Problems normalerweise zu eleganteren, generischeren und minimalistischeren Lösungen führt.
Probleme kommen zuerst
Der erste Schritt im Ansel-Designprozess besteht darin, einen Feature-Request im Community Forum einzureichen. Feature-Requests wurden von Github auf das Forum verlegt, da diese Plattform nicht einladend für Nicht-Programmierer und Nicht-Englischsprachige ist (obwohl das Forum nur Französisch und Englisch unterstützt).
Dieser Feature-Request konzentriert sich auf das zu lösende Problem und unterlässt es, eine Lösung vorzuschlagen. Das Problem wird in Bezug auf Aufgaben im Workflow eines Fotografen oder den erwarteten visuellen Ausgang des bearbeiteten Bildes definiert, also im Hinblick auf das zu erreichende Endziel, nicht im Hinblick auf die vermuteten erforderlichen Werkzeuge oder technischen Details. Dies könnte zu einer Diskussion führen, um die Wurzeln des Problems zu ergründen, die oft gut verborgen unter dem liegen, was der Benutzer für sein Problem hält.
In diesem Stadium wird kein Lösungsvorschlag akzeptiert.
Lösungen kommen als Zweites
Wenn die Definition und der Umfang des Problems zwischen den beteiligten Diskussionsteilnehmern vereinbart wird, können Lösungsvorschläge gemacht werden. Weitere Diskussionen könnten erforderlich sein, um die Nachteile und Vorteile jeder Lösung zu bewerten, was zur Annahme der besten Lösung auf Prinzipienführung führt. Lösungen werden durch ihre Funktionalitäten definiert (also, was sie tun sollten), nicht durch ihre Technologie oder Mittel (wie sie es tun sollten).
In diesem Stadium wird kein Prototypvorschlag akzeptiert.
Die angenommenen Lösungen führen zu einem neuen Eintrag im Projektmanagement-Kanban-Board .
Sie könnten einer theoretischen und technischen Recherche zur Beurteilung der Machbarkeit unterliegen, in diesem Fall werden sie in die Zu erforschen Spalte des Kanban-Boards einsortiert. Die Forschungsergebnisse werden dem ursprünglichen Thema hinzugefügt, bis die Machbarkeit der Lösung bewiesen ist. Wenn dies der Fall ist, wird das Thema in die “Zu erledigen” Spalte des Kanban-Boards verschoben.
Angenommene Lösungen könnten direkt in die Zu erledigen Spalte einsortiert werden, wenn nur bekannte Werkzeuge und Techniken erforderlich sind.
Idealerweise sollten die zu testenden Punkte und das Testverfahren zur Validierung des Prototyps geschrieben werden, noch bevor ein funktionierender Prototyp vorliegt. Zumindest sollten die Tests sicherstellen, dass es keine Regressionen bei verwandten Funktionen und Werkzeugen gegeben hat.
Prototypen kommen als Drittes
Nur die im Kanban-Board in die “Zu erledigen” Spalte einsortierten Themen werden bearbeitet, entweder von mir oder von jemandem, der sich ihnen widmen möchte.
Der Prototyp der Lösung wird in einem Pull Request eines Themenzweigs vorgeschlagen, der auf das ursprüngliche Thema verweist. Themenzweige müssen auf den master Zweig neu gebased werden, z. B. git rebase upstream master, oder wenn du deinen Zweig lokal mit neuen Master-Commits aktualisierst, git pull upstream master --rebase ausführen oder Git global einrichten , um durch rebase statt merge zu ziehen. Das stellt sicher, dass der Branch-Verlauf mit minimalem Aufwand sauber bleibt und der master Verlauf ebenfalls sauber bleibt, wenn dein PR zusammengeführt wird.
Wenn der Prototyp-Pull-Request überprüft wird und den Codequalitätsstandards entspricht (siehe unten), während er die Spezifikationen der angenommenen Lösung erfüllt, wird er genehmigt und automatisch in die “Zu testen/validieren” Spalte des Projektmanagement-Kanban-Boards einsortiert.
Validierung kommt als Viertes
Genehmigte Pull Requests werden früh im candidate oder dev Zweig für Tests zusammengeführt, je nachdem, ob sie die Bearbeitungsgeschichte von Bildern unterbrechen könnten (indem neue Modulparameter hinzugefügt oder die Datenbankschemata geändert werden). Dieser Zweig wird immer der master Zweig mit allen ausstehenden Pull Requests zur Validierung sein. Dies soll das Testen von Personen erleichtern, die nicht unbedingt up-to-date mit der manuellen Zusammenführung von Git-Zweigen sind. Im Gegensatz zum dev Zweig sollte candidate deine Bearbeitungen nicht zerstören.
Wenn nach einiger Zeit keine Fehler oder Unterbrechungen gemeldet werden und der Prototyp seinen ursprünglichen Zweck korrekt erfüllt, wird er in master zusammengeführt und das zugehörige Thema wird abgeschlossen und in die “Erledigt” Spalte des Projektmanagement-Kanban-Boards verschoben.
Wenn sich der Prototyp als unbefriedigend erweist, kann er abgelehnt werden und ein anderer muss entwickelt werden.
Profi-Tipps von einem erfahrenen Designer
Nicht alle Software-Probleme sind Programmier-Probleme
Viele Probleme erfordern nicht mehr Werkzeuge (oder Spielzeuge) und mehr Code. Mehr Code ist ohnehin immer schlecht und sollte nach Möglichkeit vermieden werden. Sehr oft besteht das Problem des Nutzers darin, dass er nicht erkennt, wie er vorhandene Funktionen zurechtbiegen kann, um seine Bedürfnisse zu erfüllen. Das löst man durch Bildung, sprich bessere Dokumentation und mehr Tutorials, und manchmal durch eine bessere UI.
Hören Sie auf die Nutzer, aber hören Sie nicht auf sie
Nutzer äußern, was sie wollen und was ihnen gefällt, niemals das, was sie brauchen. Und Sie müssen ihnen nicht zuhören, um zu wissen, was es sein wird:
- sie werden dasselbe haben wollen, was ihr Nachbar gerade bekommen hat,
- ihnen wird gefallen, woran sie gewöhnt sind.
Und dann werden Sie für alles, was der eine mag, einen anderen finden, der es nicht mag. Die Darktable-Methode, Konflikte zu lösen, besteht also darin, Konflikte nicht zu lösen, sondern jedem eine Option, einen Modus, eine Voreinstellung zu geben, um jene besondere Sache zu aktivieren, die er mag, so wie er sie mag. Das bedeutet mehr case in Ihrem switch, mehr verschachtelte if, mehr Codepfade, die Sie jetzt testen, debuggen und in Zukunft warten müssen, und dann mehr Voreinstellungen, die die anderen im Einstellungsfenster verdecken. Ehe Sie sich versehen, ist der Code ein Tumor, den niemand mehr versteht, und ihn zu reparieren macht ihn nur noch komplizierter.
Wenn Sie unter die Oberfläche kratzen, stellen Sie fest, dass das, was die Menschen tatsächlich brauchen, den Bedürfnissen anderer Menschen viel näher ist als das, was sie zu wollen behaupten. Sie können die Bedürfnisse also viel leichter in Einklang bringen als die Wünsche, und ohne Kompromisse. Aber dann müssen Sie die grundlegenden Bedürfnisse unterhalb des Willens aufspüren, und das erfordert Abstraktionsvermögen und Psychologie.
UI-Designer sind gefährliche Idioten
Jeder, der nur die UI sieht, sich auf sie konzentriert und sich nur um sie kümmert, ist ein gefährlicher Idiot. Wenn Ihre GUI kompliziert ist, bedeutet das viel mehr als nur eine „komplizierte GUI": Es bedeutet, dass die Komplexität Ihres Backends Ihr Frontend erreicht hat. Ich habe auf die harte Tour herausgefunden, dass GUI-Komplexität niemals getrennt ist von der Komplexität des Backends und der gesamten Anwendungsarchitektur und auch nicht getrennt davon gelöst werden kann. Die GUI verläuft nicht parallel zur Backend-Architektur, sie ist deren Endpunkt.
Das Problem der UI-Designer ist, dass sie typischerweise nicht programmieren, oder wenn doch, dann sind sie schlecht in Low-Level-Programmierung und Software-Architektur. Also konzentrieren sie sich auf das Wenige, das sie sehen und verstehen (der typische Streetlight-Effekt ), und sie produzieren nur nicht umsetzbare Entwürfe, die mit dem in Konflikt stehen, was die Software tatsächlich zum Funktionieren braucht. Denn diese GUI verbindet nur die Benutzereingaben mit dem Backend, und wenn wir so viele Widgets brauchen, dann deshalb, weil die Architektur so viele Eingaben braucht. Sie können dem nicht entkommen: Um Widgets zu entfernen, müssen Sie Eingaben entfernen, was bedeutet, dass Ihre Architektur zuerst mit weniger Freiheitsgraden auskommen muss. Das beginnt mit der Vereinfachung des Backends, was ein übel riechendes Refactoring von staubigem altem Code bedeutet, den niemand mehr versteht.
Sie lösen GUI-Probleme nicht mit Zeichnungen und Mockups, Sie lösen GUI-Probleme, indem Sie Backend-Probleme lösen. Aber dann brauchen Sie Leute, die beide Ebenen verstehen, und die sind vielleicht zu teuer für Sie.
Fragen Sie sich 36-mal am Tag, was das Problem war, das Sie zu lösen versuchten
Es ist super leicht, sich in technischen Feinheiten zu verlieren, wenn man in einer Low-Level-Sprache programmiert und gegen Drittanbieter-Bibliotheken oder APIs kämpft, aber manchmal ist die Lösung einfach und elegant, und Sie haben sich zu weit in Zeiger und Thread-Locks verstricken lassen. Kehren Sie immer zum ursprünglichen Problem zurück, das ist Ihre Rettungsleine zur Einfachheit.
Was ist das Problem? Wer ist davon betroffen? Wann? Wie oft? Beim Tun von was?
Der beste Weg ist der einfachste Weg zu Ihrer Lösung: einfache Technik, wenig Code, wenige Schichten.
Dokumentieren Sie Ihr beschissenes Design
Viele Male habe ich ein Design beim Dokumentieren komplett neu gemacht, denn beim Versuch, es zu erklären, merkt man, dass es zu kompliziert zu erklären ist, was bedeutet, dass es zu kompliziert zu verstehen ist. Wenn Sie Ihr Design nicht in ein paar Absätzen erklären können oder Ihre Dokumentation zu viele „wenn dies, dann das" enthält, gibt es normalerweise zwei Gründe:
- Ihre GUI stellt die relevanten Informationen nicht dort bereit, wo der Nutzer sie braucht, sodass Sie die halbe Dokumentation in Ihrer Erklärung verlinken müssen, um die Nutzer auf alles hinzuweisen, was sie wissen oder prüfen müssen, bevor sie die eine Sache nutzen, die Sie gerade dokumentiert haben. Die Lösung besteht darin, die relevanten Informationen dorthin zurückzubringen, wo sie gebraucht werden.
- Ihre GUI hat zu viele Ablagen, ausklappbare Elemente, kontextabhängige Verhaltensweisen, Anwendungsfälle oder versteckte Voreinstellungen, und alle Fälle abzudecken zwingt Sie, einen Roman zu schreiben. Die Lösung besteht darin, den Arbeitsablauf zu linearisieren, vielleicht Optionen zu entfernen oder Funktionen aufzuteilen.
Die GUI ist die Art und Weise, wie Nutzer das Backend steuern, aber sie ist auch der Ort, an dem sie von vorhandenen Funktionen und deren Zweck erfahren. Die Dokumentation sollte Kontext, Richtlinien und Referenzen dazu liefern, wie wir Dinge tun, aber die GUI sollte selbst erklären, was sie tut.
Natürlich gibt es dabei eine offensichtliche Grenze: In einer Fotografie-Anwendung müssen die Nutzer die Fotografie und ihre Sprache verstehen, was Dinge wie Dynamikumfang, Farbumfang (Gamut), Tone-Mapping usw. umfasst. Die GUI sollte selbsterklärend sein, was die Art ihrer Verwendung angeht, nicht aber die Notwendigkeit beseitigen, das Handwerk zu erlernen (was getan werden sollte und wie).
Design ist ein iterativer Prozess
Eine Anwendung ist eine virtuelle Welt, in der eine kleine Änderung neu ordnen kann, wie sich der Rest des Ökosystems darum herum anpasst. Daher kann jede Designänderung die Notwendigkeit auslösen, andere Dinge drumherum zu ändern (Werkzeuge umbauen, Widgets verschieben, Funktionen ausmisten). Was dann wiederum die Notwendigkeit auslösen könnte, die ursprüngliche Änderung erneut zu korrigieren. Es ist ein schrittweiser Prozess, in dem es töricht ist, überhaupt zu versuchen, bei jedem Schritt alles richtig zu machen; worauf es ankommt, ist, dass jeder Schritt die Umgebung gegenüber dem vorherigen verbessert.
Manchmal kann ein (Neu-)Design nicht in kleinen Schritten erfolgen, sondern nur in großen Sprüngen: dann, wenn Sie die Architektur neu machen. Diese Sprünge werden viele Dinge in ihrer Umgebung zerbrechen, was in Ordnung ist, wenn die neuere Architektur insgesamt einfacher und robuster ist und wenn Sie ihr etwas Zeit zur Erholung geben, bevor Sie erneut zum Vorschlaghammer greifen. Aber das wird einen Übergangszustand erzeugen, in dem das neue Design schlechter erscheint als das vorherige. Das sagt uns, dass die Wahrnehmung des Designs keine gültige Eingabe ist: Die Designqualität muss anhand ihrer Ziele beurteilt und mit objektiven Metriken bewertet werden, nicht mit Gefühlen und schnellen Tests.
Und manchmal sind einige Schritte Fehler und sollten rückgängig gemacht werden. Der Sunk-Cost-Fehlschluss sollte nicht dazu benutzt werden, zu rechtfertigen, dass ein Redesign beibehalten werden sollte, weil es viel Arbeit war, es zu erreichen. Es ist zu erwarten, dass nicht die gesamte Forschung und Entwicklung es in die Produktion schafft.
Whack-a-Mole-Sitzungen bedeuten, dass Ihre Architektur ausgedient hat
Ob Sie beim Beheben alter Bugs ständig neue erzeugen oder ob Sie beim Erweitern einer Funktion ständig Grenzfälle erzeugen, es weist alles in dieselbe Richtung: Ihre Architektur lässt sich nicht mehr weiter biegen, weil sie ihren Design-Anforderungen entwachsen ist. Es kann sein, dass das Backend zu verworren geworden ist, oder es kann sein, dass die bestehende Architektur wirklich nicht für das geplant war, wozu Sie sie zu bringen versuchen, aber in beiden Fällen müssen Sie die Architektur neu machen und aufhören, an Ort und Stelle herumzuhacken. Andernfalls häufen Sie nur technische Schulden an.
Aber dann wechseln die Entwicklungskosten die Größenordnung, und aus jenem Samstagnachmittag-Projekt könnte ein monatelanges Projekt werden.
Best Practices sind Richtlinien, keine Regeln
Best Practices helfen dabei, gesunde Gewohnheiten und sauberen Code zu entwickeln, es sei denn, Sie verstehen das Problem nicht, das sie zu lösen versuchten, und wenden sie außerhalb ihres Gültigkeitsbereichs an. In diesem Fall werden sie zum Cargo-Kult: der Versuch, die Wirkungen nachzuahmen, in der Hoffnung, dass dies auch die Ursachen auf magische Weise behebt.
Das Erste, was mir in den Sinn kommt, ist Code-Wiederverwendung/Code-Sharing. Wenn die Wiederverwendung desselben Codes für (scheinbar ähnliche) Funktionen zu zu viel internem Verzweigen führt (verschachtelte if, switch/case usw.), um alle möglichen Pfade zu behandeln, dann geht das, was Sie am Codevolumen gewinnen, an der zyklomatischen Komplexität verloren, und, nebenbei bemerkt, Ihre Funktionen sind nicht so ähnlich, wie Sie dachten.
Außerdem kann das Duplizieren von Code ein Ausgangspunkt sein, um das Duplikat später lokal zu optimieren: Sobald Sie die vollständige Prozedur vor sich haben, entdecken Sie möglicherweise Schritte, die zwischengespeichert oder herausgezogen werden können. Wohingegen Sie, wenn die Prozedur nur aus undurchsichtigen, hochrangigen, wiederverwendbaren API-Methoden besteht, die Fähigkeit verlieren, redundante Berechnungen zu erkennen und zu entfernen. Es gibt also ein Prinzip der Datenwiederverwendung/-teilung (sprich das Zwischenspeichern berechneter Daten, die später unverändert wieder verwendet werden, um CPU-Zyklen zu sparen), das durch Code-Wiederverwendung/-teilung unmöglich gemacht werden kann, weil sie den Lebenszyklus der Daten verschleiert und abstrahiert.
Das wird bei Pixel-Schleifen kritisch: Sie wollen alle pixelweisen Operationen in dieselbe Schleife zusammenfassen, um den Preis der Speicher-I/O nur einmal zu zahlen. Das bedeutet, dass Sie dieselbe affine Korrektur ($y = a * x + b$) in jede Schleife neu implementieren müssen, die sie verwendet, anstatt eine wiederverwendbare Methode zu haben, die genau das in ihrer eigenen Schleife erledigt.
Wappnen Sie sich nicht
Wenn Sie sich von kryptischen und zufälligen Bugs überwältigt fühlen, die immer wiederkommen und aus denen Sie nicht schlau werden, kämpfen Sie nicht blind weiter, sondern treten Sie einen Schritt zurück. Instrumentieren Sie dann Debug-Helfer oder übergeordnete Manager, die interne Zustände nachverfolgen und Ihnen eine Karte der Datenwerte der Software an jedem relevanten Punkt ihres Lebenszyklus geben. Das ist besonders kritisch bei asynchronen Aufbauten, in denen mehrere Threads parallel auf unterschiedlichen Zeitachsen Dinge erzeugen, darauf zugreifen oder sie berechnen, und die tatsächliche Reihenfolge der Ausführung vom Laufzeitkontext abhängt.
Translated from English by : ChatGPT, Claude. In case of conflict, inconsistency or error, the English version shall prevail.