Comunidad
Desarrollo
5 ago 2023
TLDR; Estamos reescribiendo Documenso para avanzar más allá de nuestras bases de MVP y crear una base aún mejor para el proyecto. Esta reescritura nos brindará la oportunidad de corregir algunas cosas dentro del proyecto mientras habilitamos un proceso de desarrollo más rápido en el futuro.
Introducción
En Documenso, estamos construyendo la próxima generación de infraestructura de firmas con un enfoque en hacerla inclusiva y accesible para todos. Para hacer esto, necesitamos asegurarnos de que el software que escribimos también sea inclusivo y accesible y por esta razón hemos decidido dar un paso atrás y realizar una _reescritura_ rápida.
Aunque hemos logrado el estatus de MVP validado y hemos ganado clientes de pago, todavía estamos bastante lejos de nuestro objetivo de crear una experiencia de firma confiable y abierta. Para acercarnos a ese futuro, necesitamos retroceder y enfocarnos en las bases del proyecto para asegurarnos de que podamos resolver todos los elementos que nos propusimos en nuestra página de inicio actual.
Afortunadamente, este no fue un caso de que alguien se uniera al equipo y propusiera una reescritura debido a una falta de comprensión del código y el contexto que la rodea. Antes de unirme a Documenso como cofundador, pasé una gran cantidad de tiempo dentro de la base de código de Documenso y tenía un entendimiento bastante íntimo de lo que estaba sucediendo en su mayor parte. Este conocimiento me permitió tomar la decisión justa y al mismo tiempo difícil de hacer una pausa rápida para que podamos reconstruir nuestras actuales bases y habilitar la accesibilidad y un tiempo de entrega más rápido en el futuro.
El razonamiento: TypeScript
Nuestra razón principal para la reescritura es aprovechar mejor las herramientas y tecnologías que ya hemos elegido, a saber, TypeScript. Mientras que Documenso actualmente usa TypeScript, no está aprovechando completamente sus características de seguridad, como los genéricos y los guardas de tipo.
La base de código actualmente tiene varias instancias de tipos `any`, lo cual es de esperar al trabajar en un dominio desconocido donde los modelos de objetos no se entienden completamente antes de la exploración y la experimentación. Estos `any` inicialmente aceleraron el desarrollo, pero desde entonces se han convertido en un obstáculo debido a la falta de información de tipo, combinada con la perforación de propiedades. Como resultado, es necesario pasar por mucho contexto para entender la raíz de cualquier problema dado.
La reescritura está utilizando TypeScript a su máximo potencial, asegurando que cada interacción esté fuertemente tipada, tanto a través de herramientas generales de TypeScript como con la introducción de Zod, una biblioteca de validación con excelente soporte para TypeScript. Con estas elecciones, podemos asegurarnos de que la base de código sea robusta frente a diversas entradas y estados, ya que la mayoría de los problemas serán detectados durante el tiempo de compilación y señalados dentro del IDE de un desarrollador.
El razonamiento: Contratos de API más sólidos
En línea con nuestro patrón de crear contratos fuertemente tipados, hemos decidido usar tRPC para nuestra API interna. Esto nos permite compartir tipos entre nuestro frontend y backend y establecer un contrato sólido para las interacciones entre ambos. Esto contrasta con los puntos finales de API actualmente no tipados en Documenso, que se acceden utilizando la API `fetch` que en sí misma no está tipada.
Utilizar tRPC reduce drásticamente la posibilidad de fallos derivados de cosas mundanas como cambios en la forma de los argumentos o las respuestas durante las actualizaciones. Tomamos esta decisión fácilmente porque tRPC es una tecnología madura que no muestra señales de perder impulso pronto.
Además, muchos de nuestros amigos de código abierto han tomado la misma elección por razones similares.
El razonamiento: Elegir tecnologías emocionantes
Aunque ya trabajamos con lo que considero un stack divertido que incluye Next.js, Prisma, Tailwind y más, no es un secreto que los contribuyentes disfrutan trabajar con nuevas tecnologías que los beneficien en sus propias carreras y proyectos.
Para aprovechar esto, hemos decidido usar Next.js 13 y la nueva arquitectura de componentes y acciones del servidor de React. Los componentes del servidor son actualmente populares entre los desarrolladores, muchos los aman y los odian al mismo tiempo.
Personalmente, he trabajado con componentes del servidor y acciones desde que fueron lanzados por primera vez en octubre de 2022 y he lidiado con la mayoría de los problemas y limitaciones en el camino. Ahora, en julio de 2023, creo que están en un lugar mucho más estable y listos para ser adoptados, con sus beneficios reconocidos por muchos.
Al elegir usar componentes y acciones del servidor, esperamos alentar a la comunidad a participar más de lo que probablemente lo harían. Sin embargo, solo elegimos esto porque se ha vuelto más maduro y estable. No elegiremos cosas que sean menos propensas a convertirse en la solución por defecto en el futuro, ya que no deseamos heredar una carga de deuda técnica más tarde.
El razonamiento: Permitir el trabajo concurrente
Otra razón convincente para la reescritura fue modularizar efectivamente el código para que podamos trabajar en funciones de manera concurrente y sin problemas. Esto significa extraer tanto como sea posible de componentes, controladores de API y más, y ponerlo en un conjunto de métodos y funciones que intentan enfocarse en una sola cosa.
Al realizar este trabajo deberíamos poder hacer refactorizaciones y otros cambios fácilmente en varias partes del código sin pisarnos los pies, esto también nos otorga la capacidad de actualizar o deprecar elementos según sea necesario al adherirnos al contrato del método anterior.
Además, esto facilita las pruebas como tarea porque podemos enfocarnos más en unidades de trabajo en lugar de extensas pruebas de extremo a extremo, aunque aspiramos a tener ambas, solo que no de inmediato.
El razonamiento: Licenciamiento del trabajo
Otra razón importante para la reescritura es asegurar que todo el trabajo realizado en el proyecto tanto por nuestro equipo interno como por colaboradores externos esté licenciado de una manera que beneficie al proyecto a largo plazo. Antes de la reescritura, los colaboradores creaban solicitudes de extracción que se fusionaban sin ningún otro proceso fuera de los ciclos comunes de revisión de código y pruebas.
Esto estuvo bien en su mayor parte, ya que simplemente estábamos trabajando en el MVP, pero ahora que nos estamos moviendo hacia un enfoque de infraestructura, tenemos la intención de asumir clientes empresariales que necesitarán una licencia no GPLv3, ya que las interpretaciones de esta pueden ser bastante dañinas para el alojamiento privado. Para facilitar esto, requeriremos que los colaboradores firmen un acuerdo de licencia de contribuidor (CLA) antes de que sus cambios sean fusionados, lo que asignará una licencia perpetua para que podamos usar su código y volver a licenciarlo según sea necesario, como para el caso de uso mencionado anteriormente.
Si bien algunos podrían estremecerse ante la idea de firmar un CLA, queremos ofrecer una oferta empresarial convincente a través de medios de doble licencia. Una gran adopción empresarial es uno de los pilares de nuestra estrategia y será clave para financiar el desarrollo de la comunidad y del producto a largo plazo.
_Tenga en cuenta que lo anterior no significa que alguna vez cerraremos el código, es un punto en nuestros acuerdos de inversión que https://github.com/documenso/documenso siempre permanecerá disponible y de código abierto._
Objetivos y No Objetivos
Reescribir una aplicación es una tarea monumental que he asumido y rechazado muchas veces en mi carrera. A medida que envejezco, me vuelvo más reacio a realizar estas reescrituras porque entiendo que los sistemas llevan mucho contexto e historia. Esto los hace más adecuados para un refactorizado por partes, en lugar de aprender las lecciones del pasado una vez más durante el lanzamiento de la reescritura.
Para asegurarnos de que no estamos simplemente lanzándonos al profundo, he establecido una lista de objetivos y no objetivos para mantener esta reescritura delgada y asequible.
Objetivos
Proporcionar un diseño e interfaz limpios para la aplicación reescrita que generen una sensación de confianza y seguridad a primera vista.
Crear una base y arquitectura estables que permitan el crecimiento en nuestros próximos elementos de hoja de ruta (equipos, automatización, flujos de trabajo, etc.
Crear un sistema robusto que requiera un contexto mínimo a través de contratos y tipado sólidos.
No Objetivos
Cambiar el esquema de la base de datos (no queremos hacer que la migración sea más difícil de lo necesario, por lo tanto, todos los cambios deben ser aditivos).
Agregar demasiadas características que no estaban en el sistema antes de la reescritura.
Eliminar cualquier función que estuviera en la versión anterior de Documenso, como firmas gratuitas (firmas que no tienen un campo correspondiente).
Plan de Despliegue
Gracias a las restricciones enumeradas anteriormente, nuestro despliegue será, con suerte, bastante indoloro, aunque para estar seguros, planeamos hacer lo siguiente.
En el actual entorno de pruebas, crear y firmar varios documentos dejando muchos en distintos estados de finalización.
Desplegar la reescritura en el entorno de pruebas y verificar que todos los documentos e información existentes sean recuperables y modificables sin ningún problema.
Crear otro conjunto de documentos utilizando la nueva reescritura y verificar que todas las interacciones entre la autoría y la firma funcionen como se espera.
Repetir esto hasta que alcancemos un nivel de confianza general (expectativa de dos semanas).
Una vez que hayamos alcanzado el nivel de confianza deseado con nuestro entorno de pruebas, buscaremos desplegar la reescritura en el entorno de producción asegurándonos de que hemos realizado todas las copias de seguridad necesarias en caso de un fallo catastrófico.
¿Quieres ayudar?
Actualmente estamos trabajando en la rama feat/refresh en GitHub, pretendemos tener un CLA disponible para firmar en los próximos días para que podamos comenzar a aceptar contribuciones externas lo antes posible. Mientras nos acercamos a la etapa final de la reescritura, lanzaremos un par de recompensas pronto para cosas como Husky y Changesets.
¡Mantente atento a nuestro https://github.com/documenso/documenso/issues para estar al día!