Fénix: el Design System de PeYa
El rápido crecimiento de una empresa trae nuevos desafíos, y PeYa es un claro ejemplo. En el último tiempo, la compañía se ha transformado para sustentar gran escalabilidad, y lo que una vez funcionó, hoy tal vez ya no tanto.
Para lograr esta escalabilidad, desde la tribu de Platform creamos las bases para que los equipos puedan trabajar de forma más autónoma, pero a la vez manteniendo la sinergia, comunicación y coordinación con el resto de la empresa. Para esto creamos procesos, estándares, herramientas, librerías, espacios de discusión, entre muchos otros.
En los últimos meses, el equipo de AppsCore y UX Ops está llevando adelante grandes cambios en algunos de estos procesos y herramientas; se trata de la construcción de un Design System para la compañía. En este post daremos más detalles sobre cómo se está llevando esto a cabo esta particular y original implementación desde un enfoque más tecnológico.
Problemas
En un producto de gran adopción como es el de PeYa, la experiencia de usuario es de suma importancia. Es imperativo que sea usable y accesible, o de lo contrario podría impactar directamente en las KPIs del negocio.
Dicho esto, y dado que la calidad del proceso está ligado directamente a la calidad del producto, es importante tener un proceso de desarrollo sólido. Dado el rápido crecimiento de la empresa, muchos procesos y herramientas ya no son tan eficientes como antes; es hora de identificar oportunidades de mejora y crear mejores soluciones.
A continuación se detallarán brevemente algunos de los problemas identificados en el proceso de desarrollo de la interfaz gráfica de nuestro producto.
Duplicación y Desgaste
La duplicación es algo frecuente en el desarrollo de frontends en equipos grandes, donde se puede encontrar un mismo componente de UI implementado varias veces. Esto puede ocurrir por poca comunicación entre equipos. Es una ineficiencia en el proceso, que le cuesta horas de retrabajo a la empresa.
Para evitarlo, en iOS, hace un tiempo se creó una librería común llamada PeyaKit, donde viven la mayoría de los componentes de UI. De esta manera los desarrolladores tienen un lugar común donde compartir y reusar estos componentes. Esto arregla muchos problemas, pero con el tiempo esta librería se ha “desgastado”:
- Componentes se han modificado bastante con el tiempo, haciéndolos difíciles de mantener y generando bugs
- Nombres de primitivas de diseño (como colores, fuentes, etc.) no están normalizados y preparados para temas
- Se han agregado muchos helpers o sugar syntaxes de UI que “ensucian” la librería, muchas de las cuales ya no se usan, o tienden a repetirse y dan muchas formas de lograr lo mismo
- Se han agregado dependencias de terceros que aumentan el tamaño de la aplicación
Algo parecido puede también suceder desde diseño. Al haber muchos equipos con diferentes desarrolladores y UXers, puede ocurrir que se creen varios diseños de un mismo componente con pequeñas diferencias, rompiendo la reusabilidad. Esta es otra posible ineficiencia, que puede causar un costo extra en horas de diseño y desarrollo, como también inconsistencia visual entre pantallas.
Comunicación y Reutilización
Es de suma importancia que los desarrolladores entiendan los diseños, o de lo contrario puede causar gran cantidad retrabajo. Hoy en día se usa Figma y documentación para realizar este pasaje de UX a desarrollo, lo cual es bueno, pero no suficiente para una aplicación de esta escala.
Actualmente, dada la naturaleza desacoplada y cohesiva de los equipos, se da lugar a que haya desconexiones, y que creen soluciones paralelas. Es por esto que se necesita establecer un acuerdo y un lenguaje común entre las partes, que logre una comunicación eficiente sin ambigüedades.
Y es esencial hacer uso del concepto de reutilización, que está bastante ejercitado del lado de desarrollo, pero tal vez no tanto del lado de diseño. Sería una gran mejora lograr un consenso de principios y guías de diseños entre todos los equipos de UX, centralizar esto en un solo lugar y que pueda ser reutilizado por cualquiera dentro de la empresa.
Soporte a nuevas tendencias
Desde el equipo de iOS, por ejemplo, se quieren empezar a adoptar nuevas tecnologías y hacer más énfasis en algunas prácticas, como por ejemplo, SwiftUI, Combine, Dark Mode y Themes, accesibilidad, UI Testing, entre otros. Algunos de éstos son actualmente difíciles de soportar dado por cómo están implementadas las librerías de UI.
Soluciones
Como punto de partida, estaba claro que se debía crear un Design System.
Fénix: el Design System
Un Design System define un repertorio de elementos que pueden ser usados por el que lo necesite, creando una “única fuente de verdad”.
Pueden haber diferentes tipos de definiciones, como principios, filosofías, patrones, guías de estilos, componentes, estándares de lenguaje, etc. Diseñadores, desarrolladores, community managers, marketing, o algún otro interesado, pueden y deben utilizar este Design System para realizar sus diseños, para así mantener una coherencia en los diferentes productos y canales.
Esto da muchas ventajas: acelera la velocidad de desarrollo, crea un lenguaje común entre los equipos, mantiene la consistencia, fomenta la reutilización, entre muchos otros.
Pero además se incorporaron más metodologías y prácticas, que detallaremos a continuación.
Atomic Design
En Fénix se utiliza la metodología de Atomic Design. Como se puede inferir de su nombre, se inspira en la química y su modelo atómico para modularizar los componentes de UI. Se basa en 5 niveles: átomos, moléculas, organismos, plantillas y páginas. Cada nivel puede estar compuesto por elementos de niveles anteriores, pero no al revés. En Fénix además definimos un nivel base llamado foundations, que son mas las primitivas o “materia prima” que los otros niveles utilizarán, como colores, fuentes, iconos, entre otros.
Esto ayuda a los que participan a entender y hacer uso del concepto de reutilización, y esto ayuda mucho a los desarrolladores.
Tokens
Además de Atomic Design, una particularidad que tenemos en nuestro proyecto es el uso de Design Tokens.
Los tokens son como variables de programación; tienen un nombre y apuntan a un valor. Por ejemplo, “primaryColor” puede ser el nombre del token, y el valor al que apunta "#FF0000". Pero además, en nuestro caso, los tokens pueden ser más sofisticados: pueden apuntar a otros tokens, o definir muchos valores. A modo de ejemplo, un token “paragraphTypography” puede apuntar a tokens como “primaryFont”, “pimaryTextColor”, “letterSpacingSmall”, etc., y a su vez éstos apuntan a sus respectivos valores.
En Fénix hay 3 niveles de tokens: Tokens de Referencia, Tokens de Sistema y Tokens de Componentes, al igual que como lo hace Material Design, el Design System creado por Google.
Los Tokens de Referencia son más aquellas primitivas que, por lo general, ya tienen un valor asignado directamente. Los Tokens de Sistema apuntan a Tokens de Referencia, y son los que más se usan a la hora de desarrollar. Por último, los Tokens de Componentes apuntan a Tokens de Sistema o Referencia, y, como bien lo dice el nombre, son Tokens específicos para componentes.
Este sistema tiene muchas ventajas.
Primeramente, se establece una especie de lenguaje que es entendido por todos, incluyendo UX, desarrolladores y computadoras. Esto nos asegura que todos usen la misma fuente de información para las medidas, colores, fuentes, etc., ya sea si se está trabajando en Figma, Javascript, Swift o Kotlin.
Segundo, al tener una arquitectura en niveles, es más fácil cambiar los valores dinámicamente en tiempo de ejecución, sin cambiar el nombre de los tokens. Podría verse como algo análogo al polimorfismo, y es práctico para soportar Dark Mode por ejemplo. Para esto es importante que el nombre de un Token de Sistema o Token de Componente esté desacoplado de su valor. Hay que evitar nombres como “yellowColor”, ya que lo que es amarillo, puede ser marrón en Dark Mode. Estos cambios dinámicos pueden realizarse en código en la aplicación, como también en Figma, creando una consistencia entre plataformas y herramientas muy interesante.
Por último, los tokens pueden ser leídos por computadoras, lo que nos abre las puertas a automatizar aún más el proceso. De esto hablaremos más adelante.
UX Ops
Se creó el equipo de UX Ops, un equipo más core, algo análogo al equipo de AppsCore en tecnología. En conjunto, estos dos equipos son los encargados de construir el Design System.
Proceso
Restricciones
Para que se respete el uso del Design System en el proceso, se implementaron ciertas restricciones.
No vamos a entrar en muchos detalles a nivel de UX, pero para dar una idea, UX Ops realiza revisiones de los diseños (algo similar a una pull request), que se deben rechazar si no cumplen con el Design System. Igualmente se respeta cierto grado de experimentación y nuevas ideas, para fomentar la creatividad y la creación de nuevos componentes.
Además, para el futuro se planea implementar un plugin de Figma que sirva de linter, y que corra de forma periódica o a demanda para mostrar errores o advertencias en un diseño.
A nivel de tecnología, se trata de hacer respetar el uso del DS de varias maneras.
Primero, se deprecan las viejas librerías a favor de la librería de Fénix, para progresivamente ir migrando.
Luego, se privatizan APIs para no poder cambiar atributos definidos por el DS. Se cierra la puerta a personalizaciones de colores y fuentes que no debieran ocurrir. Si un diseño no respeta el DS y llega a un desarrollador, éste se va a chocar con que la librería no lo soporta, generando preguntas, y es un poco la idea del proceso. Esto suena rígido, pero se tiene el apoyo desde UX para realizarlo de esta manera; UX Ops debe participar activamente en el proceso para evitar este tipo de situaciones.
Por otro lado, se están implementando linters de código hechos a medida (principalmente del lado Web y Android). También se aprovechan los lenguajes fuertemente tipados, como Kotlin y Swift, que tiran error en tiempo de compilación si no se respetan los tipos esperados.
Pipeline Automatizado
En un mundo ideal, el código se generaría automáticamente a partir de un diseño, pero esto es obviamente difícil de lograr. Pero en PeYa se apostó en desarrollar algo cercano a esto, y es una de las partes más interesantes del proyecto.
Como se mencionó anteriormente, en el proyecto se utilizan Design Tokens, y éstos se usan de igual manera en Android, iOS y Web (y parcialmente en Figma). Inmediatamente podemos ver una oportunidad de automatización en el pasaje de tokens a código. Y es así como lo hicimos.
Utilizando diferentes herramientas, se está construyendo un pipeline automatizado, donde UX puede realizar cambios y automáticamente se propaga en todas las plataformas, sin intervención de los desarrolladores.
A continuación un simplificado diagrama de cómo funciona.
Los tokens y diseños son definidos en Figma utilizando el plugin Figma Tokens, que permite generar sets de tokens, aplicarlos en Figma en forma dinámica, y exportarlos en formato JSON. Aquí se especifican los Tokens de Referencia y Tokens de Sistema. Al realizar cambios, el plugin se sincroniza automáticamente con un repositorio base del DS, llamado “Fenix-Base”. Este repositorio contiene todos los tokens y otros recursos (como imágenes, animaciones, etc.) necesarios para implementar el Design System.
Una vez detectado un cambio en el repositorio base, se activa un webhook desde Github que dispara 4 jobs de Jenkins en paralelo, 3 para cada plataforma y otro para actualizar la documentación (este último aún no se ha implementado).
En estos jobs se corren generadores de código (aplicaciones de línea de comando desarrolladas por nosotros), que transforman JSONs a código productivo de cada plataforma. Por ejemplo, en iOS se genera código Swift, o Kotlin para Android, y se agrega al repositorio de la librería de Fénix específico para cada plataforma. En el caso de Web no se genera código, sino que se lee dinámicamente los JSONs en tiempo de ejecución.
Luego, el pipeline corre diferentes chequeos: se corren tests unitarios, Snapshots Tests, tests de UI, tests de accesibilidad, linters, entre otros. Aquí también se puede chequear que tokens ya existentes no se borren o se cambien de nombre, ya que puede causar un breaking change en la API pública de la librería.
Si estos chequeos pasan, luego se genera una pull request en cada uno de los repositorios de las plataforma para ser revisados por el equipo, y eventualmente ser integrada en la siguiente liberación de la librería.
En cuanto a los Tokens de Componentes se realiza un trabajo más manual, donde se crea un JSON a mano entre UX y AppsCore para cada componente. Aquí se especifican, por ejemplo, los colores, tipografías, espaciados, tamaños, etc. que se utilizan al momento de desarrollar los componentes. Aunque aún se desea automatizar más esta parte, por el momento no se ha encontrado una forma fácil de hacerlo.
Librería Fénix
Se ha creado una librería “Fénix” para cada plataforma, que contiene todas las bases y componentes de desarrollo necesarios, con los cuales los desarrolladores pueden desarrollar sus frontends.
Como ya se explicó anteriormente, muchos archivos de esta librería son auto-generados a partir de una única fuente de verdad entre plataformas.
Esta librería contiene las foundations, como colores, tipografías, íconos, etc.; y también componentes, como átomos, moléculas u organismos. Luego las páginas finales serán implementadas en los propios módulos de los diferentes equipos, utilizando los componentes que la librería provee, como si fueran bloques de Lego.
Esta librería solamente expone públicamente lo necesario, pero posee diferentes mecanismos internos, como por ejemplo, para poder soportar diferentes themes o layouts.
También se separa el código de helpers en una librería separada a la del Design System, para no “ensuciarla”, y poniendo aquí solamente lo imprescindible. Cada equipo luego puede crear sus propios helpers o sugar syntaxes más opinables internamente en sus módulos.
La mayoría de los componentes de UI se crearon desde cero utilizando buenas prácticas, para que sean mantenibles, accesibles y fáciles de utilizar. Se utilizan animaciones y otras interacciones como haptics. Todo esto evitando incluir librerías de terceros.
Desarrollo de pantallas
La librería de Fénix contiene las piezas de Lego para armar una pantalla, pero obviamente éstas las arman los diferentes equipos. Para esto los UXers crean el diseño de la pantalla en Figma, utilizando los componentes disponibles en el Design System.
Estos diseños son compartidos con los desarrolladores, que inmediatamente encuentran que los nombres que especifica el diseño coinciden con los utilizados en la librería. Esto es gracias a este lenguaje compartido que se creó a través del Design System, Atomic Design y Design Tokens.
Siguientes pasos
Algunos de los puntos que aún se quiere trabajar en el futuro son:
- Desarrollo de un plugin de Figma que sirva de linter, para hacer respetar el Design System de forma más automatizada entre UXers
- Automatizar la generación de la documentación, mucha de esta información podría venir de los propios JSONs, acompañados de ejemplos de código
- Soportar dentro de Figma cambio dinámico de temas, como Dark Mode
- Automatizar el deprecado de Tokens ya existentes desde Figma o JSONs
En este post se hizo un breve resumen de la construcción del Design System desde un enfoque más tecnológico. Se realizó un análisis de los problemas y se presentaron las soluciones que estamos llevando adelante.
Se está construyendo el Design System de PeYa, Fénix, utilizando novedosas metodologías y herramientas. Con Tokens de Diseño se creó un lenguaje común entre todas las partes involucradas (incluyendo computadoras) facilitando la comunicación. Con Atomic Design, los equipos de desarrollo pueden armar pantallas rápidamente reutilizando componentes como si fueran bloques de Lego, y evitando posibles incoherencias visuales.
Todo esto conforma la “arquitectura” del Design System, que, como si fuera la arquitectura de un software, luego se plasma en el código de las librerías que los desarrolladores usarán para armar sus frontends.
Pero sin lugar a dudas, la parte más interesante es la automatización de generación código desde Figma a partir de tokens. UXers pueden realizar cambios, y en minutos tenerlos deployado en las librerías de todas las plataformas, con solo dos clics, evitando dependencias con ingeniería.
En estos últimos meses los equipos de AppsCore y UX Ops se enfrentaron a un montón de incertidumbres y de trabajo conjunto, mostrando su creatividad e ideas para resolver diferentes problemas. Todo esto es posible gracias al ambiente de trabajo que propicia PeYa como empresa, fomentando las ideas e innovación en sus equipos, siempre priorizando la calidad y escalabilidad.
¡Nos encantaría conocer tu opinión, los leemos!
Damos las gracias a Facundo Coto, Raymond Arteaga, Mauricio López y Ayelén López por todo el esfuerzo invertido en la construcción del Design System.