Существует специфический «эффект Makefile», напоминающий такие феномены как карго-культы, нормализация отклонений, «нечитабельный язык» и т.д. В этой статье я берусь утверждать, что Makefile — явление того же порядка, с той оговоркой, что он немного отличается от вышеперечисленных феноменов. Makefile не является по сути своей неэффективным или плохим и сказывается на результатах проектирования лишь в некоторых ситуациях.

Не могу подобрать идеального названия для этого явления, так что буду называть его просто «эффект Makefile». Эффект Makefile не назовёшь однозначно порочным — просто нужно иметь его в виду при проектировании инструментов и систем.

Суть эффекта Makefile сводится к следующему:

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

На это падки технари всех мастей, независимо от их навыков и уровня профессионализма, и в качестве наиболее красноречивого примера такого рода можно привести работу с Make:

  1. Требуется выполнить (понятную в общих чертах) задачу. Ранее вы уже решали очень похожую или даже идентичную задачу.

  2. Make (или другой инструмент, которому присущ такой эффект) считается верным или «наилучшим» из себе подобных (учитывая практическую целесообразность, зависимости пути, пр.) для решения данной задачи.

  3. Инженер не пишет новый Makefile, а копирует более ранний (зачастую очень большой и переусложнённый) Makefile из ранее решённой задачи, после чего адаптирует этот материал до тех пор, пока он не станет работать в новом контексте. Мне доводилось слышать, как некоторые технари получали Makefile «в наследство» т.e., эти Makefile они брали у своих начальников, преподавателей и т.д. В результате складывается так, что и эти «предковые» Makefile также с незапамятных времён передавались из поколения в поколение с косметическими изменениями. 

Make — просто пример, а не универсалия. Складываются разные профессиональные группы, члены которых осваивают разные инструменты. Есть и более обширное наблюдение: существуют целые классы инструментов/систем, (более) подверженных этому эффекту и (относительно) менее подверженные.

При некотором приближении это просто отличный (даже идеальный) инженерный механизм реагирования с точки зрения проектирования: зачастую наиболее рационально применить известный рабочий вариант. (Теоретически) так мы снижаем риск привнести в код баги, поскольку большая часть сделанного остаётся без изменений.

Но именно с точки зрения проектирования это выдаёт ущербность инструмента. Она может заключаться в том, как инструмент устроен или как применяется, но суть такова: инструмент (система) слишком сложны или раздражают, чтобы использовать их с нуля. Пользователь избегает применять инструмент с каждой задачей заново, а раз за разом копирует известное хорошее решение, в котором со временем накапливаются изменения.

Как только вы заметите этот эффект, вы также убедитесь, что он встречается повсюду. Кроме Make:

  • Конфигурации CI/CD (непрерывной интеграции и доставки) в таких инструментах как GitHub Actions и GitLab CI/CD, где пользователь копирует лохматую YAML-разметку из последней работавшей конфигурации и доводит её до ума, пока она снова не заработает (зачастую её для этого требуется не раз перезапустить);

  • Конфигурации линтеров и инструментов форматирования, где из проекта в проект копируется базовый набор правил, которые затем ужесточаются или смягчаются в зависимости от конкретных условий;

  • Сборочные системы как таковые, которые во всех нетривиальных деталях начинают напоминать предковую сборочную систему.

Важно ли это?

