Pull to refresh

Comments 41

STL и Boost тоже обязательно изучать? Как на счёт ffmpeg и x264? OpenSSL? А разработчики на Node.js обязаны изучить исходники Node? А почему библиотеки надо читать, а исходники ОС нет?

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

Можно решить для себя (или в рамках задачи/программы/проекта), какую библиотеку можно считать априори надёжной, и лезть внутрь только когда есть проблема, связанная с ней.
Все зависит от области в которой вы работаете, если вы создаете медицинские устройства от которых зависит жизнь пациента — то да, «библиотека априори не надежна», ее нужно покрывать тестами как грузчик матом упавший на ногу чемодан и очень хорошо думать прежде чем внедрять ибо потом вероятная замена может встать в копеечку.
Если вы пишете процесс который если упал то «ничего, запустим заново», то да — Вам побоку что за библиотека, если только уж очень часто падает и сожрал всю память можно посмотреть что внутри.
Я же специально написал, что надо заранее провести границу. И надо понимать, что невозможно протестировать библиотеку на 100%, потому что на её поведение влияет в том числе железо и ОС.
Так, давайте по-порядку:

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

Абсолютно надежных систем не бывает — бывает степень надежности системы

Еще Джон фон Нейман изучал возможности построения скольугодно надежных систем из ненадежных элементов (на примере схем из функциональных элементов):

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

Из выделенных двух тезисов выше лично я для себя решил, что:
1) Пишем систему по принципу Let it crush (отсылка к Erlang/OTP)
2) Оцениваем риски надежности и сверяем их с потребностями заказчика

Последний пункт особенно важен: повышение аптайма с 98% до 99% может обойтись заказчику далеко не в 1% стоимости продукта. Я считаю, что заказчик должен это понимать «на берегу» — это честно
Давайте по-порядку :-) Вот только я не понял о чем по порядку?
Вы хотите поспорить на тему того что проекты бывают разные и если вы тяните либу в себе в домашний проект это одно, и совсем другое если на этом будет работать спутник?
Статья же о сторонних библиотеках и о том как правильно их готовить и для каких целей, а цели как видим бывают разные.
Из выделенных двух тезисов выше лично я для себя решил, что ...

Очень здорово что Вы пришли к столь однозначному выводу, нет сомнений, нет потерянного времени, удачи Вам в дальнейшем :-)
PS: Занимался софтом для автоматизации пожаротушения на заводах (водяные пушки, завесы, пеногенераторы, ИК датчики и все что можно автоматизировать), сейчас мед софт. К сожалению NDA не позволит мне углубиться в подробности.
Хотел вам что-то написать, но понял, что проблема на столько неоднозначна, и лучше даже не пытаться спорить.

А на счет NDA, ну хотя бы основные принципы расскажите, ну хотя бы начиная со слов «один мой знакомый писал мед софт вот так ...» =)
Попробую ответить в рамках статьи.
Есть устройства которым позволительно упасть, так как время за которое они поднимаются вновь ничтожно, для подобных устройств требования мягче и тем не менее основная работа сосредоточена на стабильности и возможности восстанавливаться после сбоев. Есть другой класс устройств — падение которых крайне нежелательно, так как для того чтоб оно снова заработало нужно поднять и правильно настроить массу " железной переферии" а это драгоценные секунды — для таких устройств любая новая библиотека, особенно сторонняя — это повышенный риск, она подвергается большому кол-ву стресс тестов на стабильность: многопоточность, потребление ресурсов, граничные условия, тысячи и миллионы старт-стопов и тд и тп. Устройства обернуты в хороший слой телеметрии и watchdog'ов. Это очень глубокая тема, многогранная и весьма специфичная.
Такая разработка долгое и дорогое занятие.
STL и Boost изучить очень желательно. Там много тонкостей, которые сложно описать словами, но разработчикам знать стоит. Как то пришлось разбираться с чужим кодом, который за счет безумного использования STL страшно тормозил. Сравнительно небольшое изменение позволило ускорить его более чем в два раза (исключалось копирование больших объектов).
Вы сейчас имели в виду «изучить, чтобы научиться хитрым фичам». Это очень далеко от «изучить, чтобы удостовериться, что оно написано правильно».

