Довольно часто приходится встречать рекомендацию "не изобретать велоcипед". Иногда с ярко выраженным пренебрежением и самоутверждением, иногда, якобы, как дельный совет. Однако, пусть и призванная быть советом, данным из благих побуждений, в ряде контекстов она лишь показывает некомпетентность говорящего.
Вкладываемое назначение фразы — уберечь от выполнения бесполезной работы, призыв воспользоваться готовым решением для поставленной задачи, и с точки зрения стороннего наблюдателя действительно выглядит разумно.
Но при этом из внимания упускается ключевой фактор, характерный не только для разработки програмнного обеспечения, но и решения любых задач: при изменении контекста, в котором задана задача, изменяется и решение.
Упустить из виду этот принцип, все равно, что признаться в собственной неспособности решать прикладные задачи.
Рассмотрим несколько случаев.
API над API
Один из распространенных случаев обвинения в велосипедостроительстве — самостоятельное написание обертки API какого-то сервиса, реализации которого уже имеются.
Случай из моей практики.
Необходимо было реализовать загрузку данных из Facebook в наш сервис. Язык мейнстримовый и библиотека от самого Facebook нагуглилась за 2 минуты.
Документация библиотеки несоответстововала текущему программному интерфейсу, вокруг каждой манипуляции накручивалось очень много лишних действий. Библиотека оказалась очень низкого качества.
Результат: через 1.5 часа работы с ней, не удалось даже авторизоваться.
Коллега реализовал собственную обертку web-api фейсбука. Суммарно, на создание обертки и связанной функциональности на стороне сервиса у него ушло около часа. На вопрос "а зачем ты тут навелосипедил", он отвечал следующие несколько дней.
Подобное особенно ярко проявляется в мейнстримовых язык с большим коммьюнити: появляется нездоровая тенденция публиковать обертки API над другим API, под лозунгами "For humans" и "In simple way". Такие обертки устаревают, как только оборачиваемый интерфейс обновляется, а авторы забрасывают такие "проекты", делая код, их использующий, нерабочим.
Здравый вопрос: и что, каждый раз писать такие обертки вручную?
Ответ: Куда более сильным решением для больших оберток будет использование кодогенератора, интерпретирующего спецификации.
Контроль над кодовой базой и отказ отвечать за чужие ошибки
В кругах wannabe разработки компьютерных игр такая фраза адресуется любому, кто посмеет реализовать свой движок. "Зачем изобретать велосипед? Возьми юнити!". Или Game Maker, или, не, дай бог, Defold.
Казалось бы, двигло\конструктор предоставляет все необходимые инструменты для разработки, Многие из них кроссплатформенные и вообще упрощают жизнь.
Как должен поменяться контекст здесь, чтобы появилась необходимость делать свой движок?
Как минимум, это контроль над кодовой базой и функциями движка (например, на ряде моделей контроллеров гейммейкер стабильно глючит, и поправить это бывает крайне проблематично). То есть необходимо снизить вероятность встретить "чужой баг", исправить который cамостоятельно либо невозможно, либо крайне затруднительно.
Особенно ярко это проявляется в играх, которые не насыщенны графическими и\или физическими наворотами — банально, не так-то и много на себя берет условный движок.
К тому же, нет необходимости увеличивать суммарный объем кодовой базы, если от движка\конструктора используется малая часть его функций, а необходимые инструменты все равно нужно дописывать самостоятельно, попутно контролируя корректность их работы с движком.
Например, исходя из подобных посылок Томми Рефенес написал свой движок для Super Meat Boy.
Кто-то возразит: "Но ведь в сторе\еще каком-то хранилище, есть же гора пресетов\инструментов\расширений!".
Да, это замечательно, и дает пару очков вперед, но в движках с большой активной базой пользователей, вполне наблюдается тот же эффект "умри молодым", описанный в предыдущем разделе. Без контекста и конкретной задачи нельзя однозначно утверждать, что, де, изобилие в сторах пользовательских расширений сыграет на руку.
Мнимая идентичность. Притягивание задачи за уши.
Иногда, сформулировав задачу выясняется, что приходящие на ум существующие решения… не подходят. Из-за своей "жирности" или неудовлетворения одного из ключевых условий задачи (да, их бывает несколько).
Хороший пример: CluNet.
Cluster в своей статье достаточно полно описал причины решений, принятых им при разработке протокола умного дома. Пример очень показательный и хорошо описан, рекомендую разобрать его самостоятельно.
Вывод
При поиске решения задачи обязательно рассматриваются контекст и все заданые условия.
Даже в простых случаях, мелкие детали контекста переворачивают решение с ног на голову, и навык решения прикладных задач во многом строится на умении видеть и оценивать исходные посылки.
Так говорит ли фраза "не изобретай велосипед" о неумении советчика решать задачи? Если советчик упомянул велосипеды раньше, чем через несколько минут раздумий, скорее всего, да.
Или, по-капитански обобщая:
Прежде, чем решать — думай.
Прежде, чем говорить — думай.
И вообще — думай.
Комментарии (32)
Cerberuser
07.05.2019 04:28Пилим большой (относительно — бандл около пяти мегабайт в сумме) проект на React и TypeScript. Периодически сталкиваюсь с тем, что существует почти подходящее решение где-то в недрах npm, но в нём есть досадное несоответствие нашим ожиданиям, из-за которого брать эту библиотеку к себе "как есть" — значит, наворачивать вокруг неё лютые костыли. Уже несколько раз форкал такие пакеты и тащил в проект собственную версию. Тоже своего рода велосипед (пусть и по большей части из фабричных деталей)? Зато руль ровно нужной высоты и тормоза откалиброваны.
VolCh
07.05.2019 07:30Обратное тоже верно. Решил писать велосипед — обоснуй почему не существующие решения, почему не адаптер над ними. И аргументы должны быть чёткими, чтобы два дня не объяснять.
С другой стороны, налицо фактор протекания, просачивания не используемых сначала функций больших библиотек, затянутых в проект ради одной, в результате чего заменить её на практике почти невозможно. Хрестоматийный пример в веб-разработке — jQuery. Затащили ради AJAX или промисов, а потом во всем приложении используется всё, да ещё jQuery UI. Основной аргумент — уже есть же, не надо новую зависимость, не надо велосипед писать…
0x1000000
07.05.2019 08:29И аргументы должны быть чёткими, чтобы два дня не объяснять
Так об этом и речь. В статье у коллеги автора были вполне уважительные причины сделать свою реализацию, но очень сложно сломить предупреждение, что свой велосипед никогда не может быть лучше, чем что-то готовое.
VolCh
07.05.2019 09:15Причины может и были, но судя по "отвечал несколько дней" аргументов готово не было. Ну или не донёс их массово, убеждая каждого по одиночке.
RoSenMann Автор
07.05.2019 09:21Нет, его просто в течении нескольких дней спрашивал каждый встречный.
Newbilius
07.05.2019 12:26Но ведь в самом вопросе ничего странного нет :) Видишь контринтуитивное решение — спроси коллегу, почему он его принял. И отсутствие готовой обёртки к популярному сервису действительно вызывает недоумение и недоверие. Мой опыт показывает, что такое бывает — но редко.
0x1000000
07.05.2019 15:36И отсутствие готовой обёртки к популярному сервису действительно вызывает недоумение и недоверие
Вполне могу себе это представить. Обертка для Java, скорее всего, будет и будет актуальной, но ведь еще есть: .Net, Go, PHP, Ruby, Python и т.д. – поддерживать API обертки в актуальном состоянии для всех возможных языков и технологий может быть проблемой даже для очень крупной компании.
Некоторые мысли про REST сервисыЭта проблема, на мой взгляд, проистекает из-за того, что у REST сервисов нет общепризнанного механизма их описания. Если бы он был (и все бы его использовали), то написать универсальные генераторы API оберток для всех актуальных платформ, не было бы проблемой.polar11beer
07.05.2019 18:47Эта проблема, на мой взгляд, проистекает из-за того, что у REST сервисов нет общепризнанного механизма их описания
Хм… Swagger a.k.a. OpenAPI?
И кодогенерация под мейнстримовые языки в нём имеется.0x1000000
07.05.2019 19:04Когда (если) Swаgger станет стандартом (тут можно вспомнить про WADL), то да, проблема отпадет.
nrgian
07.05.2019 19:24Когда (если) Swаgger станет стандартом
Он вполне себе стандарт-дефакто один из пары. Что переименованием только подчеркнуто.
Дело только в лени/нехватки времени программистов его использовать/изучать.
VolCh
07.05.2019 14:43Просто надо было написать это где-то, где все сами могли прочитать. например в комментарии к MR/PR. Или рассказать на дейли-митинге, груминге, планерке и т. п.
synedra
07.05.2019 09:14+1"Зачем изобретать велосипед? Возьми юнити!"
У этого решения есть ещё один недостаток, который распространяется на многие не-писания-велосипедов. Юнити часто оверкилл и он может тащить непропорциональные системные требования. Они не запредельные, да, но всё равно как-то странно, когда рогалик с минимальной анимацией, не поддерживающий даже мышь (не говоря уже о геймпадах, 3D, VR и чёрте в ступе), весит 100+ Мб и лагает на интегрированной видеокарте. Серьёзно, отрисовка страниц в браузере — несопоставимо сложнее, чем весь UI игры, но как-то же фаерфокс умудряется работать на относительно старом железе.
Newbilius
07.05.2019 12:54Но есть другая проблема: если конкретный разрабочтик игры не смог на юнити оптимизировать рогалик с минимальной анимацией — разве можно надеяться, что он сможет написать с нуля игровой движок с лучшей производительностью? Автор игры скорее всего не осилил документацию по движку — но вот с нуля написать на SDL или вручную на OpenGL+DirectX он сможет написать нечто лучшее? Сомнительно :)
Безусловно, какие-то минимальные требования к компьютеру у Unity есть, и без него реально написать игру, весящую намного меньше и работающую в разы быстрее. Но гораздо чаще тормоза вызывает не сам движок, а неумение им пользоваться.
poxvuibr
07.05.2019 18:16+2Но есть другая проблема: если конкретный разрабочтик игры не смог на юнити оптимизировать рогалик с минимальной анимацией — разве можно надеяться, что он сможет написать с нуля игровой движок с лучшей производительностью?
Я вот вообще не смогу ничего собрать на юнити, не говоря уже об оптимизации, потому что я юнити умею только устанавливать и впринципе занимаюсь бэкендом. Но что касается движка для платформера, то думаю за неделю где-нибудь напишу. Рабочий прототип, в котором можно будет прыгать по платформе, вообще, наверное за день соберу. По фичам будет хуже, чем в юнити, но для платформера может быть больше и не нужно?
Pavenci
07.05.2019 09:22Очень хорошая статья. В реальной жизни не всё так однозначно, потому что первое что, при всём желании, не даёт разработчику возможности реализовать своё решение вместо готового — это сжатые сроки и давление со стороны менеджера/команды. Однако, если есть время то конечно лучше независимое решение заточенное целиком под проект.
Ещё бывает так, что форкнуть существующее и выпилить всё лишнее бывает быстрее, чем написать своё. А бывает и наоборот, своё написать намного проще и быстрее, чем разбираться в чужих дебрях. Так что нужно уметь трезво оценивать обстоятельства и учитывать все нюансы. Мне кажется именно эти умения и отличают по-настоящему опытного разработчика от тех, кто выбирает стек проекта по звёздочкам на гитхабе.VolCh
07.05.2019 14:41Однако, если есть время то конечно лучше независимое решение заточенное целиком под проект.
Время и деньги на разработку, плюc, наверное главное, время и деньги на поддержку.
AlexVist
07.05.2019 09:37Приветствую! Хотелось бы дать небольшой комментарий. Любая проблема, как медаль, имеет две стороны. Так и аргументы высказанные в статье описывают лишь одну сторону медали. Спасибо за примеры из практики. Так, действительно, лучше разъяснять свою точку зрения.
Существует и обратная сторона медали. Разработка своего в рамках решения задачи далеко не всегда есть велосипед. Вопрос заключается в том, учтен ли уже существующий опыт при решении подобных задач. Потому что изобретение велосипеда часто превращается в изобретение квадратного деревянного колеса.
Часто разработчики мнят себя гениями и первооткрывателями. Но, согласитесь, что мы в средней массе не Моцарты, а, дай Бог, Сальери. Из этого следует, что выбор пути решения должен быть результатом изучения технологий и подбора правильного стратегического решения. Которое не приведет в дальнейшем к серьезным проблемам.
Опыт и знания позволяют иногда выносить суждения о том, что не нужно изобретать велосипед. И все, далеко не ограничено выбором API, фреймворка или библиотеки. Естественно, что все выводы должны быть обоснованы. Но я, даже, предлагая уже известные и успешно реализуемые много лет концепции, сталкивался с непросветной глупостью "гениев изобретения велосипедов". Как результат в промышленной реализации возникали и продолжают возникать проблемы, которых легко можно было избежать.Whuthering
07.05.2019 11:26Часто разработчики мнят себя гениями и первооткрывателями.
Именно. У нас почему-то каждый второй Вася из Кукуево твердо уверен, что уж он то напишет движок/фреймворк/библиотеку/алгоритм гораздо лучше, чем огромное сообщество разработчиков или инженеры компании, которые на подобных проектах собаку съели. Особенно опасна такая самоуверенность в области криптографии, кстати.
Да, иногда при узкой специфике задачи и большом релевантности опыте у Васи получается очень хорошо и даже лучше, но чаще у жизни наблюдается обратное, увы.AlexVist
07.05.2019 11:43Да беда и в релевантности тоже. Если человек несколько лет изобретал в какой-то области квадратные колеса. То вот вам и специалист со стажем! Только качество стажа и кругозор у него слишком узки.
RoSenMann Автор
07.05.2019 12:26+1Потом он выкладывает свое решение на гитхаб, формирует пакет, рекомендует ее кому-то на StackOverflow и понеслась. Теперь его девиз «Не изобретай велосипед, возьми мой!» :)
Посылку поддерживаю. Поэтому я и говорю о «нескольких минутах раздумья». В сущности, бездумно бросаться реализовывать так же безалаберно, как и бездумно втаскивать другие решения.
virtualsys
07.05.2019 10:28+1Самое важное при выборе — рассудительность. А рассудительность включает адекватную оценку своих целей, возможностей и рисков. Тут идет рассуждение на уровне кодера-разработчика.
Если подняться на уровень мышления повыше, то основной формулой успеха является взять чужое решение и его облагородить!!! Не изобретать заново, а улучшить, или адаптировать под себя. И там надо учитывать так много нюансов, зачастую тратя много времени и ресурсов, что у кого-то его эмпирический опыт подсказывает сразу — «не надо изобретать велосипед». Это называется интуиция. Она, конечно, может поводить иногда.
Но чаще подводят велосипеды с квадратными колесами.
На мой взгляд, при более-менее сложной задаче, первый посыл — смотреть прототипы, аналоги и т.п. Это и для изобретения тоже нужно. И только все взвесив и обосновав, оценив свои силы, браться что-то кардинально менять и изобретать свое.
Амфибия летает плохо, но зато не тонет. Свой дирижабль построить легко, и он даже будет летать. Но до первой грозы.
P.S. Ну вот упомянули Unity всуе. Плохой пример, имхо. Это, конечно, монстр. Зато будет актуален для проектов любого масштаба, вплоть до прикладного моделирования и т.п. Но полно более простых вариантов, даже на уровне библиотек. Вот тут точно, свой велосипед выйдет золотым. Если мы говорим не про разовый проект, а про вхождение в геймдев на профессиональном уровне (хотя бы в мечтах).
agalakhov
07.05.2019 11:12+1Пишем большой проект. Все сторонние библиотеки четко делятся на три категории:
1) кандидаты на добавление в стандартную библиотеку,
2) те, в которые нам приходится контрибьютить,
3) непригодные к использованию вообще.
Мы выработали правило: не бери стороннюю библиотеку, если не готов посылать в нее пулл-реквесты.
ToSHiC
07.05.2019 11:38Хороший пример: CluNet.
Так если автор так засматривался на CAN, то почему не CAN то? Ни одного ограничения, которое помешало бы использовать сам CAN, в статье не упоминается.
Newbilius
07.05.2019 12:44Мне кажется пример про игровые движки мимо. Он подразумевает, что у разработчика
- хватает умений, чтобы не только написать всю игровую логику, но и игровой движок, причём делающий необходимый минимум функций не хуже или лучше готовых движков. Сюда может входить удобный редактор уровней, поддержка сразу нескольких игровых платформ (Windows/PS/Xbox), хорошая работа с разным оборудованием (вспоминаем те же геймпады) и многое другое
- есть время, чтобы написать не только игру, но и движок, а так же в дальнейшем его поддерживать
И на любой пример "автор написал свой движок и выпустил популярную игру" можно привести сотню "автор взял готовый движок и написал популярную игру". Блин, да даже Blizzard использовала Unity для Hearthstone, хотя вот у кого-кого, а у них недостатка в ресурсах уж точно нет.
И нет, я выводы статьи не оспариваю — просто пример с игровыми движками расходится с оптимальной практикой. В 9 случаях из 10 стоит таки выбрать готовое решение. А вот в единственном десятом у автора собственного движка достаточно компетенций, чтобы ему этот совет был бесполезен — он и сам может принять такое решение :)
P.S. И да, я сам писал с нуля и свой игровой движок, и редакторы уровней, и CMS для сайтов — всякое бывало. Но часто это был не оптимальный выбор, а скорее повод прокачать скилл.
VolCh
07.05.2019 14:50Ну вот вы говорите о необходимом минимуме. Но у каждого он свой, не так ли? Плюс системніе требования могут несколько различаться. Плюс нужно время, чтобы освоить готовый движок. И не факт, что этот потребует меньше времени, чем написание своего.
rombell
07.05.2019 14:39Что касается использования игр на Unity — имею негативный опыт пользователя.
Одна достаточно несложная, но увлекательная игра, в которую я постоянно донатил, была на Unity. После очередного обновления движка Unity он, а с ним и игра, перестал запускаться на относительно старых системах и относительно старых браузерах типа ФФ двухгодичной давности. Я решил, что эта игра не стоит обновления железа и системы. При этом сама игра от обновления не получила ничего, всё и так прекрасно работало (и, кстати, практически не развивалось — так что новые возможности движка не требовались, даже если они и были)
i360u
07.05.2019 15:05Очень часто сталкиваюсь с ситуацией, когда на написание велосипеда с нуля тратится меньше времени, чем на один только поиск подходящей либы, не говоря уже о ковырянии в ее спеках в попытках все настроить как тебе нужно. Это приводит к определенному когнитивному искажению, когда велосипед хочется писать во всех случаях сразу и не тратить время ни на что другое. И тут водоразделом становятся ваши архитектурные навыки: если не уверены, что ваш код будет легко читаться и поддерживаться… Хотя к черту все, больше велосипедов хороших и разных, и гори все синим пламенем!
wladyspb
07.05.2019 16:31С точки зрения получения ценного опыта — «каждый разработчик должен написать свой фреймворк». Не обязательно его при этом использовать) Я бы сказал, что для достаточной компетенции нужно написать свой фреймворк, и изучить пару тройку популярных.
В целом, статью поддерживаю, очень часто приходится писать свои решения, а чужие, в случае когда принимается решение «не писать свой велосипед», иногда приносят много боли на этапе поддержки, или когда выясняется что для фичи, которая развивается, понадобился не только функционал «А», представленный в библиотеке, но и функционал «Б», который придумали уже после внедрения сторонней либы, и который в ней отсутствует.
Впрочем, для наиболее распространённых задач, на популярных языках, как правило есть несколько десятков\сотен готовых решений, и достаточно найти в топе по скачиваниям на гитхабе такой вариант, который поддерживается мейнтейнерами. К сожалению, не все задачи, которые приходится решать, являются «распространёнными»)
nrgian
07.05.2019 17:29В кругах wannabe разработки компьютерных игр такая фраза адресуется любому, кто посмеет реализовать свой движок. «Зачем изобретать велосипед? Возьми юнити!»
Если ты сумеешь полноценно реализовать движок хотя бы приближающийся к Unity — ты более высокой квалификации, чем заметная доля программистов под Unity (извините, ребята).
Для тех не стоит вообще вопрос написать движок, ибо не смогут.
Heian
Неистово плюсую. К сожалению, далеко не всегда распространенные решения не то что идеальны, но даже просто хороши. Это все равно, что строить дом из кирпичей, где один кирпич расколот и замотан изолентой, другой не подходит по размерам, а третий обладает маленьким выступом в дырке для арматурины, из-за которой арматурина не вставляется.
P.S. Сам плевался от фейсбучных SDK, в итоге написал за полчаса коннектор на curl.