Зачастую, пожалуй, нет. Но я думаю, что об этой проблеме стоит задумываться, в особенности при проектировании инструментов и систем:

  • Инструменты и системы, в которых проявляется такой паттерн, часто оставляют желать лучшего в области поддержки при диагностике или отладке. Пользователю приходится раз за разом гонять инструмент (то и дело с длительными задержками), по крупицам выуживая информацию. Представьте себе конфигурации CI/CD, где всё скопировано и вставлено, и отладку такого конвейера приходится делать «в стиле дампа». При этом работа идёт по сети через промежуточный уровень, опосредующий оркестрацию VM. Смешно же!

  • Инструменты, в которых проявляется такой паттерн, зачастую не располагают к ознакомлению с собой: найдутся считанные спецы, которые настолько хорошо разбираются в инструменте, что способны его сконфигурировать. Все остальные будут просто копировать, а их знаний хватит ровно на то, чтобы точечно поменять настройки. Иногда это неизбежно, но не всегда. Графы зависимостей по определению сложны, как и описываемые ими сборочные системы. Но что стоит выучить разницу между $< и $^ в Make?

  • Инструменты, в которых проявляется такой паттерн не располагают к безопасному использованию: для обеспечения безопасности обычно требуется глубоко понимать, почему некоторое поведение устроено так, а не иначе. В системах, подверженных эффекту Makefile, также зачастую наблюдается спутанность между кодом и данными (или, в более общем случае, любая передача сигналов внутри полосы ), в основном потому, что рабочие решения не всегда безопасны. Вспомните, например, об инъекции шаблона в GitHub Actions.

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

  • Должен ли он быть конфигурируемым?

  • Нужен ли ему собственный синтаксис?

    • Отсюда следует: может ли он заимствовать знакомый синтаксис или идиомы из других инструментов или CLI?

  • Распространяется ли мой подход к работе с этим инструментом через копипаст? Если да, то насколько вероятно, что другие захотят повторить за мной?

Сложные инструменты — это необходимость и, можно даже сказать, неизбежность. Но, если эффект Makefile проявляется в простом приложении, это подсказывает, что данный инструмент переусложнён для решаемой им задачи. 