А в вашем примере изучать код STL совершенно не обязательно, достаточно прочитать документацию, чтобы узнать, где что и как копируется. А вот документацию эту изучить конечно же всем разработчикам на С++ надо обязательно.
И где же в документации описана разница между string и __versa_string?

А у нас быс случай в проекте, когда typedef __gnu_cxx::__vstring string (вместо using std::string) ускорил кой-какой боевой код процентов на 30.
Про то, что basic_string может быть сделан с рефкаунтерами и copy-on-write в стандарте написано, как и то, что это implementation specific. А в C++11 стандарте написано, что никаких copy-on-write больше нельзя. Вы же это хотели получить, да? В clang, если я не ошибаюсь, в строке никогда COW не было.

Ну а хак вы сделали отличный. Из-за него можно словить проблемы при компиляции, переконвертацию std::string в vstring implicitly (оно ведь умеет так, да?), ну и юзаете compiler-specific фичи, то есть какой нибудь clang идёт лесом уже.

Потестируйте std::string с --std=c++0x, высока вероятность, что будет работать не хуже, чем vstring. А раз используете специфичные для компилятора фичи — то и нечего на документацию языка или STL пенять. Разницу между string и __versa_string, очевидно, следует искать в документации GCC. Например, вот цитата:
That is not the case when reference counting is not used and indeed it will not be used (per the new C++11 Standard) in a new implementation of std::string which we are currently showcasing as <ext/vstring.h>
все реализации на всех платформах? :)
А это, как обычно, зависит от того, чего вы хотите добиться.
Неоднократно смотрел исходники NHibernate и ASP.NET MVC, чтобы лучше понять, как работают не документированные места. Это полезно. Вы передергиваете.
То есть вы согласны с тезисом автора «если вы не понимаете/не знаете какую либо (хоть одну) часть библиотеки, которую используете — вы плохой разработчик»?
В упор этого тезиса в статье не нашёл. Нашёл совет заглядывать в код библиотеки, а тезиса — не нашёл.
Ещё раз, вы — человек, который профессионально занимается созданием компьютерных программ. Который, наверное, отвечает за свой труд и, возможно, считает себя неплохим специалистом. И вы считаете возможным взять чужую библиотеку и, ознакомившись с ней на уровне её интерфейса, использовать в своём продукте?
На мотив известной песенки: «Who let the troll out?»
Я думаю, что автор призывает хоть иногда туда поглядывать. Пока только один человек из всех, кого я собеседовал, рассказал мне как работает animate в jquery.
Именно «как работает», а не «как использовать»?
Как вы это используете в работе и часто ли?
Каждый раз, когда использую jquery я знаю, как он разбирает селекторы. Это помогает писать более быстрый код.
> STL и Boost тоже обязательно изучать?
Да.

> Как на счёт ffmpeg и x264? OpenSSL?
Да и да. Да.

> А разработчики на Node.js обязаны изучить исходники Node?
Да.

> А почему библиотеки надо читать, а исходники ОС нет?
Тоже надо.

Смех-смехом, но не то чтобы обязательно надо изучить все внутренности всех используемых библиотек, но не нужно бояться или стесняться это делать. Наоборот нужно приобритать навык что при необходимости выяснить какой-нибудь аспект работы с библиотекой быстро найти это в ее исходном коде. Некоторые разработчики почему-то не могут этого делать и для них библиотека или фрейморк выглядит магической штукой которая как-то работает. Плохо тут может остутствовать понимание работы с производительностью, непонимание принципов работы, да и вообще для вообще для развития полезно заглядывать как устроены популярные библиотеки.
То есть JS разработчику надо изучать ядро Linux? А устройство процессоров и квантовые эффекты, которые уже начинают влиять на работу кремниевых микросхем при 22нм техпроцессе тоже надо изучить?

Могу вам привести такой аргумент: Larry Hastings (тот самый, который релиз менеджер питона 3.4) на своём докладе All-Singing All-Dancing Python Bytecode говорит, что питонисту совершенно не обязательно понимать тонкостей работы байткода и виртуальной машины, чтобы писать хороший код. Вот цитата:
If you are going to be a core contributor, it makes sense to study bytecode, though there are areas of the core where that knowledge is not required at all. Hastings is somewhat skeptical of the other commonly cited reasons—«so I'm really wasting your time»—but the presentation made the subject interesting, even if only as an academic exercise. For example, understanding what's «really going on» in the interpreter is one reason for understanding bytecode, but that's a bit dubious because there is more to it than that. Understanding the bytecode means understanding C, which means understanding assembly language, microcode, transistors, and, eventually, quantum mechanics. You can get really far with Python programming without knowing anything about quantum mechanics, he said.

