Что важнее — производительность или гибкость кода? Стоит ли отказаться от философии чистого кода ради перфоманса? Отвечаем на эти и многие другие вопросы вместе с командой разработчиков PVS-Studio.
Является ли сегодня чистый код вредным для индустрии разработки программного обеспечения? Вопрос, который то и дело возникает и привлекает к себе достаточно много внимания со стороны сообщества. Сегодня мы постараемся дать конкретные ответы на этот и другие вопросы в этой нескончаемой дискуссии.
Дабы моё мнение не было единственным в статье, я опросил других разработчиков PVS-Studio о том, что они думают о чистом коде. Поэтому мои мысли будут приправлены цитатами коллег, а также капелькой статистики.
Как можно понять из названия, эта статья является продолжением первой части, которую я сконцентрировал вокруг дискуссии Роберта Мартина и Кейси Муратори, поскольку она является самым объёмным материалом по теме. Если вам интересно, то можете ознакомиться с ней. Однако для прочтения этой статьи обращаться к первой части необязательно.
Отойдя от проблем производительности, задам такой вопрос: что такое чистый код? Думаю, если бы мы с вами сидели в одной комнате, то услышали бы количество ответов, соответствующих числу собеседников. Я к тому, что каждый понимает под чистым кодом что-то своё: для кого-то достаточно правильного именования и грамотной декомпозиции, а кто-то не видит чистого кода без полиморфизма и высокого уровня абстракции.
Собственно, поэтому я чистый код воспринимаю больше как философию, и такими же мыслями со мной поделился наш Senior разработчик C++ и техлид Олег Лысый:
Чистый код я рассматриваю, скорее, как философию. Естественно, любые подходы к решению задач лучше рассматривать с осторожностью.
Думаю, попробовать ответить на вопрос о позитивном или негативном влиянии чистого кода можно через цели его существования. Проблематика, из-за которой философия чистого кода возникла, заключается в необходимости писать код таким образом, чтобы его разработка и поддержка не превращались в бесконечные сеансы танцев с бубнами. Это экономит как силы, так и время разработчиков и позволяет им продуктивнее работать над своими задачами.
Думаю, каждый разработчик в своей профессиональной жизни хоть раз встречал код, который хотелось почистить. Прямо вот запастись бытовой химией и провести там генеральную уборку, чтобы никто после вас из-за него не страдал. И вот соблюдение принципов чистого кода в теории должно сократить количество таких ситуаций.
Именно поэтому я считаю, что чистый код — это хорошо. Думать о том, как написать работающий и читабельный код — это профессиональный долг каждого разработчика. И дискуссии о том, что чистый код неидеален, также хороши и важны. Благодаря им мы можем понять, что стоит поменять в нашем восприятии чистого кода для того, чтобы написанное нами программное обеспечение было ещё лучше и качественнее.
Посмотрим, как к чистому коду относятся наши разработчики:
Большинство остановилось на позиции нейтралитета! Более того, эта позиция вполне обоснована, и далее, думаю, у меня получится объяснить, почему такая статистика получилась.
Мы поговорили о том, что чистый код — это круто, однако есть множество "но", которые мы затронем сейчас и далее.
Правил чистого кода довольно много и касаются они самых разных вещей: названия функций и переменных, комментариев, форматирования, подходов к тестированию, архитектуры, обработки ошибок и многого другого. Вопрос состоит в том, стоит ли выполнять абсолютно все правила в любом контексте.
Чтобы ответить на него, предлагаю наглядный пример. Каждый разработчик в начале своего пути пишет программу, которая выводит в консоль заветные слова: "Hello World". В этом репозитории, о существовании которого мне рассказали коллеги, представлено довольно занятное решение этой задачи. Дело в том, что это Hello World Enterprise Edition, именно поэтому простой вывод всем известного сообщения на экран засыпан абстракциями, фабриками, стратегиями и прочими атрибутами ООП.
Собственно, вопрос мой в том, насколько уместно соблюдать все сложные архитектурные правила чистого кода в каком-нибудь TODO приложении на Python? Такие архитектурные манёвры не упростят работу над приложением, и разработка TODO трекера будет довольно долгой, причём неясно зачем.
Мысль, которую я хочу донести, состоит в том, что не все задачи требуют соблюдения всех правил чистого кода. Ещё раз процитирую нашего техлида команды C++ Олега Лысого:
В каждом проекте есть свои паттерны и правила. Не получится применить их все в каждом конкретном случае. Если что-то кажется избыточным, то, скорее всего, это не нужно именно здесь и сейчас. Задача архитектора — подобрать те паттерны, приёмы и подходы к написанию кода, которые будут работать конкретно в его проекте.
Посмотрим, считают ли избыточным чистый код наши разработчики:
И здесь большинство разработчиков согласились, что правила чистого кода могут быть избыточны! Это не значит, что от чистого кода нужно отказаться, а лишь означает необходимость разумнее подходить к их использованию.
Написание тестов — важная часть процесса разработки программного обеспечения, с помощью которого можно понять, насколько верный и качественный код был написан. Но когда нужно писать тесты?
В ходе дискуссии между Робертом Мартином и Кейси Муратори этот вопрос поднимался, и дядя Боб рассказывал, что TDD (Test Driven Development) — это формат написания тестов, приносящий ему максимум пользы. Кейси же говорил, что предпочитает тестировать конкретные части кода.
Собственно, в этом вопросе лично я тоже не был бы столь категоричным. Как мне кажется, оба этих подхода являются вполне себе хорошими и заслуживают внимания. В первую очередь важно понимать, какую задачу нужно решить, чтобы выбрать стратегию написания тестов.
Например, если нужно отрефакторить какой-то проблемный участок кода, то самым верным решением, как мне кажется, будет покрыть этот участок тестами, а уже потом переходить к его изменению, чтобы не нарушить работу остальной программы.
Но, с другой стороны, нет особого смысла тратить драгоценные человеко-часы на написание тестов, которые всегда будут проходить успешно или не будут запускаться в принципе. Они не диагностируют проблемы, а значит не так полезны.
Теперь посмотрим, что думают мои коллеги насчёт того, является ли TDD более полезной стратегией тестирования:
Здесь большинство тоже предпочитает выбирать стратегию тестирования в зависимости от решаемой задачи. А сторонников того или иного подхода получилось поровну.
Другой вопрос, касающийся тестов — это 100% покрытие ими кода. Думаю, каждый не раз слышал эту мысль, однако здесь тоже не всё так гладко.
Покрыть весь код тестами — вполне себе вызов, потому что для некоторых участков кода это может быть невозможно, например, модули, использующие псевдослучайные числа. Мы просто не сможем предугадать результат их выполнения.
Помимо этого, никто не может гарантировать, что на 100% покрытый тестами код будет без ошибок. Важно понимать, что тесты пусть и нужны для автоматизации процесса контроля качества, но они всё ещё пишутся такими же разработчиками, как мы с вами. Поэтому существует вероятность упущения какой-то ситуации, которая может возникнуть у пользователя.
При этом я не считаю, что полное покрытие кода тестами — зло. Мне кажется, идеал в 100% не имеет конкретного смысла. Достаточно покрыть код тестами в важных местах, где никак нельзя допустить ошибок и проблем, чтобы упростить жизнь себе и коллегам.
Я спросил коллег о необходимости полного покрытия кода тестами:
И здесь уже большинство считает, что это нужно. Интересно, что нейтральной позиции придерживается не так много людей по сравнению с позицией избыточности полного покрытия.
Собственно, теперь главный вопрос, ради которого это всё и написано. Что важнее: написать гибкий и поддерживаемый код или сэкономить время выполнения программы?
На самом деле, если вспомнить, какие правила Кейси Муратори приводил в пример, говоря, что чистый код убивает производительность, мы обнаружим всё, что сегодня прямо или косвенно ставили под сомнение.
Всегда ли стоит добиваться максимальной абстракции? Обязательно ли функции должны быть короткими и выполнять одну задачу? Мой ответ: нет, не всегда. Однако замечу, что и выдавливать максимальную производительность из кода необходимо не всегда.
Некоторые ситуации сами по себе не требуют максимальной оптимизации. Мы можем сколь угодно ускорять работу метода и разбивать его на потоки, но если он среди своих шагов выполняет сетевой запрос, то мы будем упираться в скорость соединения.
Лично я стараюсь придерживаться подхода, при котором необязательно заострять своё внимание на чём-то одном. Можно написать чистый код, который будет хорошо работать. Об этом же мне сказал и Константин Куликов, ещё один наш техлид команды C++:
Зависит от проекта, но без контекста качество кода важнее. Говорят, свой код не пахнет, но это первое время. Производительность лучше повышать в узких местах. Например, индекс на таблицу накинуть, а не ядро движка переписывать.
И мы снова упираемся в то, что нужно здраво оценивать выполняемую задачу, принимая решения о тех средствах, которые будут для её решения использованы. В этом вопросе мы сошлись во мнении с Константином Волоховским, тимлидом Java-команды:
Единственное, что тут можно сказать, так это то, что нужно иметь свою голову на плечах и относиться к рекомендациям как к рекомендациям, а не слепо им следовать.
То же самое можно сказать и насчёт спора вокруг выбора if или switch. Здесь нет особой разницы с точки зрения написания качественного и чистого кода. Подарить коду долгую и спокойную жизнь можно вне зависимости от подходов к проектированию. Об этом сказал и дядя Боб. Если в вашем коде быстрее растёт количество операций, чем типов, то зачем намеренно усложнять свою работу?
Как я и говорил ранее, чистый код — круто, производительность — круто, но нужно не забывать о том, какие задачи вы решаете и каких целей хотите достичь. Проще говоря, не стоит фанатично относиться к этим вещам.
Ради интереса я спросил у наших разработчиков, как часто они испытывали проблемы из-за соблюдения правил чистого кода и принципов SOLID. В итоге только Тарас Шевченко, наш разработчик сказал мне, что из-за проблем в иерархиях наследования у него однажды запаздывало обновление интерфейса.
Мне кажется, что проблемность снижения производительности от чистого кода на самом деле преувеличена. Даже если это и не так, то, проектируя свою программу, просто нужно учитывать, какой вы хотите получить.
Давайте посмотрим, считают ли разработчики PVS-Studio чистоту кода важнее производительности:
И здесь большинство принимает ту же позицию, что и я! Не нужно ставить чистоту кода или производительность важнее остального. Нужно держать баланс, чтобы код был написан хорошо, и производительность была достаточной.
Пришло время подытожить всё, о чём было сказано в статье.
Дискуссия о том, что важнее – чистый код или производительность, полезна для того, чтобы найти точки, в которых эти понятия не могут пересечься. Однако чётко отделять одно от другого, думаю, не имеет особого смысла.
Качественно написанный код должен быть таким, чтобы после его прочтения не захотелось прикупить новый оптический имплант. Но и о производительности программы забывать не стоит. Такой компромисс действительно поможет в получении хорошего рабочего результата.
Все действия: от написания кода, до его тестирования и поддержки, — не должны быть выстрелом в колено рабочего процесса. "Всего должно быть в меру" — об этом и хотелось сказать этими двумя статьями. Надеюсь, у нас получилось привести достаточное количество аргументов в защиту такой позиции.
"Стоять на своём часто значит проявлять упрямство. Способность к разумным уступкам — свидетельство здравого смысла".
Джейн Остин
0