Simplicity is An Advantage but Sadly Complexity Sells Better
Эссе разбирает парадокс: простота — это преимущество, но сложность лучше «продаётся». Автор объясняет, почему сложные решения воспринимаются как признак усилий, мастерства, инноваций и гибкости, тогда как простые системы дешевле в эксплуатации, легче масштабируются и часто работают не хуже сложных (примеры: tree-based модели против нейросетей, dot product против neural collaborative filtering, Instagram на PostgreSQL и Redis). Награждение сложности порождает синдром «not invented here» и излишнее усложнение ради продвижения по службе или принятия статей. Цель должна быть в том, чтобы решать сложные проблемы максимально простыми средствами, опираясь на бритву Оккама. В дополнении Rasmus Schultz обсуждает обратную предвзятость: люди по привычке переиспользуют знакомые сложные библиотеки вместо того, чтобы построить что-то проще, и приводит пример эволюции PSR-6 → PSR-16 в PHP.
Простота — это преимущество, но, увы, сложность лучше продаётся
[ machinelearning engineering production 🔥 ] · 10 мин чтения
Иногда мы слышим о том, что научную статью отклонили, потому что метод оказался слишком простым, или о том, что в повышении отказали, потому что артефактам работы не хватило сложности. Думаю, это отчасти объясняется цитатой Дейкстры:
«Простота — великая добродетель, но для её достижения требуется упорный труд, а для её оценки — образование. И что ещё хуже: сложность продаётся лучше». — Edsger Dijkstra
Почему сложность лучше продаётся?
Сложность сигнализирует об усилиях. Статьи с трудными идеями и техническими деталями намекают на кровь, пот и слёзы. Системы с большим числом компонентов и функций кажутся результатом большего труда, чем системы с меньшим. Поскольку сложные артефакты воспринимаются как требующие больше усилий, их также считают более трудными в создании и потому более достойными. А из-за предполагаемых вложенных усилий их часто оценивают как более качественные.
Сложность сигнализирует о мастерстве. Сложная система с множеством движущихся частей предполагает, что её создатель владеет каждой из них и умеет интегрировать их вместе. Недоступные статьи, изобилующие жаргоном и доказательствами, демонстрируют экспертизу в предмете. (Именно поэтому мы и гоняем кандидатов на собеседованиях по алгоритмам и структурам данных, которые редко используются в работе.) Если непосвящённым трудно понять сложную идею или систему, её создатель ведь наверняка эксперт, верно?
Сложность сигнализирует об инновации. Статьи, изобретающие совершенно новые архитектуры моделей, считаются более новаторскими по сравнению со статьями, адаптирующими существующие сети. Системы с компонентами, построенными с нуля, считаются более изобретательными, чем системы, переиспользующие уже готовые части. Работа, которая просто опирается на существующие наработки или переиспользует их, не такая уж и инновационная.
«Идея слишком проста, почти как трюк. Она меняет всего одно, а всё остальное точно такое же, как в предыдущих работах». — Reviewer 2
Сложность сигнализирует о большем числе функций. Системы с компонентами, которые можно комбинировать, намекают на гибкость и способность охватить все сценарии. Например, поддержка одновременно SQL и NoSQL хранилищ или одновременно batch- и streaming-пайплайнов. Поскольку у сложных систем больше «лего-кирпичиков» по сравнению с простыми, их считают более адаптируемыми и лучше реагирующими на изменения.
В сумме всё это приводит к complexity bias — когнитивному искажению, при котором мы незаслуженно отдаём предпочтение сложным идеям и системам перед более простыми.
Почему простота — это преимущество?
Простые идеи и функции легче понять и использовать. Это повышает шансы их принятия и влияния. Их также проще объяснить и получить по ним обратную связь. Напротив, сложные системы тяжелее объяснять и поддерживать, и пользователям трудно понять, что и как с ними делать. Из-за слишком большого числа «ручек» ошибки случаются чаще. Из-за слишком большого числа шагов возникает неэффективность.
Простые системы легче строить и масштабировать. Системы, требующие меньше, а не больше компонентов, проще реализовать. Использование стандартных, готовых технологий также облегчает поиск квалифицированных людей, которые смогут их внедрить и поддерживать. А поскольку у более простых систем меньше сложности, кода и внутренних взаимодействий, их легче понимать и тестировать. Напротив, излишне сложные системы требуют больше времени и ресурсов на постройку, что ведёт к неэффективности и расточительству.
Когда Instagram был куплен в 2012 году, его команда из 13 человек обслуживала десятки миллионов пользователей. Компания масштабировалась и удерживала операционную нагрузку на одного инженера низкой, придерживаясь проверенных технологий вместо новых и блестящих. Пока другие стартапы переходили на модные NoSQL-хранилища и мучились с ними, Instagram оставался lean, опираясь на боевые и понятные PostgreSQL и Redis.
У простых систем ниже операционные издержки. Развёртывание системы — это не финишная черта; это стартовая. Основная масса работы приходится на этап после вывода в продакшен и выполняется, скорее всего, не той командой, что её строила. Сохраняя системы простыми, мы снижаем стоимость их поддержки и продлеваем их срок жизни.
У простых систем меньше движущихся частей, которые могут сломаться, и поэтому они надёжнее и проще в починке. Отдельные компоненты также легче обновить или заменить, потому что внутри системы меньше взаимодействий. Напротив, сложные системы более хрупкие и дороги в поддержке, поскольку слишком много компонентов должна охватить ограниченная команда. Большее число взаимозависимых частей также затрудняет диагностику.
«Чем проще вещь, тем меньше она склонна расстраиваться и тем легче её починить, когда она расстроится». — Thomas Paine, Common Sense, 1776
Если говорить конкретно о machine learning, простые методы вовсе не обязательно показывают худшие результаты, чем более изощрённые. Неполный список примеров:
Tree-based модели > глубокие нейросети на 45 средних табличных датасетах Жадные алгоритмы > graph neural networks на комбинаторных задачах на графах Простое усреднение ≥ сложные оптимизаторы в задачах multi-task learning Простые методы > сложные методы по точности прогнозирования по данным 32 статей Dot product > neural collaborative filtering в рекомендации товаров и поиске
Скромный dot product обходит глубокие методы обучения для рекомендаций (source)
Что не так с поощрением сложности?
Это стимулирует людей делать вещи излишне сложными. Использование простых методов или построение простых систем кажется более лёгким и поэтому ценится меньше. В итоге люди подыгрывают системе, чтобы получить больше наград, и самое простое решение перестаёт быть самым очевидным. Сложность порождает ещё больше сложности, и в конце концов работать становится невозможно.
Это также подпитывает менталитет «not invented here», при котором люди предпочитают строить с нуля и избегать переиспользования существующих компонентов, даже когда это экономит время и силы. Это растрата времени и ресурсов, часто приводящая к худшим результатам.
К сожалению, большинство процессов повышения переоценивают сложность артефактов работы.
Простые решения проще внедрять и масштабировать, чем сложные. Однако повышения часто получают те, кто создаёт сложные решения. Нам нужно начать признавать силу простого. Не соглашайтесь на сложное. Это ловушка. — Bryan Liles (@bryanl) 5 августа 2022
То же самое верно и для статей по machine learning.
Распространённое замечание рецензентов по ML — что метод слишком прост или собран из существующих частей. Но простота — это сила, а не слабость. Простые методы у людей принимаются гораздо охотнее, и обычно они также интерпретируемее и интуитивнее. 1/2 — Micah Goldblum (@micahgoldblum) 2 августа 2022
(Если ваша идея не получила заслуженного признания, утешьтесь тем, что такие прорывы, как фильтр Калмана, PageRank, SVM, LSTM, Word2Vec, Dropout и так далее, тоже были отклонены. Мы в принципе плохо умеем оценивать, насколько полезной или значимой окажется инновация. 🤷)
Как же тогда стоит думать о сложности?
Цель должна быть в том, чтобы решать сложные задачи как можно более простым решением. Вместо того чтобы фокусироваться на сложности решения, нам стоит фокусироваться на сложности задачи. Простое решение демонстрирует глубокое понимание проблемы и умение избежать более громоздких и дорогих вариантов. Зачастую лучшее решение — это именно простое.
«Всё должно быть сделано настолько просто, насколько возможно, но не проще». — Albert Einstein
Вместо одного сложного универсального решения подумайте о нескольких сфокусированных. Решение «один размер на всех» обычно оказывается менее гибким и переиспользуемым, чем ожидалось. А поскольку оно обслуживает несколько сценариев и стейкхолдеров, оно склонно быть «tightly coupled» и требует больше координации при планировании и миграциях. Напротив, узкоспециализированные системы проще эксплуатировать — и, что неизбежно, выводить из эксплуатации.
За много лет работы tech lead-ом, затем консультантом и потом в @godotengine я убеждён, что главный враг, с которым воюют software-инженеры — это глубокая и распространённая вера в то, что универсальные решения проблем (даже гипотетических) всегда существуют. Принятие того, что их не существует, ведёт к лучшему коду. pic.twitter.com/Tc14NGOexi — Juan Linietsky (@reduzio) 3 июля 2022
А овчинка стоит выделки?
Один из способов преодолеть complexity bias — бритва Оккама. Она гласит, что самое простое решение или объяснение обычно и есть правильное. Так что не будем спешить отбрасывать простые идеи или нагружать их излишней сложностью, чтобы оправдать их ценность.
А ещё спросите себя: с учётом цены сложности, стоит ли овчинка выделки?
Благодарю Yang Xinyi и Swyx за вычитку черновиков.
Дополнение: Расмус о другой стороне медали, где люди предпочитают знакомые — но более сложные — компоненты, расходуя ресурсы и получая худший результат.
Обожаю эту статью, огромное спасибо, что её написал! 🤩
Есть только один момент, который мне хотелось бы немного обсудить и развить:
Это [поощрение сложности] также подпитывает менталитет «not invented here», при котором люди предпочитают строить с нуля и избегать переиспользования существующих компонентов, даже когда это экономит время и силы. Это растрата времени и ресурсов, часто приводящая к худшим результатам.
Мне кажется, формулировка здесь недостаточно нюансирована, и тут есть о чём поговорить.
По моему опыту, у менталитета «not invented here» есть столь же мощный противоположный bias: люди автоматически предпочитают переиспользовать существующие компоненты, потому что уже знакомы с ними, относительно их личного опыта это проще и это экономит их время и силы — однако если эти существующие компоненты очень сложны, в итоге это может оказаться большей растратой времени и ресурсов и привести к худшему результату, чем если бы было построено что-то более простое.
Если мы хотим делать акцент на простоте, то когда простого варианта нет под рукой, иногда приходится изобретать что-то новое.
И поймите меня правильно: я не отрицаю, что это часто идёт не так — мы нередко думаем, что что-то можно сделать проще, и в итоге создаём нечто просто другое, но столь же сложное, как то, что мы пытались упростить. Некоторые задачи по своей природе сложны и несут в себе сущностную сложность, которую на практике не убрать.
Мой любимый «плохой» пример — это ORM: их продолжают строить, все начинают с простоты, а затем взрывообразно усложняются. Object/relational impedance mismatch — хорошо задокументированный факт, и сама идея по своей природе сложна. (А если вы прямо сейчас вспоминаете ORM, которому удалось остаться простым, — это, скорее всего, и не ORM, а query builder, data mapper или что-то в этом роде.)
В качестве «хорошего/плохого» примера: PSR-6 cache API сменился гораздо более простым PSR-16 cache API, который снизил сложность и количество концепций, сделав API существенно меньшим и менее «мнительным» — можно написать PSR-6-адаптер для PSR-16-реализации (и, кстати, наоборот), что демонстрирует: никакая из сложностей, убранных в PSR-16, изначально не была сущностной.
Существует множество библиотек и фреймворков, которые уходят далеко за пределы работы с сущностной сложностью — часто это делается во имя «developer experience», довольно высокопарной идеи, метко названной «developer», а не «development», что подчёркивает: эта ценность часто абсолютно субъективна и относительна к индивидуальному опыту; в отличие от настоящей простоты, которая является объективной и измеримой штукой (строки кода, число публичных методов, количество связности и так далее).
Эти библиотеки иногда вырастают в 10 раз против своего исходного размера за несколько лет — часто с крупными ломающими изменениями каждые 6–12 месяцев, и, по иронии, это тоже воспринимается как «хорошо», хотя нередко указывает на полный провал в удержании скоупа пакета в рамках, фокусе и небольшом размере. На самом деле, библиотеки, которые не растут, нередко воспринимаются как «мёртвые», и люди склонны переходить на пакеты, которые более «активны», — ещё один явный симптом complexity bias.
Иногда нам приходится строить что-то новое именно для того, чтобы избежать этой расползающейся сложности — если мы можем сделать вещи действительно проще, если можем зафиксировать требования и не дать им расползтись, это может быть благом и иногда единственным способом сдержать ту заразную сложность, которую мы часто наследуем от «слишком активных» библиотек и фреймворков.
Это тонкая тема, и нам нужно быть аккуратными, чтобы не подпитывать ни один из этих противоположных biases. 🙂
Если эта заметка оказалась полезной, пожалуйста, цитируйте её так:
Yan, Ziyou. (Aug 2022). Simplicity is An Advantage but Sadly Complexity Sells Better. eugeneyan.com. https://eugeneyan.com/writing/simplicity/.
или
@article{yan2022simplicity, title = {Simplicity is An Advantage but Sadly Complexity Sells Better}, author = {Yan, Ziyou}, journal = {eugeneyan.com}, year = {2022}, month = {Aug}, url = {https://eugeneyan.com/writing/simplicity/} }
Присоединяйтесь к 11 800+ читателей, получающих обновления по machine learning, RecSys, LLMs и инженерии.