А вот про не бояться и поднимать квалификацию — тут я полностью согласен. Посыл же автора про интерфейсы библиотеки и профессионализм мне не понравился.
Вообще статья отчасти пересекается с похожей про «протекающие абстракции», habrahabr.ru/post/123402/. Глубину разбора абстракций пускай подсказывает здравый смысл, но по крайней мере в первый уровень надо уметь заглядывать.

> То есть JS разработчику надо изучать ядро Linux?
Если это как-то обяснит возникщую у него проблему/вопрос, то почему нет. Если про node.js, то это вполне может быть актуально, чтобы представлять например какой объем памяти понадобится на миллион входящих соединений. Когда появился node.js я изучал его исходники (больше js-кую часть), чтобы понять что является ограничением HttpClient, который использовался для нагрузочного тестирования.
Если библиотека достаточно популярна и имеет много хороших отзывов — сразу начинаю использовать, а знакомство с кодом оставляю на потом. Если же библиотека не на столько популярна, то в зависимости от наличия времени могу пробежаться по некоторым частям кода чтобы понять, насколько можно доверять ему. Если код не нравится, смотрю внимательнее.
Знакомлюсь с кодом глубже в нескольких случаях:
  • обычный интерес
  • что-то пошло не так
  • нужно расширение функционала
  • поиск надокументированного/слабодокумментированного кода, чтобы случайно не изобрести велосипед
  • библиотека стала занимать важную часть в проекте
  • отладка
Специально с кодом не знакомлюсь, но наличие исходников очень сильно облегчает поиск ошибок, когда не ясно, где она — в операционной системе, в библиотеке, или в собственном коде.
Сильно зависит, конечно, от реализуемых функций, но ваш порядок поиска развернуть бы в обратную сторону. Мне тут не так давно довелось услышать отличное объяснение ошибке в программе — tcp теряет байт. Ну в самом деле, не в коде программы же ошибка.
Тут нет никакой сортировки по приоритетам, просто список
Но почему-то этот список отсортирован по возрастанию приоритетов :-) Ну… так случайной получилось, надо полагать.

Прежде всего нужно, конечно, искать ошибку у себя, потом в использованных библиотеках, потом в OS, потом в процессоре… ну просто потому что статистика такая: ошибок у нас программе мы нашли (и исправили) вагон, ошибок в библиотеках — уже меньше, ошибок в OS — ещё меньше, ошибки в процессоре — исключительная редкость (но да, представьте себе, мы умудрились и с подобным реально столкнуться тоже). И конечно исходники очень помогают: разобраться с проблемами в Linux гораздо проще, чем с подобными же проблемами в Windows.

P.S. Разбираться с квантовой физикой пока не пришлось и, я боюсь, никогда не придётся: уже обнаружение и борьба с ошибками в процессоре было чем-то жутко сложным (я лично в ней не участвовал, но знаю что вся эпопея заняла чуть не полгода), а уж дальше вряд ли удастся продвинуться (ибо даже и ошибку в процессоре мы сами не могли исправить).
Вот, например, известнейшая база SQLite. Я ковырял ее исходники и честно оценил свой уровень — никакой. Т.е. чтобы просто понять, как все работает внутри нужно потратить больше времени, чем я потрачу на собственный проект с использованием этой библиотеки. И я честно делегирую решение вопроса о качестве кода сообществу, т.к. никакого времени (а иногда и способностей) не хватит на оценку всего этого самостоятельно.
Читать библиотеки бывает полезно. Читая Doctrine1 можно открыть для себя целые семейства недокументированных фич, кучу подводных камней и странностей (чего стоит почитать хотя бы как реализуется count()).

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

