07.08.2022

Сергей Немчинский: Что такое преждевременная оптимизация? Почему это плохо?

Сергей Немчинский
10 минут просмотра
Сергей Немчинский: Что такое преждевременная оптимизация? Почему это плохо?

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

Преждевременная оптимизация — корень всех зол

Так сказал Дональд Кнут, автор книги «Искусство программирования». Почему же он так считает? Для начала разберемся с терминами.

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

Цена преждевременной оптимизации слишком высока

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

Цель написания кода — принести пользу бизнесу. Исходя из этого, оптимизация является преждевременной, когда она еще не нужна бизнесу. Оптимизация — точно такая же фича, как и, к примеру, какая-то кнопка на экране, инструмент для пользователя, кабинет и пр. Делать ее нужно только в том случае, когда она понадобится бизнесу и он ее потребует.

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

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

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

Новички хотят все оптимизировать

Знаете, как определить, что разработчик — новичок? Он постоянно хочет все оптимизировать. Это очень определяющий маркер. Новички говорят, что какая-то программа тормозит, рассказывают о скорости работы языков программирования и пр. 

При этом я ни разу не слышал подобных разговоров от топовых программистов, а я общался со многими. Ни один из них не начинал сравнивать, что какой-то язык работает медленнее или быстрее (разве что в качестве ответов на вопросы кому-то). А если такой разговор и заходит, то в духе, что язык работает достаточно быстро. Вот это «достаточно быстро» и означает, что дальше оптимизировать будет не оптимально. Начинающие же разработчики очень любят тему скорости работы. Например, что язык С намного быстрее Python. Ок, но только сайты не будут писать на языке С, потому что это очень дорого. Для этого используют Python или PHP. А дальше, когда рынок это запросит, начинается работа над скоростью. И то ускорение идет до «достаточно быстро», и сколько это конкретно — определяет рынок.

Как получаются успешные проекты?

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

Расскажу вам, как с точки зрения бизнеса появляются успешные проекты. Сначала из чего попало лепится идея проекта — Proof of concept. Просто чтобы показать потенциальному заказчику, что мы можем это реализовать. Оно может очень тормозить, глючить и вообще работать черт знает как. Но если потенциальный заказчик скажет, что все отлично — то это все вываливается на рынок прямо так как есть, вносятся лишь минимальные изменения.

Время работает против вас. Нужно максимально быстро застолбить максимально большую часть рынка. Поэтому первые несколько лет стартап вынужден полностью забить на любую оптимизацию. Я всегда говорю, что если в стартапе вы пишите unit test, вы тратите деньги клиента просто так — не делайте этого. Вам нужно торопиться как можно быстрее, забывая о техническом долге и обо всем остальном. Потому что в данный момент времени стоит вопрос выживания, а красивый вылизанный код — дело второстепенное. Либо вы успеваете выйти на рынок, либо нет, и тогда высокое качество вашего кода будет до одного места.

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

Интересное наблюдение. Во всех успешных проектах, в которых я работал, юнит-тестами было покрыто меньше 10% кода. Почему? Именно из-за описанного выше —  проект растет быстрее, чем вы успеваете дописать к нему юнит-тесты.

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

Как делать правильную оптимизацию приложений?

Как делать оптимизацию, чтобы она не была преждевременной? Сначала вы пишите приложение так, чтобы оно работало. Потом, когда у вас появится возможность, наводите в нем порядок, проводите рефакторинг (если, конечно, вы не на этапе proof of concept). После этого запускаете приложение, если нужно, проводите измерение времени работы, и спрашиваете бизнес, подходит ли ему такая скорость. Скорее всего про скорость даже спрашивать не придется, потому что если не Ок — вам и так скажут. Но если все же хотите перестраховаться — спросите. Но, помните, что оптимизировать скорость работы без такого запроса со стороны бизнеса не нужно.

Если вы получили такой запрос от бизнеса, что нужно делать? Вы, наверное, скажете — идти и оптимизировать. Нет, ни в коем случае! Как все происходит. Что-то тормозит, программист предполагает, в каком куске кода проблема и хочет его оптимизировать. Но как показывает моя практика, в 80% случаев программист ошибается со своей оценкой. Соответственно, если начать оптимизировать сразу, вы оптимизируете не то что нужно и не решите проблему. Примите за истину — вы не знаете, где тормозит. Чтобы узнать это, нужно запустить профайлер. После этого вы будете уже наверняка знать место, которое тормозит (и оно почти наверняка будет не тем, о котором вы думали изначально). И вот именно его вы портите. Вы должны понимать, что для того чтобы оптимизировать код, его нужно сделать ужасным. Если вы пишете код в расчёте на то, что он будет оптимизироваться, значит у вас изначально весь код нечитабельный. Если же вы сначала написали красивый код, который тормозит, то потом вы оптимизируете только то место, которое реально нужно оптимизировать.

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

Дисклеймер

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

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

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

Всегда ваш Сергей Немчинский