Declaración de (No)Dependencias para ESM

Fecha de publicación: 2026-02-25

Mi nombre es Alex Gusev y hoy voy a contar cómo ChatGPT me convenció de reescribir la biblioteca @teqfw/di, que llevo cultivando con cuidado desde 2019, y por qué al final acepté.

Esta biblioteca JS me permite usar enlace tardío en mis aplicaciones web y escribir código isomórfico que funciona sin cambios en el navegador y en el backend. Sin transpilar el código fuente, sin registrar dependencias a mano, tal como estaba acostumbrado a hacerlo en Java y PHP. Durante casi siete años revisé manualmente cada línea de esta biblioteca, y la semana pasada la dejé en manos de un agente Codex. Esto fue lo que hizo con ella.

KDPV basada en la canción "Dva korablya" de Agata Kristi.

Lo que tenía

Tenía una biblioteca que permitía prescindir de importaciones estáticas en el código de aplicación (enlace temprano) y aplicaba el principio de Inversion of Control mediante inyección de dependencias en el constructor (enlace tardío). Mi código de aplicación típico se veía así:

export default class Namespace_Package_Module_Component {
    constructor({
        Namespace_Package_Module_Dep1: dep1,
        Namespace_Package_Module_Dep2: dep2,
    }) { ... }
}

Mi contenedor de objetos (después de la configuración, claro está) analizaba las claves del objeto pasado al constructor y creaba por nombre de clave las dependencias que el constructor necesitaba (tengo muchos artículos sobre este tema, donde se describe en detalle toda la mecánica).

Si era necesario, a las dependencias se les podían añadir bibliotecas del propio nodejs o paquetes de ./node_mpdules/ (usando el prefijo node: en el nombre de la clave). En resumen, al final las importaciones estáticas solo quedaban en composition root (bootstrap) y en @teqfw/di.

Tiempos de cambio

El problema llegó de donde no se esperaba - de los agentes LLM. En el sector IT se ha consolidado una idea pública de que los agentes LLM deben ayudar a crear código. Que están entrenados con toneladas de ejemplos y superan no solo a los juniors, sino también a los mid-level. ¡Lo único que hace falta es un prompt claro!

Bueno, yo no podía quedarme al margen de esa ola - lo probé (una vez, dos, tres). Sí, efectivamente, los agentes (al menos Codex) pueden crear código si se les dan instrucciones claras. Escriben código horrible (desde mi punto de vista), pero funciona. Y lo más importante: lo hacen rápido. Muy rápido. Casi tan rápido como decir "pastel de arándanos".

Sí, tengo un estilo algo peculiar de enlazar archivos JS, un estilo específico de formatear módulos ES6 y de usar anotaciones JSDoc. Pero todo eso se resolvía con documentación normal. Los agentes son disciplinados, por más que se diga de su naturaleza estocástica.

Naturalmente, no podía evitar confiar a los agentes el desarrollo de mi paquete más básico - @teqfw/di. Y la semana pasada lo hice.

Lo que obtuvo el agente

GPT y yo pasamos toda la semana escribiendo el cuerpo de documentación según mi propia metodología ADSM (también tengo publicaciones sobre eso). Íbamos de nivel en nivel, alineando descripciones, terminología, funcionalidad, arquitectura, convenciones, etc. Yo ya lo sabía todo, claro, pero había que dejarlo por escrito en una documentación contextual que los agentes pudieran usar para escribir código (o más bien, reescribirlo).

Con los agentes hubo entendimiento total, bajo mi presión cuidadosa, hasta el momento mismo de generar el código de la biblioteca. Decidí cambiar un poco la gramática de codificación de dependencias para poder obligar a tsserver en vscode a entender el código generado mediante anotaciones JSDoc. Había un par de detalles desagradables en mi gramática actual que chocaban con JSDoc.

Así que le di un poco de libertad al agente en este punto. El agente propuso: a partir de ahora, en el código de aplicación describirás las dependencias así:

export const __deps__ = {
    default: {
        dep1: 'Namespace_Package_Module_Dep1',
        dep2: 'Namespace_Package_Module_Dep2',
    },
};
export default class Namespace_Package_Module_Component {
    constructor({ dep1, dep2 }) { }
}

“¡Uf! — le digo. — ¡Genial! ¿Y por qué así? ¡Eso duplica los lugares donde se describen las dependencias! Y si además añades una anotación @typedef de JSDoc, sería todavía más!”

Y él me responde: “A mí me resulta más cómodo. Me da igual si son uno, dos o tres lugares, siempre que sigan una plantilla. Su DRY me da lo mismo, sobre todo si todo está en un solo archivo y no hay que ir a ningún sitio.”

Y empezó a insistir con argumentos del tipo: tu forma de ensamblar dependencias solo funciona de forma recursiva (¡como si yo no lo supiera!), al crear el objeto mediante el contenedor, mientras que mi forma (es decir, la suya) permite analizar todo el árbol de dependencias sin crear objetos (bueno, argumento, no lo discuto).

Resumen

¿Sabes cuál fue el argumento de hierro del agente? ¡La práctica! No solo construyó en un par de iteraciones código en la biblioteca (código horrible, repito, pero funcional!), que introduce (ya según su esquema) en JS enlace tardío sin transpilar y sin registro manual de dependencias en el contenedor (¡su contenedor!). Además, en una sola iteración (2-3 minutos) migró mi último proyecto del viejo esquema de declaración de dependencias (el mío) al nuevo (el suyo).

Creamos reglas de desarrollo de software para personas, y ahora hay que reescribir esas reglas para agentes. Y a los agentes DRY les da igual, así es. Ellos necesitan claridad:

export const __deps__ = {
    configManager: 'Ttp_Back_Configuration_Manager$',
    storage: 'Ttp_Back_Storage_Repository$',
    aggregateFactory: 'Ttp_Back_Aggregate_Factory$',
    telegramReader: 'Ttp_Back_External_TelegramReader$',
    telegramPublisher: 'Ttp_Back_External_TelegramPublisher$',
    llmTranslator: 'Ttp_Back_External_LlmTranslator$',
    promptProvider: 'Ttp_Back_Prompt_Provider$',
    logger: 'Ttp_Back_Logger$',
};

Y eso es todo. Sin moraleja.