Мы используем куки, чтобы пользоваться сайтом было удобно.
Хорошо
to the top

Вебинар: Каждая идиома когда-то была проблемой - 11.09

>
>
Сопротивляйтесь добавлению в проект...

Сопротивляйтесь добавлению в проект новых библиотек — 10 лет спустя

02 Сен 2026

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

Десять лет назад я опубликовал статью "Сопротивляйтесь добавлению в проект новых библиотек", и озвучил эту же тему в подборке из 42-х рекомендаций.

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

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

Я был прав в рекомендациях, хотя выбрал не лучший способ изложения и примеры. Более того, в старых рассуждениях нет важного пункта, который проявил себя в теме разработки безопасного ПО и его сертификации. Вернее, он есть — №4, но звучит слишком вскользь. Поэтому давайте попробую ещё раз.

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

41-я глава из подборки советов 2016 года

Итак, вам понадобилось реализовать в проекте функциональность X. Теоретики разработки программного обеспечения в этот момент говорят, что для этого нужно взять уже существующую библиотеку Y и использовать её для реализации необходимых вам вещей. Собственно, это классический подход в разработке программного обеспечения — повторное использование своих или чужих наработок (сторонних библиотек). Именно этим путём движется большинство программистов.

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

Я рекомендую всячески сопротивляться добавлению в проект каждой новой библиотеки. Прошу понять меня правильно — я вовсе не говорю, что не надо использовать библиотеки и писать всё самостоятельно. Это просто-напросто глупо. Дело в том, что часто новая библиотека добавляется в проект по прихоти одного разработчика с целью использовать в ней какую-то маленькую "фитюльку". Добавить новую библиотеку несложно, вот только потом всей команде много лет придётся нести груз её поддержки.

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

  • Добавление новых библиотек быстро увеличивает размер проекта. В нашу эпоху быстрого интернета и больших SSD дисков это не является существенной проблемой. Но когда проект начинает скачиваться из системы контроля версий не за 1 минуту, а за 10, это уже неприятно.
  • Даже если вы используете 1% от возможностей библиотеки, как правило в проект она будет включена целиком. В результате, если библиотеки используются в виде готовых модулей (например, DLL), то очень быстро растёт размер дистрибутива. Если вы используете библиотеки в виде исходного кода, то существенно увеличивается время компиляции.
  • Усложняется инфраструктура, связанная с компиляцией проекта. Некоторым библиотекам требуются дополнительные компоненты. Простой пример: для сборки C++ библиотеки требуется наличие Python. В результате через некоторое время для сборки проекта нужно в начале вырастить на компьютере целый сад вспомогательных программ. Возрастает вероятность, что где-то что-то перестанет работать; объяснить это сложно, это надо прочувствовать. В больших проектах постоянно "отваливается" то одно то другое, и нужно постоянно прилагать усилия, чтобы всё работало и компилировалось.
  • Если вы заботитесь об уязвимостях, вы должны регулярно обновлять сторонние библиотеки. Злоумышленникам выгодно изучать код библиотек с целью поиска уязвимостей. Во-первых, многие библиотеки открыты, а во-вторых, найдя дыру в одной из библиотек, можно получить отмычку сразу ко многим приложениям, где эта библиотека используется.
  • Одна из используемых библиотек неожиданно может сменить тип лицензии. Во-первых, вам нужно про это помнить и отслеживать изменения. Во-вторых, непонятно, что делать если это произошло. Например, в один момент распространённая библиотека softfloat перешла с "самодельного" соглашения на BSD.
  • У вас будут проблемы при переходе на новую версию компилятора. Обязательно будет несколько библиотек, которые не будут торопиться адаптироваться под новый компилятор, и вы будете вынуждены ждать или сами вносить какие-то правки в библиотеки.
  • У вас будут проблемы при переходе на другой компилятор. Например, вы используете Visual C++, а хотите использовать Intel C++. Стопроцентно найдётся пара библиотек, с которыми что-то не заладится.
  • У вас будут проблемы при переходе на другую платформу. Необязательно даже на "сильно другую платформу". Достаточно захотеть превратить Win32 приложение в Win64. У вас будут всё те же проблемы — несколько библиотек к этому окажутся не готовы и будет непонятно, что с ними делать. Особенно неприятна ситуация, когда библиотека заброшена и более не развивается.
  • Рано или поздно, если вы используете множество С-библиотек, где типы не лежат в namespace, у вас начинают пересекаться имена. Это приводит к ошибкам компиляции или к скрытым ошибкам. Например, начинает использоваться константа не из того enum, из которого вы планировали.
  • Если в проекте используется много библиотек, добавление ещё одной не выглядит чем-то вредным. Можно провести аналогию с теорией разбитых окон. В результате разрастание проекта приобретает неконтролируемый характер.
  • Есть масса других негативных моментов, о которых я не помню и не знаю. Но в любом случае, дополнительные библиотеки очень быстро увеличивают сложность поддержки проекта. Эта сложность может проявляться в самых неожиданных местах.

Ещё раз подчеркну: я не призываю вас отказаться от использования сторонних библиотек. Если в программе вам понадобилось работать с изображениями в формате PNG, то вам надо взять библиотеку LibPNG и не изобретать велосипед.

