Скачай PVS-Studio и найди ошибки в С, С++,C# коде
288,01
рейтинг
15 января в 12:03

Разработка → Сопротивляйтесь добавлению в проект новых библиотек

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

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

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

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

Наблюдая за развитием некоторых больших проектов, я могу перечислить ряд проблем наличия большого количества сторонних библиотек. Наверное, я перечислю далеко не все проблемы, но даже следующий список должен побудить вас задуматься:
  1. Добавление новых библиотек быстро увеличивает размер проекта. В нашу эпоху быстрого интернета и больших SSD дисков это не является существенно проблемой. Однако, когда проект начинает скачиваться из системы контроля версий не за 1 минуту, а за 10, это уже неприятно.
  2. Даже если вы используете 1% от возможностей библиотеки, как правило в проект она будет включена целиком. В результате, если библиотеки используется в виде готовых модулей (например, DLL), то очень быстро растёт размер дистрибутива. Если вы используете библиотеки в виде исходного кода, то существенно увеличивается время компиляции.
  3. Усложняется инфраструктура, связанная с компиляцией проекта. Некоторым библиотекам требуются дополнительные компоненты. Простой пример: для сборки требуется наличие Python. В результате через некоторое время для сборки проекта нужно в начале вырастить на компьютере целый сад вспомогательных программ. Возрастает вероятность, что где-то что-то перестанет работать. Объяснить это сложно, это надо прочувствовать. В больших проектах постоянно «отваливается» то одно то другое и нужно постоянно прилагать усилие чтобы всё работало и компилировалось.
  4. Если вы заботитесь об уязвимостях, вы должны регулярно обновлять сторонние библиотеки. Злоумышленникам выгодно изучать код библиотек с целью поиска уязвимостей. Во-первых, многие библиотеки открыты, а во-вторых, найдя дыру в одной из библиотек можно получить отмычку сразу ко многим приложениям, где эта библиотека используется.
  5. У вас будут проблемы при переходе на новую версию компилятора. Обязательно будет несколько библиотек, которые не будут торопиться адаптироваться под новый компилятор. И вы будете вынуждены ждать или самим вносить какие-то проявки в библиотеки.
  6. У вас будут проблемы при переходе на другой компилятор. Например, вы используете Visual C++, а хотите использовать Intel C++. Обязательно найдется пара библиотек, с которыми что-то не заладится.
  7. У вас будут проблемы при переходе на другую платформу. Не обязательно даже на «сильно другую платформу». Достаточно захотеть превратить Win32 приложение в Win64. У вас будут все те-же проблемы. Несколько библиотек к этому окажутся не готовы и будет непонятно, что с ними делать. Особенно неприятна ситуация, когда библиотека заброшена и более не развивается.
  8. Рано или поздно, если вы используете множество Си-библиотек, где типы не лежат в namespace, у вас начинают пересекаться имена. Это приводит к ошибкам компиляции или к скрытым ошибкам. Например, начинает использоваться константа не из того enum, из которого вы планировали.
  9. Если в проекте используется много библиотек, добавление ещё одной не выглядит чем-то вредным. Можно провести аналогию с теорией разбитых окон. В результате разрастание проекта приобретает неконтролируемый характер.
  10. Есть масса других негативных моментов, о которых я не помню и не знаю. Но в любом случае дополнительные библиотеки очень быстро увеличивают сложность поддержки проекта. Это сложность может проявляться в самых неожиданных местах.

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

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

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

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

Совершенно случайно, именно в тот момент я читал книгу «Beautiful Code» (ISBN 9780596510046). Эта книга о простых и изящных решениях. И в ней я повстречал крайне простую реализацию регулярных выражений. Буквально несколько десятков строк. И всё!

Я взял из книги эту реализацию и начал использовать в PVS-Studio. И знаете, что? До сих пор возможностей этой реализации нам хватает. Какие-то сложные регулярные выражения нам просто не нужны.

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

Этот случай окончательно убедил меня, что надо по возможности искать простые решения. По возможности, отказываясь от библиотек вы делаете проект более простым.

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

Посмотрите, как элегантно. Это код был мной немого изменён при интеграции в PVS-Studio, но его суть не изменилась.
Код из книги
// Формат регулярного выражения.
// c     Соответсвует любой букве "с"
// .(точка)  Соответсвует любому одному символу
// ^     Соответсвует началу входящей строки
// $     Соответствует концу входящей строки
// *     Соответствует появлению предыдущего символа от нуля до
//       нескольких раз

int matchhere(char *regexp, char *text);
int matchstar(int c, char *regexp, char *text);

// match: поиск соответствий регулярному выражению по всему тексту
int match(char *regexp, char *text)
{
  if (regexp[0] == '^')
    return matchhere(regexp+1, text);
  do { /* нужно посмотреть даже пустую строку */
   if (matchhere(regexp, text))
     return 1;
  } while (*text++ != '\0');
  return 0;
}

// matchhere: поиск соответствий регулярному выражению в начале текста
int matchhere(char *regexp, char *text)
{
   if (regexp[0] == '\0')
     return 1;
   if (regexp[1] == '*')
     return matchstar(regexp[0], regexp+2, text);

   if (regexp[0] == '$' && regexp[1] == '\0')
     return *text == '\0';
   if (*text!='\0' && (regexp[0]=='.' || regexp[0]==*text))
     return matchhere(regexp+1, text+1);
   return 0;
}

