El problema del ecosistema: menos librerías, ventaja y bloqueo
La objeción que más veces derriba una propuesta de adoptar Elm no habla del lenguaje sino de lo que falta a su alrededor, y suele formularse con una comparación de cifras que parece zanjar la discusión antes de empezarla: cientos de miles de paquetes en un registro frente a unos pocos miles en el otro. Esta lección se toma la objeción en serio y la desarma por partes. Primero establece qué falta exactamente, distinguiendo entre categorías donde la ausencia es total, categorías donde existe alternativa idiomática y categorías donde el ecosistema pequeño resulta ser suficiente porque el lenguaje ya trae en su núcleo lo que en otros sitios requiere una dependencia. Después construye el argumento incómodo de que un catálogo reducido produce efectos de segundo orden favorables: menos superficie de ataque, menos decisiones triviales, menos rotación de modas, versiones semánticas garantizadas por el compilador y una probabilidad muy alta de que la librería que existe sea la que todo el mundo usa. Y termina en el terreno donde la objeción tiene razón sin matices, enumerando los bloqueos reales que han hundido adopciones y las tres salidas técnicas disponibles cuando aparecen.
Comparar registros de paquetes contando entradas es una operación tan fácil de hacer como difícil de interpretar. El registro grande incluye decenas de miles de paquetes abandonados, miles de duplicados que resuelven el mismo problema con diferencias mínimas, una cantidad considerable de envoltorios triviales de una función, y una fracción no despreciable de paquetes cuya única razón de existir es rellenar un hueco que el lenguaje base dejó abierto. El registro pequeño no tiene nada de eso, y por tanto la comparación de tamaños mide algo distinto de lo que aparenta medir. Pero sería una trampa quedarse ahí, porque después de descontar todo el ruido sigue quedando una diferencia enorme y perfectamente real: hay categorías enteras de problemas resueltos en un ecosistema y sin resolver en el otro, y ninguna cantidad de argumentación sobre la calidad frente a la cantidad hace aparecer un editor de texto enriquecido que no existe. Esta lección busca el punto exacto donde la objeción deja de ser un prejuicio y pasa a ser un dato, porque ese punto es el que decide proyectos.
- Descomponer la carencia de ecosistema por categorías y distinguir ausencia total de existencia de alternativa idiomática.
- Explicar por qué buena parte del catálogo de un registro grande cubre huecos que
Elmno tiene abiertos. - Enunciar los efectos de segundo orden favorables de un catálogo reducido sin caer en la coartada de la calidad frente a la cantidad.
- Reconocer los bloqueos reales y elegir entre las tres salidas técnicas disponibles con criterio de coste.
Qué falta exactamente
Una carencia enunciada en general no sirve para decidir nada, así que conviene desglosarla. Hay una primera categoría donde la ausencia sencillamente no existe porque el lenguaje ya la cubre en su núcleo o en paquetes oficiales: manejo de listas y diccionarios, análisis y generación de JSON, peticiones al servidor, enrutado, tiempo, aleatoriedad, animación básica y pruebas. En un ecosistema grande cada una de esas cosas se resuelve eligiendo entre varias dependencias, comparándolas y asumiendo su mantenimiento; en Elm viene decidido. Una parte sustancial de la diferencia de tamaño entre registros se explica exactamente aquí, y es la parte de la que la comparación numérica nunca informa.
La segunda categoría es la de los problemas para los que existe una solución en Elm, pero menos madura o menos completa que la de referencia del otro lado. Aquí entran las librerías de gráficos, los formularios complejos con validación declarativa, las tablas con ordenación, filtrado y virtualización, la internacionalización y el manejo de zonas horarias. Existen, se usan en producción, y hay que aceptar que cubren el ochenta por ciento de los casos y que el veinte restante se escribe uno mismo. Esa es una negociación normal y casi siempre asumible.
La tercera categoría es donde duele, y hay que nombrarla sin rodeos. Editores de texto enriquecido, visores de documentos, mapas interactivos con capas y comportamiento avanzado, editores de código, componentes de calendario con todas las reglas de recurrencia, integraciones con proveedores de pago y de autenticación que publican su propio widget. Son piezas de enorme complejidad acumulada, muchas veces con años de trabajo dentro, y reescribirlas no es una opción realista para casi ningún equipo. En Elm la salida no es reescribirlas sino envolverlas, y el coste de esa envoltura es lo que hay que estimar antes de comprometerse.
Hay una cuarta categoría que casi nunca se menciona y que conviene añadir porque cambia el balance: la de los problemas que en un ecosistema grande se resuelven con una dependencia y en Elm sencillamente no se plantean. La gestión de estado global, con su mercado de librerías competidoras y sus generaciones sucesivas de soluciones, no existe como problema porque el estado ya vive en un solo sitio por construcción. La inmutabilidad, que en otros entornos requiere una librería o una disciplina, es la única semántica disponible. La comparación estructural profunda de valores, la normalización de datos remotos y buena parte de las herramientas de depuración de flujo tampoco tienen mercado, porque el bucle único y el registro de mensajes las hacen innecesarias. Al comparar catálogos, esta categoría se cuenta a favor del registro grande cuando en realidad mide un problema que el otro lenguaje no tiene.
Hay un experimento mental que aclara la comparación. Imagina dos lenguajes idénticos salvo en que uno trae en su biblioteca estándar el manejo de fechas, la inmutabilidad, la validación de datos externos y la comparación profunda de valores, y el otro no trae ninguna de las cuatro. El segundo tendrá un registro visiblemente más grande, y esa diferencia no indica riqueza sino deuda: son paquetes que existen porque hacían falta. Cuando midas ecosistemas, descuenta primero todo lo que el otro lenguaje resuelve sin dependencias, después los duplicados, después los abandonados, y quédate con lo que sobrevive. Lo que sobrevive sigue siendo mucho, y por eso el argumento no se gana con este descuento; pero sin hacerlo la conversación empieza con una cifra que no significa lo que parece.
Por qué la escasez produce efectos favorables
El argumento de que menos es mejor suena a coartada, y lo es cuando se enuncia sin mecanismo. Con mecanismo, deja de serlo. El primero es la superficie de ataque y de mantenimiento: cada dependencia transitiva es código que se ejecuta con los mismos privilegios que el tuyo, escrito por alguien a quien no conoces, actualizado por alguien que puede cambiar. En Elm las dependencias son código de Elm puro, no pueden ejecutar efectos arbitrarios porque el lenguaje no lo permite, y no pueden ejecutar scripts de instalación. Un paquete malicioso de Elm tiene un techo de daño que un paquete malicioso de npm no tiene, y ese techo lo pone el lenguaje, no la auditoría.
El segundo mecanismo es el versionado semántico impuesto por herramienta. El gestor de paquetes de Elm compara la interfaz pública de la versión nueva con la anterior y calcula el número de versión que corresponde; no lo sugiere, lo exige. Un cambio incompatible no puede publicarse como parche porque el compilador lo detecta. La consecuencia práctica es que actualizar dependencias deja de ser una operación de riesgo con notas de versión que hay que leer con desconfianza, y pasa a ser una operación cuyo peor caso el compilador enumera.
Conviene detenerse un momento en la magnitud de esa garantía, porque no tiene equivalente en ningún otro registro de paquetes de uso masivo. En los demás, la versión que un autor publica es una declaración de intenciones: dice que el cambio es compatible porque él cree que lo es. En Elm la versión es el resultado de una comparación automática entre dos interfaces, y publicar una menor cuando corresponde una mayor es sencillamente imposible. La consecuencia sobre la práctica diaria es que la instalación de una versión nueva dentro del mismo rango no puede romper la compilación por un cambio de firma, y que cuando la rompe es porque la herramienta ya lo anunció con el número.
El tercero es la ausencia de decisiones triviales. Elegir entre cuatro librerías equivalentes de peticiones al servidor consume tiempo, produce inconsistencia entre módulos escritos en momentos distintos, y genera discusiones que no aportan valor al producto. Cuando hay una sola forma idiomática, ese coste desaparece por completo y con él desaparece una fuente constante de fragmentación interna.
El cuarto es menos evidente y quizá el más valioso a largo plazo: la ausencia de rotación de modas. En un ecosistema grande, la solución idiomática a un problema cambia cada pocos años, y esa rotación tiene un coste doble. Uno es directo, en forma de migraciones que no añaden funcionalidad ninguna y que hay que justificar ante quien paga. El otro es indirecto y peor: una base de código de cierta edad acumula estratos, cada uno escrito con la solución que era idiomática en su momento, y quien llega tiene que aprender no una forma de hacer las cosas sino tres, distinguiendo cuál corresponde a cada archivo por su fecha. En un ecosistema pequeño y estable ese estratigrafiado no ocurre, y el código de hace cuatro años se lee exactamente igual que el de esta semana.
Hay un quinto efecto que solo se aprecia en proyectos con auditoría o cumplimiento normativo, y que en esos contextos pesa más que todos los anteriores juntos. Enumerar la superficie total de código de terceros que un producto ejecuta es un requisito habitual, y en un proyecto con centenares de dependencias transitivas esa enumeración es un ejercicio de arqueología con resultado aproximado. En un proyecto de Elm la lista completa suele caber en una pantalla, todas sus entradas son código del mismo lenguaje sin capacidad de efectos arbitrarios, y ninguna ejecuta nada durante la instalación. Para un equipo que tiene que responder por escrito de lo que ejecuta, esa diferencia no es una comodidad sino un cambio de categoría en el trabajo.
-- El coste de no tener la libreria: se escribe una vez y queda dentro del sistema
module Tabla exposing (Columna, ver, ordenarPor)
type alias Columna a msg =
{ titulo : String
, celda : a -> Html msg
, clave : Maybe (a -> String)
}
ver : List (Columna a msg) -> List a -> Html msg
-- Actualizar una dependencia: el peor caso lo enumera el compilador
-- elm bump calcula la version comparando interfaces publicas
-- Un cambio incompatible no puede publicarse como parche
-- Y una dependencia no puede hacer esto, porque el lenguaje no lo permite:
-- leer variables de entorno
-- abrir una conexion de red por su cuenta
-- ejecutar un script durante la instalacion
Techo de daño
Una dependencia de Elm no puede producir efectos arbitrarios ni ejecutar scripts al instalarse. El límite lo pone el lenguaje.
Versión calculada
La herramienta compara interfaces y exige el número que corresponde. Un cambio incompatible no cabe en un parche.
Una forma idiomática
Sin cuatro alternativas equivalentes no hay fragmentación entre módulos escritos en épocas distintas.
Bloqueo real
Editores enriquecidos, mapas avanzados, widgets de proveedores. Aquí la objeción tiene razón y hay que medir el coste.
Hay un ejercicio que zanja esta discusión mejor que cualquier argumento, y consiste en hacer el inventario real en lugar de imaginarlo. Toma el archivo de dependencias de tu proyecto y clasifica cada entrada directa en cuatro cubos: la que rellena un hueco que Elm no tiene, la que tiene equivalente idiomático, la que se envolvería con coste conocido y la que bloquearía el proyecto. Casi siempre ocurre lo mismo: el primer cubo se lleva la mitad de la lista, el segundo un tercio, el tercero unas pocas entradas con coste estimable y el cuarto tiene cero elementos o tiene uno solo, y ese uno decide la respuesta. La conversación sobre ecosistemas es interminable en abstracto y se resuelve en veinte minutos con la lista delante.
Cuándo bloquea de verdad y qué se hace entonces
Llega el punto en que la objeción gana, y conviene saber reconocerlo antes de haber escrito diez mil líneas. El bloqueo aparece cuando el producto tiene como núcleo funcional una pieza de la tercera categoría, no como accesorio. Un editor colaborativo de documentos, una herramienta de cartografía, un producto cuya interfaz principal es un lienzo con manipulación directa, una aplicación que vive dentro del widget de un proveedor: en todos esos casos la envoltura no rodea un detalle, rodea el corazón, y el resultado es una aplicación de Elm que en realidad es un anfitrión de código externo con toda su complejidad concentrada en la frontera.
Cuando la pieza no es el núcleo, hay tres salidas y conviene elegirlas por coste, no por preferencia. La primera es el elemento personalizado, que envuelve el widget externo en una etiqueta del navegador con atributos y eventos, y permite usarlo desde la vista como si fuera un nodo más. La segunda es el port, que sirve cuando la integración es de flujo de datos y no de presentación. La tercera es la coexistencia: montar el programa de Elm con el constructor de elemento dentro de una página que gestiona otra tecnología, y repartir el territorio por pantallas en lugar de por nodos.
| Categoría | Qué contiene | Decisión |
|---|---|---|
| Cubierta por el núcleo | Listas, JSON, red, rutas, tiempo, pruebas |
No hay nada que decidir |
| Existe con menos madurez | Gráficos, tablas, formularios, idiomas | Se asume el ochenta por ciento |
| Tediosa pero simple | Fechas, subidas, desplegables | Escribirla sale más barato que envolverla |
| Bloqueante | Editores ricos, mapas, widgets de proveedor | Medir si es núcleo o accesorio |
La regla para elegir entre las dos primeras es sencilla y ahorra muchas discusiones. Si lo que hace falta es que algo aparezca en un sitio concreto del árbol de la interfaz, la salida es el elemento personalizado, porque su ciclo de vida lo gobierna el navegador y encaja de forma natural con el redibujado de la vista. Si lo que hace falta es enviar o recibir datos sin que haya nada que colocar en pantalla —telemetría, almacenamiento local, una conexión persistente, un cliente de un proveedor externo—, la salida es el port, porque el flujo de datos es asíncrono y no tiene posición. Confundir las dos produce los dos errores típicos: un port que intenta manipular el árbol por su cuenta y acaba peleándose con el redibujado, o un elemento personalizado que sirve de conducto para datos que no dibujan nada.
-- Salida uno: el widget externo vive dentro de una etiqueta propia
verEditor : String -> Html Msg
verEditor contenido =
node "editor-enriquecido"
[ attribute "valor" contenido
, on "cambio" (Decode.map Cambio decodificarValor)
]
[]
Queda un caso intermedio que merece mención porque se resuelve mal muy a menudo: la pieza que existe en el ecosistema grande, no existe en Elm, y sin embargo no es compleja sino solo tediosa. Ejemplos típicos son un selector de fechas sencillo, un componente de subida de archivos con progreso, una tabla con ordenación por columnas o un menú desplegable accesible. La tentación es envolver la versión externa, y suele ser un error de cálculo: escribir esas piezas en Elm cuesta días, no meses, se hace una sola vez, queda dentro del sistema de tipos y no arrastra dependencias. La envoltura, en cambio, es permanente, requiere serialización en ambos sentidos y convierte un componente trivial en una frontera que hay que mantener. La regla es medir el coste de escribirlo antes de aceptar el coste de envolverlo, porque en esta categoría la intuición se equivoca de forma sistemática.
flowchart TD
A[Necesito una pieza que no existe en Elm] --> B{Es el nucleo del producto}
B -->|Si| C[Reconsidera la eleccion de tecnologia]
B -->|No| D{Es visual}
D -->|Si| E[Elemento personalizado]
D -->|No| F[Port con protocolo explicito]
E --> G[La frontera queda en la vista]
F --> H[La frontera queda en el runtime]
style C fill:#f38ba8,color:#11111b
style E fill:#a6e3a1,color:#11111b
style F fill:#89b4fa,color:#11111bSolemos hablar del ecosistema como si fuera una despensa: cuanto más llena, mejor cocinas. La metáfora falla porque omite que cada ingrediente que tomas prestado viene con un contrato invisible que firmas sin leer. Ese contrato dice que confías en el criterio de mantenimiento de alguien que no conoces, que asumes la cadena entera de sus propias dependencias, que aceptas su ritmo de actualización y su interpretación de qué es un cambio incompatible, y que heredas su superficie de ejecución con tus mismos privilegios. Nada de eso es un argumento contra usar librerías, que sería absurdo; es un argumento para ver que la abundancia no es gratuita sino diferida, y que su factura llega en forma de actualizaciones que rompen, vulnerabilidades transitivas, piezas abandonadas que hay que sustituir con prisa y una superficie total de código que nadie del equipo ha leído jamás. La decisión de Elm no consiste en tener menos librerías, consiste en haber puesto un techo estructural a lo que una librería puede hacerle a tu programa, y ese techo tiene como consecuencia inevitable que ciertas librerías no puedan existir. Es la misma renuncia que atraviesa todo el lenguaje, aplicada aquí al código de terceros: menos poder concedido, menos incertidumbre heredada. Lo que hay que entender, y sirve para cualquier tecnología, es que evaluar un ecosistema por su tamaño es evaluar solo el lado del activo. La pregunta completa incluye el pasivo, y el pasivo se mide en cuántas líneas ejecuta tu producto que nadie de tu equipo podría explicar, y en qué le pasaría a tu programa si una de ellas hiciera algo inesperado. Un registro pequeño y cerrado y un registro inmenso y abierto no son dos puntos de la misma escala: son dos respuestas distintas a la pregunta de quién responde por lo que no escribiste.
- Lista las dependencias directas de un proyecto tuyo y clasifícalas en las tres categorías de esta lección.
- Cuenta las dependencias transitivas totales y estima cuántas líneas ejecuta tu producto que nadie del equipo ha leído.
- Elige una dependencia que rellene un hueco del lenguaje base y comprueba si
Elmtendría ese hueco abierto. - Toma la pieza más difícil de sustituir y decide cuál de las tres salidas usarías; escribe la interfaz del elemento personalizado o el protocolo del
port. - Determina si esa pieza es el núcleo del producto o un accesorio, y saca la conclusión que corresponda sin suavizarla.
- Escribe el argumento en contra de esta lección: defiende que la abundancia de un registro grande compensa todo su pasivo, con ejemplos de tu propio proyecto.