---
title: "Декларация (не)зависимостей для ESM"
date: 2026-02-25
---

<zoom-img
            src="https://habrastorage.org/r/w1560/getpro/habr/upload_files/e70/f5f/89c/e70f5f89c3ce225e396777a85357c14e.png"
            alt="Декларация (не)зависимостей для ESM"
            width="100px"
            style="float: left; margin: 15px 15px 0 0;"
    ></zoom-img>

Меня зовут Алекс Гусев и сегодня я расскажу о том, как ChatGPT убедил
меня переписать библиотеку **@teqfw/di**, которую я бережно "выращиваю"
с 2019-го года, и почему я всё-таки убедился.

Эта JS-библиотека позволяет мне использовать в своих веб-приложениях
позднее связывание и даёт возможность писать изоморфный код, который без
изменений работает и в браузере, и на бэке. Без транспиляции исходников,
без ручной регистрации зависимостей - так, как я привык делать в Java и
PHP. Я почти 7 лет вручную выверял каждую строку этой библиотеки, а на
прошлой неделе я отдал её на откуп Codex-агенту и вот что он с ней
сделал.

КДПВ по песне "Два корабля" группы "Агата Кристи".

Что было у меня

У меня была библиотека, которая позволяла отказаться от статических
импортов в прикладном коде (раннее связывание) и реализовывала принцип
Inversion Of Control через внедрение зависимостей в конструктор (позднее
связывание). Типовой прикладной код у меня выглядел так:

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

Мой контейнер объектов (после конфигурации, разумеется) анализировал
ключи объекта, передаваемого в конструктор, и создавал по именам ключей
нужные конструктору зависимости (у меня много статей на эту тему, там
вся механика описана в деталях).

При необходимости в зависимости можно было добавлять библиотеки самого
nodejs или пакетов из ./node_mpdules/ (через префикс node: в имени
ключа). В общем, под конец у меня вообще статические импорты были только
в composition root ( bootstrap ) и самой @teqfw/di.

## Время перемен

Беда пришла, откуда не ждали - со стороны LLM-агентов. В IT-сфере
сложилось публичное представление, что LLM-агенты должны помогать
создавать код. Что они натасканы на тоннах примеров и затыкают за пояс
не только джунов, но уже и мидлов. Всё, что нужно - нормальные понятные
промпты!

Ну, я ж не мог остаться в стороне от такого движа - я попробовал (раз,
два, три). Да, действительно - агенты (как минимум, Codex) могут
создавать код, если им дать понятные инструкции. Они пишут ужасный (с
моей точки зрения) код, но он работает. И главное - они делают это
быстро. Очень быстро. Почти так же быстро, как сказать "черничный
пирог".

Да, у меня несколько необычный стиль связывания JS-файлов, специфический
стиль оформления es6-модулей и применения JSDoc-аннотаций. Но это всё
решалось обычной документацией. Агенты - ребята дисциплинированные, кто
бы что бы там ни говорил про их стохастическую природу.

Разумеется, я не мог не попробовать поручить агентам разработку самого
базового своего пакета - @teqfw/di. И на прошлое неделе я это сделал.

## Что стало у агента

Мы с GPT-чатиком всю неделю сидели писали корпус документации согласно
моей же методологии ADSM (тоже в моих публикациях есть и про это).
Ходили кругами от уровня к уровню, выравнивали описание, терминологию,
функционал, архитектуру, соглашения и т.п. Ну, я-то всё и так знал, но
нужно было доступно изложить это в виде контекстной документации
агентам, которые будут писать код (вернее - переписывать).

С агентами было полное взаимопонимание - под моим чутким давлением до
самого момента генерации кода библиотеки. Я решил слегка изменить
грамматику кодирования зависимостей, чтобы можно было заставить tsserver
в vscode понимать создаваемый код через JSDoc-аннотации. Было там пару
неприятных моментов в моей текущей грамматике, что конфликтовали с
JSDoc.

Ну я и дал в этом вопросе немного свободы агенту. Агент выдал - отныне
ты в прикладном коде будешь описывать зависимости так:

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

"Ух! - говорю я. - Здорово! А зачем так? Это ж удвоение мест описания
зависимостей!! А если ещё и @typedef аннотацию JSDoc добавить - то и
утроение!!!"

А он мне и отвечает - "А мне так удобнее! Мне всё равно, одно-два-три
места, если по шаблону. Мне ваш DRY до лампочки, особенно если всё в
одном файле и ходить никуда не надо."

Ну и начал давить аргументами, типа твой способ сборки зависимостей
работает только рекурсивно (а то я не в курсе!), при создании объекта
контейнером, а мой (т.е. - его) способ позволяет проанализировать всё
дерево зависимостей без создания объектов (ну, аргумент, не спорю).

## Резюме

Знаете, что стало железобетонным аргументом со стороны агента? Практика!
Мало того, что он за пару подходов соорудил в библиотеке код (ужасный
код, повторяю, но - рабочий!), который привносит (уже по его схеме) в JS
позднее связывание без транспилции и ручной регистрации зависимостей в
контейнере (его контейнере!). Так он ещё за одну итерацию (2-3 минуты)
перевёл мой последний проект со старой схемы декларации зависимостей
(моей) на новую (свою).

Мы создавали правила разработки ПО для людей, а теперь нужно
переписывать эти правила под агентов. А агентам DRY до лампочки, так-то.
Им нужна ясность:

``` js
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$',
};
```

Поэтому и вот. Моралей не будет.