Но даже работая с PNG, надо остановиться и подумать. А нужна ли библиотека? Какие операции нужно выполнять с изображениями? Быть может, если вся задача сводится к тому, чтобы сохранить какое-то изображение в *.png-файл, можно обойтись системными функциями. Например, если у вас Windows приложение, то вам поможет WIC. А если вы уже используете библиотеку MFC, то вообще не надо усложнять код, ведь есть класс CImagе (см. обсуждение на сайте Stack Overflow). Минус одна библиотека — отлично!

Рекомендация

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

Вот некоторые возможные манёвры:

  • Быть может, нужную функциональность уже предоставляет API вашей системы или одна из уже используемых библиотек, исследуйте этот вопрос.
  • Если вы планируете использовать совсем маленький кусочек функциональности из библиотеки, то есть смысл реализовать его самостоятельно. Аргумент "лучше подключить библиотеку, вдруг потом ещё что-то понадобится" никуда не годится. Почти всегда из этой библиотеки в будущем больше ничего использоваться не будет. Программисты слишком тяготеют к универсальности, которая на самом деле не нужна.
  • Если для решения задачи есть несколько библиотек, то выбирайте самую простую, которая удовлетворяет требованиям. Как я писал выше, гоните прочь мысли "на всякий случай взять библиотеку покруче".
  • Прежде чем начать добавлять библиотеку, просто подождите и подумайте. Попейте чаю, отвлекитесь, обсудите задачу с коллегами. Возможно, в процессе выяснится, что можно решить задачу совсем иным путём, не прибегая к помощи сторонних библиотек.

P.S. Многим рассказанное здесь придётся не по душе. Например, то, что я рекомендую использовать не переносимую универсальную библиотеку, а допустим WinAPI. На это будут возражения, основанные на том, что тем самым мы привязываем проект к одной операционной системе. И потом будет очень сложно сделать программу переносимой. Я с этим не согласен. Часто идея "потом перенесём на другую операционную систему" живёт только в голове разработчика. На самом деле такая задача вообще может быть никогда не поставлена руководством. Или проект "загнётся" из-за излишней сложности и универсальности, ещё до момента популярности и необходимости портирования. Плюс не забывайте пункт (8) в списке проблем, приведённый выше.

Конец старой публикации, но не конец истории

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

Когда вы добавляете в проект библиотеку, возникают транзитивных зависимости. Это значит, что библиотека использует другие библиотеки, а те в свою очередь ещё библиотеки и так далее. Запросто бывает, что вам в проекте требуется 10 библиотек, а по факту их используется уже 100 штук. В каких-то языках, как С++, это выражено слабо, а при разработке на JavaScript, Java, Python, наоборот, быстро возникают огромные цепочки зависимостей.

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

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

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

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

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

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

Появилась и ещё одна разновидность такой атаки, связанная с созданием кода с помощью генеративного ИИ (GenAI). Решая задачу, ИИ иногда галлюцинирует в названиях требуемых пакетов. Подмечено, что есть определённые паттерны в галлюцинациях, и этим пользуются. Создаются и публикуются заражённые пакеты, с названиями, которые с наибольшей вероятностью могут быть выдуманы GenAI. Звучит надуманно, но это работает и такие атаки уже успешно применялись. Они получили название Slopsquatting [смотри также 1, 2].

Следующая тема — статический анализ кода. С точки зрения безопасности, вы должны проверять не только свой, но и весь сторонний код. Этим мало кто занимается, так как все обычно думают про анализ только своего кода. А кто задумывается — сталкивается с огромным объёмом работ.

Однако если вы захотите провести сертификационные испытания своего проекта, то нет разницы, код написан вами или взят извне. Раз вы используете сторонний код, значит, теперь это ваш код, и вы несёте за него ответственность. Это логично — нет разницы, кто автор кода, из-за которого приложение окажется уязвимым.

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

Не буду здесь углубляться. Но тем, кому будет суждено погрузится в мир ГОСТ Р 56939—2024, ГОСТ Р 71207—2024, методического документа ВУ и НДВ, сертификационных испытаний и т.д., прочувствуют какую стоимость им предстоит платить за каждую когда-то добавленную в проект зависимость.

Неспроста в ГОСТ Р 71207—2024 есть даже вот такой пункт 8.3:

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

Страшно про двое суток? :) Правильно, что страшно. Могу только успокоить: если что — анализатор PVS-Studio укладывается в нормативы. Но стоит ли до такого доводить? И это ведь только анализ, а разбирать критические ошибки? Это не разовая история про внедрение анализатора, а регулярная в силу обновления библиотек.

Заключение

Конечно, подход активного добавления в проект сторонних компонентов не повернуть вспять. Я к этому и не призываю, это непродуктивно. Однако я хочу, чтобы вы всё-таки знали о потенциальных проблемах, с которыми можете столкнуться.

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

В идеале, изредка полезно проводить аудит: используются ли все связанные с проектом библиотеки. Я видел, как при портировании большого C++ приложения с Win32 на Win64, выяснилось, что около 10 библиотек вообще никак не используются. При этом многие годы тратились ресурсы на их "содержание" в проекте. Провести такой аудит может быть непросто, но возможно, у вас получится его организовать.

Дополнительные ссылки

Подписаться на рассылку
Хотите раз в месяц получать от нас подборку вышедших в этот период самых интересных статей и новостей? Подписывайтесь!
Популярные статьи по теме

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

Следующие комментарии next comments
close comment form