Ну а вообще, многое можно сказать о библиотеке по её апи. (Далее вещи, справедливые для ПХП) Если вместо конструктора используется Init() — значит у разработчика инитомания, и хорошего уже можно не ждать. Еще бывают библиотеки с Явой Головного Мозга (это когда для простого действия нужно инстанционировать больше одного объекта), «я всё еще живу в мире инклюдов», «у нас конфиг на дефайнах в файлике, лежащем в папке библиотеки, а что?», «а чочо, мы же добавили композер.жсон. ах, надо было еще автолоадер настроить?», «не забудьте настроить пути, сами мы ни за что не поймем где находимся», «а давайте-ка мы вам функций в глобальный неймспейс подсыпем?» и так далее.
Не нужно использовать библиотеки, код которых приходиться читать.

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

Вот практический пример: работаю в Android с AlertDialog, при нажатии на кнопку меня уведомляют, что кнопка была нажата, и ещё отдельно о том, что диалог был убран с экрана. Мне важен порядок, в котором эти вызовы происходят. В документации ничего не написано. Я посмотрел исходники и проверил опытным путём, допустим, для Android 4.4, какой порядок у этих обратных вызовов. ОК. А он такой же в каждой остальной версии? Не знаю. А в новых версиях он тоже останется таким же? Тоже не знаю, и если с предыдущими версиями ещё можно разобраться, потратив тучу времени, то о новых версиях никто, кроме разработчиков, мне не расскажет.

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

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

Путь в тысячу миль начинается с первого шага!

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

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

Из личного примера: ragel. Отлично работающий генератор DFA, но есть у него один недостаток: C код, который он выдают — нестабилен, от запуска к запуску может отличаться. Причём на малых примерах это вообще не воспроизводится и даже на больших воспроизводится не всегда. Исследование показало, что, как водится, кое-кто заложился на хешсет, где ключами были адреса объектов, но так как у нас возникла проблема ещё и со сгенерированным им кодом (MSVC отказался работать с функцией в 100'000 строк), то мы «просто» сделали свой кодогенератор, который берёт XML-описание машины и выдаёт C код. Как можно догадаться это заняло, в общем, не пять минут…
Как Вы поняли, что Ragel выдает нестабильный код? Я в том смысле, в каких обстоятельствах у Вас закрались первые сомнения о том что он работет нестабильно?
DrMetallius уже говорил об этом, но я всё равно повторю. Любая библиотека имеет контракт. Например, ядро UNIX в своем контракте имеет примерно следующее: «если ты вызовешь fopen(…) с такими-то параметрами, на полученном FILE сделаешь fwrite(…) с неким буфером в памяти, а потом сделаешь fclose(…), то содержимое этого буфера будет записано на диск». И если вы заплатили за разработку UNIX-подобной ОС для ЭВМ на базе 144-ядерного процессора Ч. Мура, попытались в ней записать описанным образом дату на диск, а она тупо не сделала ничего, то вы вправе предъявить разработчику неустойку. А вот если вы запустили программу:
void *f = fopen("some.txt", "w");

, и она вылетела с SIGSEGV или вообще не скомпилилась, то это ваши личные трудности. Как и то, что если она все-таки заработает, но после обновления вдруг перестанет.

Контракт может быть хорошим и плохим. Например, если Javadoc библиотеки состоит из одного класса с одной статической функцией «void doCommand(String command)» без какого-либо комментария. Точно так же и реализация может быть хорошей и плохой: например, компилятор IBM XL C/C++ согласно документации по умолчанию должен вставлять выброс std::bad_alloc при невозможности выделить память оператором new, но на самом деле вставляет возврат nullptr. Кстати, тут кто-то кому-то имеет право предъявить неустойку, но все как всегда реагируют нордически, не суть. Так вот, если в документации к библиотеке написано незнамо что, то и библиотека с точностью до бритвы Оккама делает незнамно что (и вполне логично её заменить другой, делающей знамо то, что нужно). А если в доках написано одно, а при тестах библиотека выдает другое, то это баг (по определению), и реагировать на эту ситуацию нужно, как на баг, а не делать okay-face и городить очередной workaround.

И уж тем более не надо заниматься чтением исходников без необходимости, а то потом появляются быдлоколлеги, которые определяют размер выделенного в куче блока через «(ulong)(pObj-sizeof(long))» («да это работает на Линуксе, я проверял!») и велосипедят свои shared_ptr.
Мне кажется, статья очень перекликается с другим постом на эту тему, почему абстракции текут.

В принципе я согласен с тезисом автора: чтобы система хорошо работала, достаточно знать её устройство уровнем ниже, чем тот, на котором работаем.
Sign up to leave a comment.