openlang

Хитрые скрипты сборки это налог на следующего читателя

Попался build.gradle, где половина логики жила в замыканиях, которым во время выполнения подменяли delegate. Долго разбирался, что здесь что значит.

Groovy это только поощряет. По умолчанию замыкание ищет неквалифицированные имена сначала у владельца, а DSL меняет порядок, выставляя resolveStrategy в Closure.DELEGATE_FIRST. Для задокументированного блока настроек нормально. Мучение начинается, когда вспомогательная функция из другого файла меняет делегат и голое version тихо начинает значить что-то другое.

Скрипты сборки читают гораздо чаще, чем пишут, и читает обычно тот, кто пришёл поднять версию одной зависимости. Поэтому правило у меня скучное. В скрипте только декларативные блоки. Всё, где есть цикл или условие, уходит в класс плагина с именем и тестом. Никаких фокусов с metaClass в сборке.

Обычно возражают, что файлов становится больше. Да. Зато только так следующий читатель узнает, на что ссылается version, не проходя сборку по шагам в отладчике.

Разговор продолжается 4

Следуя доводам
  1. Ссылка на ответ

    Спотыкаются читатели на переменных экземпляра. Большинство Ruby DSL выполняют блок через instance_eval, поэтому внутри self становится получателем, голые вызовы методов уходят к нему, и переменные экземпляра тоже его. Так что @config внутри блока это не тот @config, что в окружающем коде, и в самом коде об этом ничего не сказано. То же искушение, что и делегат в Groovy, только в пальто получше.

    Для всего, что будет править посторонний человек, я предпочитаю блоки, которые передают объект, в виде configure do |c| с c.version = ... в каждой строке. На два символа больше в строке, зато у каждого имени виден хозяин.

  2. Ссылка на ответ

    Вот на тестах я бы настаивал сильнее всего. Логика сборки с тестом читается как спецификация того, что сборка делает. Без теста единственное описание это сам скрипт, причём написанный на самом нечитаемом диалекте в репозитории.

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

    Тесты плагинов у вас запускаются при каждом изменении или только когда кто-нибудь вспомнит?

  3. Ссылка на ответ

    С tsconfig то же самое. Корневой конфиг с несколькими слоями extends и переопределениями для каждого пакета читается не легче хитрого скрипта сборки, и никто не скажет, какая опция победит, пока не запустит tsc --showConfig.

    Схема, которой я доверяю, это один базовый файл, один extends на пакет и references, где перечислены настоящие зависимости и больше ничего. У проектов, на которые ссылаются, должен быть включён composite, и тогда tsc --build пересобирает только те проекты, у которых изменились входные файлы или объявления выше по цепочке. Читаемая схема заодно и быстрее собирается, и на это приятно иметь возможность указать.