Комментарии (14)


  1. Drakosh
    12.01.2025 06:50

    Подумалось о мутациях и отборе генетическим алгоритмом. В результате спонтанных мутаций выживают самые лучшие? Гибкие? Популярные? Примеры кода, которые потом мутируют и размножаются в другие проекты, чтобы скрещиваться с другими примерами кода. В результате остаются только некоторые "выжившие".


  1. JBFW
    12.01.2025 06:50

    Ну вот, командную строку уже ниасилили теперь для простых задач требуется Гуй.

    Теперь ниасилили Makefile - нужен новый инструмент (наверное, снова Гуй в виде ide)

    А есть ещё языки программирования: там сложнааа... )

    (не, идея вроде бы: не стоит тупо копировать то чего не понимаешь, но читается оно именно так: сложнааа, очень сложнааа)


    1. rukhi7
      12.01.2025 06:50

      Ну вот, командную строку уже ниасилили теперь для простых задач требуется Гуй.

      вроде как логичным продолжением после того как не осилили командную строку являются перфокарты. Похоже настало время вспоминать что это такое.


  1. Rive
    12.01.2025 06:50

    Напоминает промпты для генерации ИИ-изображений, которые могут оставаться неизменными даже на профильных конкурсах, пока за них не обещают начать банить.


    1. askv
      12.01.2025 06:50

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


      1. ToSHiC
        12.01.2025 06:50

        Я не очень понял вашу аналогию с рыбой договора. Если мне надо сделать типовое действие (купить машину, например) - почему плохо использовать шаблон?

        Если действие между юрлицами - то примером может быть подписание договора о неразглашении между двумя контрагентами. Вы считаете, что тут тоже нельзя использовать шаблон, и всегда надо привлекать дорогих юристов?


  1. aragaer
    12.01.2025 06:50

    На моем опыте это обычно сопровождается словами "я программист на языке <вставить название языка>, а изучать документацию make я не хочу". Кроме Makefile это же распространялось на shell-скрипты, причем местами даже "какую-то ты тут странную конструкцию написал, почему это у тебя if без скобок? Сделай как привычно"

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


  1. nerudo
    12.01.2025 06:50

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


  1. rukhi7
    12.01.2025 06:50

    Насколько я понял суть в статье в том что людям свойственно НЕ читать инструкцию если что-то можно попробовать запустить с уже имеющейся (скопированной) конфигурацией. То есть русский авось он на самом деле не русский а международный авось! Инструкции читать никому не хочется.

    Оказывается надежда на то что мы сейчас скопируем сюда оттуда и оттуда и оно авось заработает, разбираться не с чем не надо - свойственна людям не зависимо от национальности. Интересная мысль!


  1. dyadyaSerezha
    12.01.2025 06:50

    Смысл статьи вижу только тогда, когда copy-paste используется без понимания, что именно ты потом модифицируешь и как это вообще работает. В остальном же это абсолютно правильная и вездесущая практика не только в инструментах сборки, а вообще везде в программировании.

    Пример. Чел работает в группе над большим мнонолетним проектом и вдруг ему надо написать новуюю утилиту, которая парсит некие входные параметры запуска (CLI). Варианты: парсить целиком с нуля или использовать стандартную библиотеку в проекте (для любого языка есть такие библиотеки). Далее, варианты для библиотеки: читать доки и писать все самому или посмотреть любую другую прогу в проекте (в инете) с этой юиюлиоткеуой и сделать нужный copy-paste, модифицировав его нужным образом. Вариант "я и так помню, как парсить входные CLI параметры с помощью этой стандартной библиотеки" в 99% случаев не катит, потому что писать такие пироги в большой проекте нужно в лучшем случае раз в год, в скорее всего гораздо реже или вообще никогда

    Итак, что выберет любой нормальный программист? Самый последний вариант с copy-paste, естественно, и это абсолютно правильно. А если есть доступ к нужному ИИ, он ещё и попытается сгенерить код для парсинга именно его параметров CLI с использованием их библиотеки и только слегка модифицировать код при необходимости.

    Ещё раз, это абсолютно нормальная и верная тактика вообще везде в инженерии.


    1. rukhi7
      12.01.2025 06:50

       большим мнонолетним проектом и вдруг ему надо написать новуюю утилиту, которая парсит некие входные параметры запуска (CLI)

      я бы для начала сосредоточился на том откуда берется это сказочное "вдруг". А оно берется как раз оттого что эти "входные параметры запуска" копируются (или даже лепятся-наворачиваются) в проекте как получится в виде заплаток, в виде костылей лишь бы оно продолжало хоть как то работать. И в один прекрасный момент случается такой вдруг что какой то начальник проснулся, например, и обнаружил что половина функциональности работает через зад или вообще не работает при определенных вполне естественных условиях, а работает только если стоять в очень не удобной позе. Ну и тогда принимается "сбалансированное" решение не привести эти параметры и способ их использования в порядок (это же многолетний проект!), а навернуть еще "простенький" парсер на эти параметры, а после парсера фильтр, а после фильтра,... что там у нас после фильтра обычно?

      Как то так.

      Самое грустное что это действительно абсолютно нормальная и верная (тут я очень не уверен) тактика вообще везде в инженерии.


  1. sukharichev
    12.01.2025 06:50

    О да, есть такой эффект. У админов это особенно ярко проявляется в конфиге squid proxy, где поколения и поколения копипастят порт для протокола gopher. Так и хочется сказать: на кой ты это делаешь, этот протокол умер раньше, чем ты родился, прекрати!


    1. randomsimplenumber
      12.01.2025 06:50

      прекрати

      Работает - не трогай (бурчит по админски).

      В далёких 200х, когда me конфигурировал squid, методом копипасты конечно же, не было уже там никаких gopher. Кто-то нашел архив старинных конференций Fido и разбудил древнее зло?


      1. Uporoty
        12.01.2025 06:50

        Работает - не трогай (бурчит по админски).

        В этом случае это очень опасный подход.

        Включение какого-нибудь ненужного сервиса или протокола как минимум повышает риск эксплуатации уязвимости в этом сервисе или реализации протокола злоумышленниками. Особенно когда его порт точно также забыли закрыть на фаерволе из-за бездумного копипаста существующих портянок.