Gemeinschaft
Entwicklung
05.08.2023
TLDR; Wir schreiben Documenso neu, um von unseren MVP-Grundlagen wegzukommen und eine noch bessere Basis für das Projekt zu schaffen. Diese Neuschreibung wird uns die Möglichkeit geben, einige Dinge innerhalb des Projekts zu reparieren und gleichzeitig einen schnelleren Entwicklungsprozess für die Zukunft zu ermöglichen.
Einführung
Bei Documenso bauen wir die nächste Generation von Signaturinfrastrukturen mit dem Fokus darauf auf, sie inklusiv und zugänglich für alle zu gestalten. Um dies zu erreichen, müssen wir sicherstellen, dass die Software, die wir schreiben, ebenfalls inklusiv und zugänglich ist, und aus diesem Grund haben wir beschlossen, einen Schritt zurückzutreten und eine _schnelle_ Neuschreibung durchzuführen.
Obwohl wir den validierten MVP-Status erreicht und zahlende Kunden gewonnen haben, sind wir noch ziemlich weit von unserem Ziel entfernt, eine vertrauenswürdige, offene Signaturerfahrung zu schaffen. Um uns diesem Ziel näher zu bringen, müssen wir einen Schritt zurücktreten und uns auf die Grundlagen des Projekts konzentrieren, um sicherzustellen, dass wir alle Punkte, die wir auf unserer aktuellen Homepage festgelegt haben, lösen können.
Glücklicherweise war dies kein Fall, in dem jemand dem Team beitrat und eine Neuschreibung aufgrund eines Mangels an Verständnis für die Codebasis und den damit verbundenen Kontext vorschlug. Bevor ich als Mitgründer zu Documenso kam, hatte ich viel Zeit in der Documenso-Codebasis verbracht und hatte ein ziemlich intimes Verständnis dafür, was größtenteils geschah. Dieses Wissen ermöglichte es mir, die faire und gleichzeitig schwierige Entscheidung zu treffen, eine kurze Pause einzulegen, damit wir unsere aktuellen Grundlagen neu aufbauen können, um Zugänglichkeit und eine schnellere Lieferzeit in der Zukunft zu ermöglichen.
Die Überlegung: TypeScript
Unser Hauptgrund für die Neuschreibung ist, die Werkzeuge und Technologien, die wir bereits ausgewählt haben, besser zu nutzen, nämlich TypeScript. Während Documenso derzeit TypeScript verwendet, wird nicht vollständig von seinen Sicherheitsfunktionen wie Generics und Typwächtern profitiert.
Die Codebasis hat derzeit mehrere Instanzen von `any`-Typen, was zu erwarten ist, wenn man in einem unbekannten Bereich arbeitet, in dem Objektmodelle vor der Erkundung und Experimentierung nicht vollständig verstanden werden. Diese `any`s haben die Entwicklung anfänglich beschleunigt, sind jedoch mittlerweile eine Behinderung geworden, da der Mangel an Typinformationen in Kombination mit Prop Drilling die Notwendigkeit erhöht, durch viel Kontext zu gehen, um die Ursache eines bestimmten Problems zu verstehen.
Die Neuschreibung nutzt TypeScript in vollem Umfang, um sicherzustellen, dass jede Interaktion stark typisiert ist, sowohl durch allgemeine TypeScript-Tools als auch durch die Einführung von Zod, einer Validierungsbibliothek mit hervorragender TypeScript-Unterstützung. Mit diesen Entscheidungen können wir sicherstellen, dass die Codebasis robust gegenüber verschiedenen Eingaben und Zuständen ist, da die meisten Probleme während der Kompilierung erkannt und im IDE des Entwicklers markiert werden.
Die Überlegung: Stärkere API-Verträge
Im Einklang mit unserem Muster, stark typisierte Verträge zu erstellen, haben wir beschlossen, tRPC für unsere interne API zu verwenden. Dies ermöglicht es uns, Typen zwischen unserem Frontend und Backend zu teilen und einen soliden Vertrag für die Interaktionen zwischen beiden zu etablieren. Dies steht im Gegensatz zu den derzeit untypisierten API-Endpunkten in Documenso, die mit der `fetch`-API erreicht werden, die selbst untypisiert ist.
Die Verwendung von tRPC reduziert drastisch die Wahrscheinlichkeit von Fehlern, die aus alltäglichen Dingen wie Änderungen an Argumenten oder Antwortformaten während Updates und Upgrades resultieren. Wir haben diese Entscheidung getroffen, weil tRPC eine ausgereifte Technologie ist, die keine Anzeichen zeigt, in absehbarer Zeit an Schwung zu verlieren.
Darüber hinaus haben viele unserer Open-Source-Freunde dieselbe Wahl aus ähnlichen Gründen getroffen.
Die Überlegung: Spannende Technologien wählen
Obwohl wir bereits mit einem Stack arbeiten, den ich für spannend halte und zu dem Next.js, Prisma, Tailwind und mehr gehören, ist es kein Geheimnis, dass Mitwirkende gerne mit neuen Technologien arbeiten, die ihnen bei ihren eigenen Karrieren und Projekten zugutekommen.
Um dies zu nutzen, haben wir beschlossen, Next.js 13 und die neue Architektur der Serverkomponenten und Aktionen von React zu verwenden. Serverkomponenten sind derzeit bei Entwicklern beliebt, wobei viele sie gleichzeitig lieben und hassen.
Ich habe persönlich seit ihrer ersten Veröffentlichung im Oktober 2022 mit Serverkomponenten und Aktionen gearbeitet und habe die meisten der Hürden und Einschränkungen auf dem Weg behandelt. Jetzt, im Juli 2023, glaube ich, dass sie sich an einem viel stabileren Punkt befinden und bereit sind, übernommen zu werden, wobei ihre Vorteile von vielen anerkannt werden.
Durch die Entscheidung, Serverkomponenten und Aktionen zu verwenden, hoffen wir, die Gemeinschaft zu ermutigen, mehr teilzunehmen, als sie es sonst vielleicht tun würden. Wir wählen dies jedoch nur, weil es reifer und stabiler geworden ist. Wir werden keine Dinge wählen, die weniger wahrscheinlich in Zukunft die De-facto-Lösung werden, da wir nicht wünschen, ein Bündel technischer Schulden später zu erben.
Die Überlegung: Ermöglichung paralleler Arbeiten
Ein weiterer überzeugender Grund für die Neuschreibung war, den Code effektiv zu modularisieren, damit wir an Funktionen parallel und ohne Probleme arbeiten können. Das bedeutet, so viel wie möglich aus Komponenten, API-Handlern und mehr in eine Reihe von Methoden und Funktionen zu extrahieren, die sich nur auf eine Sache konzentrieren.
Bei der Durchführung dieser Arbeiten sollten wir in der Lage sein, Refactorings und andere Änderungen an verschiedenen Teilen des Codes einfach vorzunehmen, ohne uns gegenseitig in die Quere zu kommen. Dies ermöglicht uns auch, Gegenstände nach Bedarf zu aktualisieren oder abzulehnen, indem wir an den Vertrag der vorherigen Methode festhalten.
Darüber hinaus macht dies das Testen zu einer viel einfacheren Aufgabe, da wir uns mehr auf Arbeitseinheiten konzentrieren können, anstatt umfangreiche End-to-End-Tests durchzuführen, obwohl wir beide anstreben, nur nicht sofort.
Die Überlegung: Lizenzierung der Arbeit
Ein weiterer wesentlicher Grund für die Neuschreibung besteht darin, sicherzustellen, dass alle Arbeiten, die sowohl von unserem internen Team als auch von externen Mitwirkenden am Projekt durchgeführt werden, auf eine Weise lizenziert sind, die dem Projekt langfristig zugutekommt. Vor der Neuschreibung hätten Mitwirkende Pull-Requests erstellt, die ohne weitere Verfahren außerhalb der üblichen Code-Überprüfungs- und Testzyklen zusammengeführt wurden.
Dies war größtenteils in Ordnung, da wir einfach am MVP arbeiteten, aber jetzt, da wir uns auf eine Infrastruktur konzentrieren, beabsichtigen wir, Unternehmenskunden zu übernehmen, die einen nicht-GPLv3-Lizenz benötigen, da die Interpretationen davon für das private Hosting ziemlich schädlich sein können. Um dies zu erleichtern, werden wir von Mitwirkenden verlangen, dass sie eine Mitwirkenden-Lizenzvereinbarung (CLA) unterzeichnen, bevor ihre Änderungen zusammengeführt werden, sodass wir eine ewige Lizenz erhalten, um ihren Code zu verwenden und ihn nach Bedarf umzuwandeln, wie im obigen Anwendungsfall.
Während einige bei der Vorstellung, eine CLA zu unterzeichnen, zurückschrecken könnten, möchten wir ein überzeugendes Unternehmensangebot durch duale Lizenzierung anbieten. Eine großartige Unternehmensakzeptanz ist einer der Grundpfeiler unserer Strategie und wird entscheidend sein, um die Entwicklungsarbeit für die Gemeinschaft und das Produkt langfristig zu finanzieren.
_Bitte beachten Sie, dass dies nicht bedeutet, dass wir jemals Closed-Source werden, es ist ein Punkt in unseren Investorenvereinbarungen, dass https://github.com/documenso/documenso immer verfügbar und Open Source bleibt._
Ziele und Nicht-Ziele
Eine Anwendung neu zu schreiben, ist eine monumentale Aufgabe, die ich in meiner Karriere viele Male angenommen und abgelehnt habe. Je älter ich werde, desto zögerlicher werde ich bei diesen Neuschreibungen, da ich verstehe, dass Systeme viel Kontext und Geschichte mitbringen. Dies macht sie besser für schrittweises Refactoring geeignet, um nicht die Lektionen der Vergangenheit bei der Einführung der Neuschreibung erneut lernen zu müssen.
Um sicherzustellen, dass wir nicht einfach ins kalte Wasser springen, habe ich eine Liste von Zielen und Nicht-Zielen erstellt, um diese Neuschreibung schlank und erschwinglich zu halten.
Ziele
Ein sauberes Design und eine Schnittstelle für die neu geschriebene Anwendung bereitzustellen, die auf den ersten Blick ein Gefühl von Vertrauen und Sicherheit vermittelt.
Eine stabile Grundlage und Architektur zu schaffen, die Wachstum in unsere zukünftigen Roadmap-Elemente (Teams, Automatisierung, Workflows usw.) ermöglicht.
Ein robustes System zu schaffen, das minimalen Kontext durch starke Verträge und Typisierung erfordert.
Nicht-Ziele
Das Datenbankschema zu ändern (wir möchten die Migration nicht schwieriger machen, als sie sein muss; daher müssen alle Änderungen additiv sein).
Zu viele Funktionen hinzuzufügen, die im System vor der Neuschreibung nicht vorhanden waren.
Funktionen zu entfernen, die in der älteren Version von Documenso vorhanden waren, wie z.B. kostenlose Signaturen (Signaturen, die kein entsprechendes Feld haben).
Rollout-Plan
Dank der oben genannten Einschränkungen wird unser Rollout hoffentlich recht schmerzlos sein. Um sicher zu gehen, planen wir Folgendes.
Im aktuellen Testumfeld eine Reihe von Dokumenten zu erstellen und zu signieren, von denen viele in unterschiedlichen Zuständen der Vollständigkeit verbleiben.
Die Neuschreibung in der Testumgebung bereitzustellen und zu überprüfen, dass alle bestehenden Dokumente und Informationen ohne Probleme abrufbar und veränderbar sind.
Ein weiteres Set von Dokumenten mit der neuen Neuschreibung zu erstellen und zu überprüfen, dass alle Interaktionen zwischen Autorisierung und Signatur wie erwartet funktionieren.
Dies zu wiederholen, bis wir ein allgemeines Vertrauensniveau erreichen (Erwartung von zwei Wochen).
Sobald wir das gewünschte Vertrauensniveau mit unserer Testumgebung erreicht haben, werden wir die Neuschreibung in der Produktionsumgebung bereitstellen und sicherstellen, dass wir alle erforderlichen Backups im Falle eines katastrophalen Fehlers durchgeführt haben.
Möchten Sie helfen?
Wir arbeiten derzeit an dem feat/refresh-Branch auf GitHub. Wir werden in den kommenden Tagen eine CLA zur Unterzeichnung zur Verfügung stellen, damit wir mit der Annahme externer Beiträge so schnell wie möglich beginnen können. Während wir uns dem Endstadium der Neuschreibung nähern, werden wir bald ein paar Bounties für Dinge wie Husky und Changesets ausschreiben.
Behalten Sie unsere https://github.com/documenso/documenso/issues im Auge, um auf dem Laufenden zu bleiben!