// matchstar: поиск регулярного выражения вида с* с начала текста
int matchstar(int c, char *regexp, char *text)
{
  do {   /* символ * соответствует нулю или
            большему количеству появлений */
    if (matchhere(regexp, text))
      return 1;
  } while (*text != '\0' && (*text++ == c || c == '.'));
  return 0;
}



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

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

P.S. Многим рассказанное здесь придется не по душе. Например, то что я рекомендую использовать не переносимую универсальную библиотеку, а допустим WinAPI. На это будут возражения, основанные на том, что тем самым мы привязываем проект к одной операционной системе. И потом будет очень сложно сделать программу переносимой. Я с этим не согласен. Часто идея «потом перенесем на другу операционную систему» живет только в голове разработчика. На самом деле такая задача вообще может быть никогда не поставлена руководством. Или проект «загнётся» из-за излишней сложности и универсальности, ещё до момента популярности и необходимости портирования. Плюс не забывайте пункт (7) в списке проблем, приведенный выше.
Автор: @Andrey2008
PVS-Studio
рейтинг 288,01
Скачай PVS-Studio и найди ошибки в С, С++,C# коде

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

  • +14
    > Сопротивляйтесь добавлению в проект новых библиотек
    Золотые слова!

    От себя добавлю ещё одну проблему. Она возникает в больших и серьёзных компаниях, когда существенен легальный статус выпускаемого продукта: возрастание затрат времени на анализ лицензий входящих сторонних библиотек. Особенно, если их лицензии меняются по ходу дела.
    Например, в прошлом году библиотека softfloat перешла с «самодельного» соглашение на BSD. Во многих программных симуляторах требуется моделировать инструкции над числами с плавающей запятой в формате IEEE 754, и многие из них используют именно для этого именно softfloat. И иногда код ещё и модифицируется (например, включается поддержка 82-битных чисел).
    • +2
      Вы что, из мастера/транка библиотеки тащите?
      • +2
        Тащишь из мастера/транка — огребаешь от обновлений, тащишь определенные версии — огребаешь проблемы с безопасностью.
        • +4
          огребаешь проблемы с безопасностью

          Если вы знаете о конкретной проблеме, тогда можно перейти на новый релиз, предварительно прочитав чейнжлог. Или исправить ошибку силами своей команды.
          Если о проблеме вы не знаете, но тараканы в голове не дают вам уснуть, шепчя в ухо постоянно «безопасность, безопасность!», то как вы можете быть уверены, что новая версия не откроет кучу новых дырок?
          • 0
            Если вы знаете о конкретной проблеме

            Да, я о том и говорю, что подключив библиотеку крайне желательно следить за ней: либо за сменой API, если подключаете master/trunk, либо за обновлениями безопасности, если подключаете какую-то версию.
  • +8
    Провокационно-холиварный пост, хотя в общем и целом согласен, правда есть и другая крайность:
    так появляются свои, проприетарные библиотеки/фреймворки: сделали что-то маленькое и нужное, вынесли в отдельный модуль (хорошо бы переиспользовать потом где-нибудь, неправда- ли?), через месяц еще туда запихали «нужного», потом еще, потом еще.
    • 0
      Я использую простое решение: разбейте этот разожравшийся мего-проект на небольшие узко специализированные проекты в одном солюшене. Все под рукой, неплохо структурировано и кучу лишнего за собой не тянет. Как правило, и зависимостей между такими библиотеками нет. Да и собираться он будет 1 раз в N единиц времени, что не сильно критично даже при большом количестве проектов.

      + Всегда можно позаимствовать из него конкретную реализацию, если библиотека все еще кажется чересчур избыточной.
  • –6
    Windows-only пост, имхо. Дополнительная библиотека — это дополнительная строка в debian/control и pkgname.spec
    • +30
      Вы не поняли сути поста. Там не про тяжесть установки пакета, а про цену его поддержания в проекте. В Windows отлично работают пакетные менеджеры, так что у вашего наезда красные глаза и свитер не стиран (он стереотип).
      • –12
        Не надо мне рассказывать про поддержание внешних библиотек в проекте. Моя работа отчасти с этим связана. И в 90% всё его поддержание заключается в добавление разовой зависимости.
        • +17
          т.е. на десять пунктов из статьи у вас один аргумент: надо добавить зависимость в пакетный менеджер.

          Но в чудесном мире разработки иногда авторы пакета:
          а) поворачивают реки вспять и переписывают основной API под новые веянья
          б) отказываются править баги
          в) отказываются обновлять зависимости
          г) обновляют свои зависимости до бета версий
          д) умирают
          е) меняют лицензию (лучше уж пусть умирают)
          • –12
            т.е. на десять пунктов из статьи у вас один аргумент: надо добавить зависимость в пакетный менеджер.

            Просто из 10 пунктов только 3 последних актуальны для linux систем. Остальное неактуально, при наличии библиотеки в стандартном репозитории ОС.
            Судя по вашему перечислению, вы не очень хорошо знаете «экосистему», так как что CentOS, что Debian не обновляют пакеты в рамках одного релиза. Поэтому смены API, лицензий и т.д. опять же не страшны. Остаётся толлько отсутствие некоторых исправлений. Которые попадают под 10%
            • +7
              разработка != сборка из сорцов
            • +2
              Первые 2 пункта для линукса менее актуальны. А все остальные только в путь. И усложнение инфраструктуры и уязвимости и версии компиляторов и платформ.
              • –5
                Уязвимости библиотек в офф репозитории — забота их мэнтейнеров. И у них следить за этим получается лучше :)
                • +1
                  Так и есть, но они всё равно не справляются. Поэтому лучше просто не использовать библиотеку, если это возможно.
                  • +1
                    В linux, что libpng, что libc — одинаково внешние зависимости. Писать без libc C++ немного тяжеловато будет :)
                    • +4
                      Ну автор примерно так и написал. В частности про libpng. Если не обойтись — надо использовать.
                • +5
                  Уязвимости и баги в вашем ПО, вызванные зависимостями — это проблема разработчиков. И неожидданно может оказаться, что половину внешних библиотек пришлось фиксить самостоятельно, и теперь прокет собирается только с кастомными сборками внешних библиотек, и либо в проекте растет размер папки аля 3rdparty, либо приходится указывать, откуда брать правильные версии библиотек (которые в свою очередь могут начать конфликтовать с чем-нибудь на ПК пользователя). А бывают еще замечательные зависимости типа gstreamer'а или ffmpeg'а у которых API ломают чуть ли не в минорных апдейтах.
                  А не дай бог еще весь проект — это какое-то критичное ПО, подлежащее к сертификации по какой-нибудь MISRA, то с кучей депендеси можно вешаться.

                  Т.ч. внешние зависимости даже в linux — это далеко не «добавить одну строчку в debian/control и pkgname.spec и радоваться жизни», все как всегда умирает на сопровождении. И иногда проще написать приватный фреймворк, с полностью контролируемым кодом, чем тягать зависимости из внешних библиотек.
              • 0
                Ваши слова да каждому разработчику в сердце! Особенно ребятам из salt, из OpenStack из злободневных.
                Причем проблемы с обратной совместимостью появляются даже в таких «серьёзных и проверенных временем» проектах, как mongodb(привет совместимости аутентификации в различных версиях).
            • –2
              а кто тут говорить про дебиан/центос? Если у вашей поделки еще есть мизерный шанс попасть в дебиан, то попасть в центос уже будет невозможно.
              • 0
                А зачем туда попадать? ПО замечательно распространяется через сторонние репозитории
                • 0
                  тогда не надо фантазировать насчёт несменяемости библиотек.
                  • –2
                    Библиотеки-то как жили в офф репе, так и остались. Со своими стабильными версиями. А в стороннем репозитории только сам пакет с распространяемым ПО.
                    • +2
                      это годится только для софта, написанного в те же лохматые годы, что и эти стабильные версии.

                      Нормальная ситуация — несколько лет отставания в обновлении.
                      • 0
                        Почему? Пересобрал пакет, и всё. Если ломающих изменений в API не было, то и проблем никаких.
                    • 0
                      Специально для вас: habrahabr.ru/company/pvs-studio/blog/275159/#comment_8741329
                      Вопрос: насколько отстаёт стандартная репа RHEL/CentOS от официального стабильного релиза?
                      • +1
                        От стабильного релиза чего? Вы хотите юзать, например, openssl последней версии? Флаг в руки, только зачем? Вам шашечки или функциональность?
                        Если нужна непременно какая особенная версия, и этой версии нету в репах поддержимаевого дистрибутива — не стоит её использовать и стоит поискать другой вэй.
                        Решение об использовании библиотеки выносится с учётом того, а есть ли она в поддерживаемых нашим ПО дистрибутивах(проблемы индейцев на arch, gentoo и т.д. не интересны). Если она есть и её версия подходит(а такое чаще всего), то никаких аргументов против нету.
                        Не спорю, проблемы могут быть, например с namespace, была один раз c mysql и gnutls(точнее в зависимости у gnutls — trousers: http://sourceforge.net/p/trousers/trousers/ci/7d207231355a5702cbdcf24628e8c1e2e3722593/

                        Но как уже писали и ниже, как я писал выше, в контексте C/C++ linux разработки актуальны только последние 3 из 10.

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

                        p.s. проблемы aur — это проблемы arch, а не разработчика ПО, если этот разработчик не брал на себя иных обязательств.
                        • 0
                          В ваших кейсах — может быть.
                          Но я повторюсь, бывают случаи когда по тем или иным причинам собрать какую-то версию либы/пакета бывает сложно. И речь не только об Arch, у нас было такое и на старых Ubuntu.
                          Поэтому если пакет тянет за собой тучу зависимостей — это сулит геморрой при обновлении дистра и лично я стараюсь такого софта избегать.
                          Яркий пример — virt-manager. Может это и отличный инструмент, но просто потому что он за собой тянет еще полтора десятка пакетов которые реализуют ненужные мне функции, я не стану его ставить.
                • +2
                  Великолепно.
                  Вам рассказать как я собирал один SIP-клиент из AUR'а?
                  Затык был один: какая-то сторонняя библиотека обновилась, а старую версию выпилили с сайта. Смешно? Мне нет.
                  • 0
                    Ой, а я тут еще вспомнил про сервер с FreeBSD 7.x, на который не ставится rsnapshot.
                    rsnapshot. КАРЛ! Модулей в CPAN'е нету. Порты из CVS нельзя обновить — нету их там больше. Так и гуглю до сих пор, старые версии либ…
                    • 0
                      почему вы его просто не переустановите? 7-ка это же что-то несусветно древнее.
                      • 0
                        Я бы не против, но права не имею.
          • +1
            Форкаете и пилите дальше, какие проблемы?

            Выкинуть библиотеку всегда успеете, вы заведомо тратите не больше времени, а, как правило, на порядки меньше.
            • 0
              Вот честно не помню откуда фраза, но она есть олицетворение «великой мудрости»: идеальный код тот которого нет.

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

                Ну вот, и вы всю сложность теперь замели под ковёр из определений слова «лишние».

                Копирование реализации регекспов из книги, кстати, напоминает копирование реализации из библиотек. И общепринятым библиотекам я бы доверял больше.
        • +2
          На всякий случай перескажу содержимое статьи.
          Проблемы появляются когда:
          1. Пытаешься сменить версию компилятора
          2. Пытаешься сменить компилятор
          3. Пытаешься сменить платформу
          4. Начинаются коллизии имён типов
          • 0
            1,2. В рамках одного релиза debian/centos один компилятор является штатным. И все бинарные пакеты в репах уже им собраны.
            3. Если пакет в репозитории есть для 32 битной архитектуры, то и для 64 бита тоже. Проблемы могут быть с arm, но большая часть всё же там есть, да мы arm не поддерживаем.
            4. Вот это было 1 раз.

            Повторюсь, для linux проектов актуальны только 3 пункта из 10. Сужу по собственном опыту.

            Распухание проекта, проблемы со сборкой этих зависимостей и т.д. — неактуально
            • +2
              Более того, любые добавленные в проект внешние зависимости, если ваше приложение таки попадет в официальные репозитории, будут безжалостно удалены мейнтейнерами (согласно правилам дистрибутива) и заменены на системные версии.
              • +1
                Тут-то ваше приложение и перестанет работать.
                • +1
                  Не перестанет. Если оно от такой замены ломается, оно просто не будет добавлено. Если оно сломается от обновления, то будет удалено из дистрибутива (как xmms, привязанный к GTK1) — но это никогда не происходит в пределах стабильной ветки дистрибутива. Поэтому на линукс-разработчиках всегда лежит обязанность поддерживать совместимость со всеми возможными версиями зависимостей, в т.ч. с еще не выпущенными.
                  • 0
                    И чтобы не было необходимости поддерживать совместимость со всеми возможными версиями зависимостей — лучше использовать только те библиотеки, без которых вообще никак.
                    • 0
                      С этим согласен.
                    • 0
                      А чтоб минимизировать время адаптации под новые версии зависимостей, надо вместо qt и gtk писать на голой xlib
            • 0
              Про архитектуры — допустим. Наверное время, когда авторы переводили свои проекты с 32 на 64 и ловили эпические баги — уже прошло. Но собирать библиотеки самому всё равно придётся, смена компилятора — всё равно боль, попытка собрать код под FreeBSD — всё равно может не увенчаться успехом.
      • +1
        Какие пакетные менеджеры работают отлично под Windows, и сравнится ли набор доступных там библиотек с таковым в каком-нибудь Debian или Gentoo с оверлеями?

        На память только msys2 приходит, то он не сказать чтоб распространён, насколько я скудно могу судить.
      • 0
        Про пакетные менеджеры в Windows можно поподробнее?
        • 0
          Nuget посмотрите. Chokolatey ещё
    • +3
      Добрый день!

      В посте речь идет про разработку а не про деплой/использование конечными пользователями. Моменты описанные в статье актуальны для любого кроссплатформенного проекта.
    • 0
      Я вчера попытался поставить у себя virt-manager на Arch'е.
      Мне предложило утянуть 28 зависимостей.
      Я посмотрел, подумал и нажал Ctrl+C.
  • +9
    > Например, если у вас Windows приложение, то вам поможет WIC. А если вы уже используете библиотеку MFC, та вообще не надо усложнять код, ведь есть класс CImagе (см. обсуждение на сайте StackOverflow). Минус одна библиотека — отлично!

    Отлично! Теперь-то, разумеется, я не наступлю на грабли из пунктов 7, 6, 1, 2, 4!

    > Какие-то сложные регулярные выражения нам просто не нужны.
    Конечно, они никогда и не понадобятся. А когда понадобятся, разумеется, все будут помнить, что работает, а что нет.
  • +2
    Сопротивляйтесь добавлению в проект новых библиотек

    Это конечно разумно. Но я бы продолжил рекомендацию, Open Source by Default:

    Всегда ищите маленькие атомарные библиотеки для реализации какого-либо повторяющегося функционала. Если не найдете — по мере возможностей создайте такую и выложите в Open Source.

    Код состоящий из композиции библиотек становится более модульным. Баги будет исправлять коммюнити из разработчиков со всего мира, а не только вы с ограничением по ресурсам.

    Я iOS-разработчик, приведу пример хорошей и плохой библиотеки:
    1) github.com/mxcl/UIImageWithColor
    пусть это всего несколько строчек, но я считаю разумным вынести это в дополнительный модуль, а не копипастить из раза в раз.

    2) Three20 и в меньшей степени, но Nimbus
    github.com/jverkoey/nimbus
    Тащить в проект 10 контролов ради одного? И скорее всего они будут развиваться только параллельно, хотя отдельно один их них мог заглохнуть, а один стать очень популярным и получить внимание разработчиков, которые не обращают внимание на все-в-одном.

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

    Еще один плюс — сталкиваясь со знакомой проблемой, вы найдете решение в своем гитхабе за пару минут, а не будете ворошить исходники проекта 5-летней давности.
    • +1
      Попробуйте поддерживать программу лет 10-15, тогда о волшебных свойствах сторонних библиотек мнение изменится.
      • +2
        Попробовал. Не изменилось.
  • +13
    Небольшой комментарий из мира Java, почему я считаю, что вышеперечисленное не актуально для нас:

    1) VCS для сорцев, а не для бинарников, все бинарники хранятся в бинарном репозитории.
    2) Время компиляции увеличивается несущественно
    3) Не актуально для 95% библиотек, билд системы полностью решают эту проблему
    4) Библиотеки хотя бы исправляют, а у вас "security through obscurity", для настоящего злоумышленника — не помеха
    5) Binary compatibility
    6) Binary compatibility
    7) JVM везде JVM
    8) Ни разу не встречал подобную проблему в языках с явными импортами и корректной статической типизацией
    9, 10) Отвечу одно — библиотека — 150+ коммитов исправлений, накопленный опыт, ваш велосипед — 1 коммит…

    Поэтому статья актуальна не для хаба «разработка», а для «c++»

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

      Сам сталкивался и со сменой лицензий, и прекращением поддержки, и (вдруг, при попытке обновления библиотеки) несовместимости зависимостей с другими библиотеками. По жтим причинам некоторые тяжеловесные сторонние библиотеки нам пришлось вынести в независимые процессы — чтобы избежать конфликтов зависимостей.
    • –5
      ахахаха!

      Я охотно верю что в голове у среднего джавадевелопера такая же каша и поэтому типичный джавопроект — это по одной библиотеке на одну строчку существенной логики.

      Поэтому например людей, которые могут скомпилять хадуп с нуля единицы, остальные просто ищут готовые собранные пакеты.
      • +1
        А может аргументированно попробуете ответить?
        • –3
          Все советы, которые давал автор абсолютно верны для любого языка программирования.

          API у библиотек меняется, в них находят уязвимости. Одну и ту же библиотеку могут втянуть разные импортированные библиотеки под разными версиями. Джава от этого не защитит.
          • +1
            абсолютно верны для любого языка программирования

            Пролистайте чуть выше, я там перечислил почему не верны, прямо по пунктам

            в них находят уязвимости

            И исправляют их, а в вашем коде никто не исправляет не баги, не уязвимости

            под разными версиями

            Да, я выше и написал, трейдоф, однако правильные билд-системы предоставляют мощнейшие тулы чтобы решить эту проблему
            • –1
              ну что вы фантазируете? =) Как вам билд-система решит проблему, когда библиотека A требует C версии 1.5, а библиотека B требует C версии 2.0!

              И я сейчас говорю не про ту легкую ситуацию, когда можно фиксировать выбор, скажем C=1.7 и удовлетворить обе библиотеки, а именно про жесткую и нерешаемую зависимость.

              Рантайм то один и адресное пространство в нём одно.
              • +1
                Отлично, со всеми пунктами разобрались, остался последний, «транзитивные зависимости» (заметим, что ее автор даже не упомянул, хотя это как раз и есть может стать проблемой). Билд система вам, конечно, не решит эту проблему, однако просигнализирует и предложит попытку решить «ту легкую ситуацию».

                Предположим, что у нас действительно «нерешаемая зависимость», тогда вы, как инженер, стоите на распутье — можете либо пересобрать либу как shade-jar и нести в дальнейшем груз поддержки, либо отказаться от ее использования в пользу своего кода.
                Что лучше — зависит от ситуации, а вам, как инженеру, платят за решение этой проблемы.
                • –2
                  это вы — инженер, которому платят за работу. Я владелец бизнеса, которому потом разгребать последствия ковбойства в стиле «а, втащим ещё одну тулзу, она разрулит конфликты между предыдущими двумя втащенными либами».

                  Итого, мы выяснили, что ваши фантазии о том, что джава чем-то особенным помогает с зависимостями не более, чем желание весело поспорить с кем-то в интернетах.
                  • +1
                    Итого, учитывая то, что вы так и не смогли предоставить никаких аргументов, мы выяснили, что вы пытаетесь влезть в сферу в которой, мягко говоря, экспертом не являетесь, тем не менее настойчиво пытаетесь продавить свое мнение.
                    • –3
                      нет, мы выяснили то, что вы наврали про то, что якобы какие-то магические механизмы в джаве позволяют разрешить конфликтующие зависимости.

                      • 0
                        Можно поподробнее, с цитатами вместо голословных утверждений?
    • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Проблемы начинаются когда вы используете библеотеку А и Б, потом нужно добавить библиотеку С, но внезапно она тоже использует библеотеку Б, только другой версии (скажем, старой). Придется собирать библиотеку С самому, добавлять ее в свой собственный Maven репозиторий. Когда придет время обновить библиотеки (например security issue где-то исправили или нужна новая функциональность или performance improvements, много может быть разных причин) то внезапно задача будет не тривиальной. Иногда даже код библиотеки С приходится править, т.к. в библиотеке Б сменился API.
      Чем больше у вас Java библиотек в проекте, тем больше вероятность таких проблем.
      • 0
        Если таки столкнулись с проблемой, а вам позарез нужна эта либа:

        1. Создаем в проекте модуль D, D зависит от C
        2. Собираем D как shade-jar
        3. Делаем главный модуль зависимым на артефакте D
        4. ???
        5. PROFIT
        • 0
          До первой библиотеки, которая использует reflection для загрузки своих кусков или ситуации, когда один и тот же объект должен попасть и в B, и в D, но оказывается несовместим, т. к. один и тот же интерфейс представлен в runtime двумя версиями (из B и из D).
  • 0
    Быть может, если вся задача сводится к тому, чтобы сохранить какое-то изображение в *.png — файл, можно обойтись системными функциями.

    Можно сделать отдельное приложение, слинкованное с этой библиотекой и реализующее нужные функции.
  • +3
    Всеми руками и ногами поддерживаю автора! Зависимости — настоящий бич любого более-менее сложного проекта. Но с одной оговоркой. Когда предметная область, решаемая библиотекой, сложна сама по себе (например, реализация HTTP), то каждая новая библиотека обречена проходить по всем граничным случаям.

    Я как-то раз пилил процедурную генерацию 3D-моделей и мне надо было сделать объединение многогранников. Там зубодробительная математика — ошибок элементарно было наделать кучу, решил библиотеку использовать. На выбор было несколько библиотечек — пара маленьких и один огромный пакет для вычислительной геометрии. Решил маленькую использовать — вот геморрой был. Одна так и не завелась, только время потратил. Вторая заработала. А потом началось веселье: попадается вырожденная грань — глючит, многогранники касаются — падает. В каком-то из случаев вообще напоролся на TODO в коде. Пришлось использовать монстроидальную библиотеку — зато всё заработало сразу.
    • +1
      Согласен с вами на 100%. В своё время не смог даже найти РАБОТАЮЩУЮ либу для поиска пересечения пары кривых Безье. Опыт как у вас: как ни странно, более монструозные библиотеки с большей вероятностью работают корректно.
  • +5
    Просто в плюсах нет встроенного менеджера зависимостей. С тем же crates.io особых проблем библиотеку подключить не наблюдается.
    Плюс стоит пользоваться только теми библиотеками, авторы которых имеют нормальную культуру разработки. А лишний раз лезть в api операционной системы тоже идея не слишком хорошая — это лишает кроссплатформенности.
  • 0
    Во многом согласен с автором. В моём немаленьком проекте (NanoFL) используется пара чужих библиотек и десяток-другой самописных (и выложенных в open-source). Почему так? Большинство не очень распространённых открытых библиотек являются велосипедами в том или ином виде. Т.е. это обычно толком не протестированные решения, более-менее работающие в типичных ситуациях. Нередко написанные со скрытыми ошибками даже для типичных входных данных. Приходится залезать внутрь и править. А красивого кода внутри никто не гарантирует. Однако есть в использовании чужих библиотек огромный плюс — они позволяют быстрее продвигаться проекту. В конце концов — их можно переписать когда ситуация подожмёт.
  • +1
    Если вы планируете использовать совсем маленький кусочек функциональности из библиотеки, то есть смысл реализовать его самостоятельно. Аргумент «лучше подключить библиотеку, вдруг потом ещё что-то понадобится» никуда не годится. Почти всегда из этой библиотеки в будущем больше ничего использоваться не будет. Программисты слишком тяготеют к универсальности, которая на самом деле не нужна.


    Часто бывает как раз, что используется и это зло. Сначала подключаем большую «год» либу, хотя можно было бы маленькую или свою реализацию, потом в каких-то ситуациях начинаем использовать другие функции этой либы, хотя можно было бы подключить другую небольшую или свою реализацию, потом ещё, ещё и ещё и получаем десятки зависимостей, от которых практически уже невозможно избавиться. Ну а чё, уже же подключена либа, нарушим немного юникс-вэй…
  • 0
    А чего ж вы тогда у себя Roslyn используете?
    • +1
      Статья не об аскетизме, а о балансе между простотой быстро взять библиотеку и 10 годами мучений с поддержкой. :)
  • –1
    Почему-то уверен, что недавно читал эту статью на хабре!
  • +9
    Извините за грубость, но это типичный трёп Windows-программиста. Давайте теперь разберём ваши «советы».

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

    Shared libraries, DLLs. И что, вы скачиваете код из репозитория каждую минуту?

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

    Shared libraries, DLL. Статическую библиотеку достаточно один раз скомприлировать.

    Усложняется инфраструктура, связанная с компиляцией проекта. Некоторым библиотекам требуются дополнительные компоненты. Простой пример: для сборки требуется наличие Python. В результате через некоторое время для сборки проекта нужно в начале вырастить на компьютере целый сад вспомогательных программ. Возрастает вероятность, что где-то что-то перестанет работать. Объяснить это сложно, это надо прочувствовать. В больших проектах постоянно «отваливается» то одно то другое и нужно постоянно прилагать усилие чтобы всё работало и компилировалось.

    Ночные кошмары Windows-программиста. «Что-то где-то перестанет работать» звучит как «я понятия не имею, что делаю», та картинка с собакой и компьютером, вы знаете.

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

    Если у вас пользовательское приложение, то угроза минимальная. Если нет, то подход «напишу всё сам, а то кто его знает» самый глупый, и вам нужно срочно перестать писать код.

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

    Но нормальные компиляторы сохраняют обратную совместимость… Ах да, Visual C+…

    У вас будут проблемы при переходе на другой компилятор. Например, вы используете Visual C++, а хотите использовать Intel C++. Обязательно найдется пара библиотек, с которыми что-то не заладится.

    В жопу такие библиотеки, которые писали разработчики, которые не знают стандартов.

    У вас будут проблемы при переходе на другую платформу. Не обязательно даже на «сильно другую платформу». Достаточно захотеть превратить Win32 приложение в Win64. У вас будут все те-же проблемы. Несколько библиотек к этому окажутся не готовы и будет непонятно, что с ними делать. Особенно неприятна ситуация, когда библиотека заброшена и более не развивается.

    В жопу такие библиотеки ещё раз.

    Рано или поздно, если вы используете множество Си-библиотек, где типы не лежат в namespace, у вас начинают пересекаться имена. Это приводит к ошибкам компиляции или к скрытым ошибкам. Например, начинает использоваться константа не из того enum, из которого вы планировали.

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

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

    А как же сотни проектов, которые не разрослись, но зависят от других библиотек? Ещё существует опциональное включение.

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

    Лучше вообще код не писать, это дело наблагодарное.

    А теперь по существу. «Библиотеки» являются одной из бесконечного множества проблем разработки наравне с goto, C vs. C++, shared vs. static и т.д. Все эти проблемы объединяет то, что их нельзя решить однозначно. Прямо как в сказке про дудочку и горшочек. И когда я вижу, как кто-то в очередной раз выходит и «открывает глаза» на то, как делать правильно, то у меня два варианта: или у человека мало опыта, или человек живёт в своём маленьком мирке, где ему уютно и тепло.
    • +9
      Давайте разберем.

      У меня на одном проекте порезвились любители добавлять библиотеки. CPAN ставит их минут 15-20.

      Нужна новая копия? CPAN. Обновился perl? Придется запускать CPAN на всех копиях — из-за конфликтов версий библиотеки ставятся в локальную, а не системную директорию.

      Ночные кошмары Windows-программиста.

      Ночные кошмары любого программиста, работающего в команде над большим и долгим проектом.

      Но нормальные компиляторы сохраняют обратную совместимость

      Недавно обновил gcc с 4.9.3 до 5.3.0, часть пакетов отказалась собираться. Посижу еще год на 4.9.
      Perl не компилятор, но на пути с 5.16 до 5.22 ухитрился потерять поддержку старого Catalyst — проект запускается, но потом падает с ошибками. А новый Catalyst несовместим со старым кодом.

      В жопу такие библиотеки, которые писали разработчики, которые не знают стандартов.

      Для этого нужно всезнание.

      когда библиотека заброшена и более не развивается.

      О, а для этого нужны еще и навыки прорицания.

      у меня два варианта: или у человека мало опыта

      Какой чудесный самострел.
      • +3
        И что вы хотите сказать своим примером? Мой комментарий не был оппозиционным, я не говорил «Да, давай, вперёд — добавляй библиотеки в проект!». Я просто показал несостоятельность тезисов автора. Для любого языка и платформы можно найти пример, когда получается dependency hell. Выше кто-то написал, что для Java это неактуально. Как раз таки более чем, в случае, когда A и B обе зависят от C разных версий (пожалуйста, Java-адепты, не нужно набегать, я знаю, что это проблема решается, просто привёл пример).

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


        Ну и ещё надо быть честным — это блог PVS-Studio. Её авторы знамениты своими эпатажными постами и комментариями. Взять хотя бы их доводы по поводу Linux-версии анализатора. По ним и по этому посту складывается впечатление, что их анализатор жестко привязан к Windows, что у любого нормально программиста вызовет большое «Как?!». Поэтому сомневаться в сомнительности их «советов» не приходится.
        • +8
          Я хочу сказать, что названные выше проблемы не ограничиваются разработкой под Windows. Я сталкивался ровно с теми же, хотя под винду за последние 15 лет не написал ни строчки кода. Сторонние библиотеки меняются и отмирают. Люди, инициировавшие их включение — покидают проект. Оставшимся приходится разбираться со всем этим наследием, которое после определенной критической массы начинает создавать проблемы с завидной регулярностью: постоянно что-то отваливается, что-то оказывается несовместимым.

          Чаще всего для исправления ситуации нужно написать буквально пару строк. Но вот только определение места, где и как их надо написать иногда занимает часы. А в случае неявных багов — дни и даже месяцы.

          И самое неприятное: эти любители библиотек считали себя абсолютно правыми, мол, зачем я буду писать аж целых тридцать строчек кода, когда есть библиотека или компонент, ведь пользоваться чужим кодом по максимуму — модно и современно, а те, кто считает иначе — невежественные замшелые ретрограды, застрявшие в восьмидесятых.
        • –4
          Поэтому сомневаться в сомнительности их «советов» не приходится.
          С технической точки зрения Linux не проблема (Проверка Vim при помощи PVS-Studio в GNU/Linux). Другое дело, что там деньги зарабатывать не получается.
          • +4
            У Coverity отлично получается. И у Intel получается. И у ряда других компаний, у которых фирма, в которой я работаю, покупает порядком лицензий, получается, видимо.

            Intel'овские продукты вроде всяких профайлеров и прочих inspector'ов, кстати, в нашу команду принёс и посоветовал купить лично я, пользуясь опытом личного использования, для которого мне не приходилось доказывать Интелу, что я стою того, чтобы на меня тратить время, в отличие от PVS.
    • +1
      Несмотря на то, что я с вами в целом полностью согласен и даже разделяю ту же нелюбовь к MSVC, но вот по этому пункту хотелось бы возразить:
      Но нормальные компиляторы сохраняют обратную совместимость… Ах да, Visual C+…


      gcc в пятой ветке сломал ABI, добавив такую вещь, как ABI tag. Поэтому на системе, собранной gcc 5, другие компиляторы чувствуют себя не очень хорошо. Например: llvm.org/bugs/show_bug.cgi?id=23529

      Лично у меня от этого возникла довольно забавная ситуация, кстати, когда кусок одной моей софтины, использующий C++14, может быть собран только на системах с gcc =4.9 и clang ≥3.4, потому что даже gcc 5.3 недостаточно корректно поддерживает C++14, а вставлять костыли под него мешает эстетическое чувство.
      • 0
        Может я неправ, но изменение major версии предполагает возможное изменение ABI.
        • 0
          С одной стороны — да, предполагает. С другой стороны — clang уж скоро год как на современных дистрибутивах по факту не работает, что несколько печалит.
        • +2
          В этом случае принято менять major компоненту soversion, чтобы не было проблем с ABI.
          • 0
            Дополню: при этом возможно сосуществование двух версий библиотеки на диске, и каждая программа будет использовать правильную для нее версию библиотеки.
            • 0
              Веселуха начнётся, когда программа втянет две разных версии по зависимостям в одно адресное пространство.
  • 0
    Короче, резюмирую.

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

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

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

    Статья хорошая, мысли правильные. Спасибо.
  • +2
    Пример использования большой сторонней библиотеки.

    Несколько лет назад в перловом мире был бум Moose.
    Я сделал на нём большой проект. На Moose v1. Сейчас проект надо обновлять. А установить Moose v1 из CPAN не получилось — так как у него в зависимостях стоит… Moose v2 :) который с ним не очень совместим.

    На самом деле смысл статьи надо понимать так: программа должна быть максимально простой и с минимальными связями с какими-то другими программами (библиотеками, сервисами и так далее).
    • –2
      ещё стоит придерживаться золотого правила о минимуме зависимостей у библиотек.
  • +1
    >> В процессе разработки анализатора PVS-Studio

    До этой строки был уверен, что читаю перевод госпела какого-нибудь североамериканского блоггера.

    Из терминальных случаев:
    Как-то раз на глаза попался java-проект с Guava в зависамостях.
    Из всего богатства Guava использовались ровно 2 (ДВА!) статических метода, Lists::newArrayList() и ImmutableList::copyOf()
  • +3
    Меня всегда злит, когда пытаются все под одну гребенку подогнать. Лишние зависимости да, зло. Но нужно ведь быть адекватным и понимать где золотая середина. Вообще, у любого решения есть две стороны. Нужно изучать исключительно риски и выбирать то решение, где риски меньше или где они могут нанести меньше вреда.

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

    В общем почитать стоит какие проблемы есть, но давать это в виде утверждения — не профессионально, КМК.
    • +2
      где риски меньше или где они могут нанести меньше вреда.

      Не «или», а «вероятность осуществления риска умножить на его стоимость». Если грубо.
      • 0
        Да, приятно вас послушать. :)
  • +1
    Посмотрите, как элегантно. Это код был мной немого изменён при интеграции в PVS-Studio, но его суть не изменилась.

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

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

    А если нужно матчить сколь нибудь большие строки, лучше всё же взять RE2.

    Implementing Regular Expressions
    A Regular Expression Matcher
  • 0
    Это замечательная история о том, что если бы в самом начале придерживались вот таких правил… А так же история о том, будет ли наш новый проект иметь хоть какое то будущее, чтобы делать все по уму… Но на самом деле вопрос лишь в своевременном рефакторинге, на который кое кто не спешит раскошелиться. Но и вопрос о том, что люди не склонны делать рефакторинг по совести, с склонны делать видимость рефакторинга.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка