Что такое чистый код
«Чистый код» Роберт Мартин. Конспект. Как писать понятный и красивый код?
Я решил написать конспект книги, которая всем известна, а сам автор называет ее «Школой учителей Чистого кода». Пристальный взгляд Мартина как бы говорит:
«Я тебя насквозь вижу. Ты опять не следуешь принципам чистого кода?»
Глава 1. Чистый код
Что же такое этот самый чистый код по версии Мартина в нескольких словах? Это код без дублирования, с минимальным количеством сущностей, удобный для чтения, простой. В качестве девиза можно было бы выбрать: «Ясность превыше всего!».
Глава 2. Содержательные имена
Имена должны передавать намерения программиста
Имя переменной, функции или класса должно сообщить, почему эта переменная существует, что она делает и как используется. Если имя требует дополнительных комментариев, значит, оно не передает намерений программиста. Лучше написать, что именно измеряется и в каких именно единицах.
Пример хорошего названия переменной: daysSinceCreation;
Цель: убрать неочевидность.
Избегайте дезинформации
Не используйте слова со скрытыми значениями, отличными от предполагаемого. Остерегайтесь малозаметных различий в именах. Например, XYZControllerForEfficientHandlingOfStrings и XYZControllerForEfficientStorageOfStrings.
По-настоящему устрашающие примеры дезинформирующих имен встречаются при использовании строчной «L» и прописной «O» в именах переменных, особенно в комбинациях. Естественно, проблемы возникают из-за того, что эти буквы почти не отличаются от констант «1» и «0» соответственно.
Используйте осмысленные различия
Если имена различаются, то они должны обозначать разные понятия.
«Числовые ряды» вида (a1, a2,… aN) являются противоположностью сознательного присваивания имен. Они не несут информации и не дают представления о намерениях автора.
Неинформативные слова избыточны. Слово variable никогда не должно встречаться в именах переменных. Слово table никогда не должно встречаться в именах таблиц. Чем имя NameString лучше Name? Разве имя может быть, скажем, вещественным числом?
Используйте удобопроизносимые имена: generationTimestamp намного лучше genymdhms.
Выбирайте имена, удобные для поиска
Однобуквенные имена могут использоваться только для локальных переменных в коротких методах.
Избегайте схем кодирования имен
Как правило, кодированные имена плохо произносятся и в них легко сделать опечатку.
Интерфейсы и реализации
Я (автор книги) предпочитаю оставлять имена интерфейсов без префиксов. Префикс I, столь распространенный в старом коде, в лучшем случае отвлекает, а в худшем — передает лишнюю информацию. Я не собираюсь сообщать своим пользователям, что они имеют дело с интерфейсом.
Имена классов
Имена классов и объектов должны представлять собой существительные и их комбинации: Customer, WikiPage, Account и AddressParser. Старайтесь не использовать в именах классов такие слова, как Manager, Processor, Data или Info. Имя класса не должно быть глаголом.
Имена методов
Имена методов представляют собой глаголы или глагольные словосочетания: postPayment, deletePage, save и т. д. Методы чтения/записи и предикаты образуются из значения и префикса get, set и is согласно стандарту javabean.
Воздержитесь от каламбуров
Задача автора — сделать свой код как можно более понятным. Код должен восприниматься с первого взгляда, не требуя тщательного изучения. Ориентируйтесь на модель популярной литературы, в которой сам автор должен доступно выразить свои мысли.
Добавьте содержательный контекст
Контекст можно добавить при помощи префиксов: addrFirstName, addrLastName, addrState и т. д. По крайней мере читатель кода поймет, что переменные являются частью более крупной структуры. Конечно, правильнее было бы создать класс с именем Address, чтобы даже компилятор знал, что переменные являются частью чего-то большего.
Переменные с неясным контекстом:
Функция длинновата, а переменные используются на всем ее протяжении. Чтобы разделить функцию на меньшие смысловые фрагменты, следует создать класс GuessStatisticsMessage и сделать три переменные полями этого класса. Тем самым мы предоставим очевидный контекст для трех переменных — теперь абсолютно очевидно, что эти переменные являются частью GuessStatisticsMessage.
Переменные с контекстом:
Не добавляйте избыточный контекст
Короткие имена обычно лучше длинных, если только их смысл понятен читателю кода. Не включайте в имя больше контекста, чем необходимо.
Глава 3. Функции
Компактность!
Первое правило: функции должны быть компактными.
Второе правило: функции должны быть еще компактнее.
Мой практический опыт научил меня (ценой многих проб и ошибок), что функции должны быть очень маленькими. Желательно, чтобы длина функции не превышала 20 строк.
Правило одной операции
Функция должна выполнять только одну операцию. Она должна выполнять ее хорошо. И ничего другого она делать не должна. Если функция выполняет только те действия, которые находятся на одном уровне под объявленным именем функции, то эта функция выполняет одну операцию.
Секции в функциях
Функцию, выполняющую только одну операцию, невозможно осмысленно разделить на секции.
Один уровень абстракции на функцию
Чтобы убедиться в том, что функция выполняет «только одну операцию», необходимо проверить, что все команды функции находятся на одном уровне абстракции.
Смешение уровней абстракции внутри функции всегда создает путаницу.
Чтение кода сверху вниз: правило понижения
Код должен читаться как рассказ — сверху вниз.
За каждой функцией должны следовать функции следующего уровня абстракции. Это позволяет читать код, последовательно спускаясь по уровням абстракции в ходе чтения списка функций. Я называю такой подход «правилом понижения».
Команды switch
Написать компактную команду switch довольно сложно. Даже команда switch всего с двумя условиями занимает больше места, чем в моем представлении должен занимать один блок или функция. Также трудно создать команду switch, которая делает что-то одно — по своей природе команды switch всегда выполняют N операций. К сожалению, обойтись без команд switch удается не всегда, но по крайней мере мы можем позаботиться о том, чтобы эти команды были скрыты в низкоуровневом классе и не дублировались в коде. И конечно, в этом нам может помочь полиморфизм.
В примере представлена всего одна операция, зависящая от типа работника.
Эта функция имеет ряд недостатков. Во-первых, она велика, а при добавлении новых типов работников она будет разрастаться. Во-вторых, она совершенно очевидно выполняет более одной операции. В-третьих, она нарушает принцип единой ответственности (Single responsibility principle), так как у нее существует несколько возможных причин изменения.
В-четвертых, она нарушает принцип открытости/закрытости (The Open Closed Principle), потому что код функции должен изменяться при каждом добавлении новых типов.
Но, пожалуй, самый серьезный недостаток заключается в том, что программа может содержать неограниченное количество других функций с аналогичной структурой, например:
isPayday(Employee e, Date date)
deliverPay(Employee e, Money pay)
Все эти функции будут иметь все ту же ущербную структуру. Решение проблемы заключается в том, чтобы похоронить команду switch в фундаменте абстрактной фабрики и никому ее не показывать. Фабрика использует команду switch для создания соответствующих экземпляров потомков Employee, а вызовы функций calculatePay, isPayDay, deliverPay и т. д. проходят полиморфную передачу через интерфейс Employee.
Мое общее правило в отношении команд switch гласит, что эти команды допустимы, если они встречаются в программе однократно, используются для создания полиморфных объектов и скрываются за отношениями наследования, чтобы оставаться невидимыми для остальных частей системы. Конечно, правил без исключений не бывает и в некоторых ситуациях приходится нарушать одно или несколько условий этого правила.
Используйте содержательные имена
Половина усилий по реализации этого принципа сводится к выбору хороших имен для компактных функций, выполняющих одну операцию. Чем меньше и специализированнее функция, тем проще выбрать для нее содержательное имя.
Не бойтесь использовать длинные имена Длинное содержательное имя лучше короткого невразумительного. Выберите схему, которая позволяет легко прочитать слова в имени функции, а затем составьте из этих слов имя, которое описывает назначение функции.
Аргументы функций
В идеальном случае количество аргументов функции равно нулю. Далее следуют функции с одним аргументом (унарные) и с двумя аргументами (бинарные). Функций с тремя аргументами (тернарных) следует по возможности избегать.
Выходные аргументы запутывают ситуацию еще быстрее, чем входные. Как правило, никто не ожидает, что функция будет возвращать информацию в аргументах. Если уж обойтись без аргументов никак не удается, постарайтесь хотя бы ограничиться одним входным аргументом.
Преобразования, в которых вместо возвращаемого значения используется выходной аргумент, сбивают читателя с толку. Если функция преобразует свой входной аргумент, то результат
должен передаваться в возвращаемом значении.
Аргументы-флаги
Аргументы-флаги уродливы. Передача логического значения функции — воистину ужасная привычка. Она немедленно усложняет сигнатуру метода, громко провозглашая, что функция выполняет более одной операции. При истинном значении флага выполняется одна операция, а при ложном — другая.
Бинарные функции
Функцию с двумя аргументами понять сложнее, чем унарную функцию. Конечно, в некоторых ситуациях форма с двумя аргументами оказывается уместной. Например, вызов Point p = new Point(0,0); абсолютно разумен. Однако два аргумента в нашем случае являются упорядоченными компонентами одного значения.
Объекты как аргументы
Если функция должна получать более двух или трех аргументов, весьма вероятно, что некоторые из этих аргументов стоит упаковать в отдельном классе. Рассмотрим следующие два объявления:
Если переменные передаются совместно как единое целое (как переменные x и y в этом примере), то, скорее всего, вместе они образуют концепцию, заслуживающую собственного имени.
Глаголы и ключевые слова
Выбор хорошего имени для функции способен в значительной мере объяснить смысл функции, а также порядок и смысл ее аргументов. В унарных функциях сама функция и ее аргумент должны образовывать естественную пару «глагол/существительное». Например, вызов вида write(name) смотрится весьма информативно.
Читатель понимает, что чем бы ни было «имя» (name), оно куда-то «записывается» (write). Еще лучше запись writeField(name), которая сообщает, что «имя» записывается в «поле» какой-то структуры.
Последняя запись является примером использования ключевых слов в имени функции. В этой форме имена аргументов кодируются в имени функции. Например, assertEquals можно записать в виде assertExpectedEqualsActual(expected, actual). Это в значительной мере решает проблему запоминания порядка аргументов.
Разделение команд и запросов
Функция должна что-то делать или отвечать на какой-то вопрос, но не одновременно. Либо функция изменяет состояние объекта, либо возвращает информацию об этом объекте. Совмещение двух операций часто создает путаницу.
Изолируйте блоки try/catch
Блоки try/catch выглядят весьма уродливо. Они запутывают структуру кода и смешивают обработку ошибок с нормальной обработкой. По этой причине тела блоков try и catch рекомендуется выделять в отдельные функции.
Обработка ошибок как одна операция
Функции должны выполнять одну операцию. Обработка ошибок — это одна операция. Значит, функция, обрабатывающая ошибки, ничего другого делать не должна. Отсюда следует, что если в функции присутствует ключевое слово try, то оно должно быть первым словом в функции, а после блоков catch/finally ничего другого быть не должно.
О книге Боба Мартина «Чистый код»
(Картинка без намека, просто уж очень хотелось котика в статью добавить! Ведь это основной залог популярности в интернетах, правда? :))
У меня очень неоднозначное отношение к книгам Роберта Мартина… В них довольно много здравых и интересных мыслей, но иногда они выражаются столь категорично, что неокрепшим программерским мозгом они могут восприниматься неправильно. Если же мозг читателя достаточно окреп, чтобы воспринимать советы прагматично, то есть все шансы, что ничего нового из этих советов он (мозг или программист) не вынесет.
Вот и получается, что идеальной аудиторией для этих книг является продвинутый разработчик, окрепший достаточно, чтобы критически оценивать советы «гуру», но при этом не настолько матерый, чтобы эти советы не звучали из разряда «Спасибо, Кэп!».
Хотя, нужно признать, что если в книге «Принципы, паттерны и методики гибкой разработки» спорных советов с моей точки зрения было очень много, то в «Чистом коде» приводится значительно больше разумных и прагматичных советов.
Но поскольку паранойи в книге тоже достаточно, поэтому я хочу начать именно с неоднозначных моментов, а уже потом перейти к радостям.
Спорные и сомнительные моменты
В «Чистом коде» есть несколько откровенно сомнительных моментов. Во-первых, в ней довольно много кода, читать который на страницах книги очень сложно. В 15-й главе рассматривается улучшение парсера командной строки на 60 страницах, 40 из которых – это сплошной код на Java. Главы 16-17 написаны в том же формате, но они короче, поэтому следовать рассуждениям автора проще.
СОМНИТЕЛЬНЫЙ СОВЕТ
Первое правило: функции должны быть компактными. Второе правило: функции должны быть еще компактнее. … Из сказанного выше следует, что блоки в командах if, else, while и т.д. должны состоять из одной строки, в которой обычно содержится вызов функции. Это не только делает вмещающую функцию более компактной, но и способствует документированию кода, поскольку вызываемой в блоке функции можно присвоить удобное содержательное имя.
Некоторые главы откровенно слабые. Мне вообще не понятна цель главы 13 о параллелизме, в которой даются лишь общие сведения о проблемах многопоточности, а затем в приложении А разбираются те же самые темы, но уже более детально и с необходимыми примерами.
Но больше всего меня смущает неоднозначное отношение автора к состоянию и побочным эффектам.
Побочные эффекты суть ложь. Ваша функция обещает делать что-то одно, но делает что-то другое, скрытое от пользователя. Иногда она вносит неожиданные изменения в переменные своего класса – скажем, присваивает им значения параметров, переданных функции…
Несмотря на этот совет, автор во многих примерах использует код с побочными эффектами и отговаривает всеми силами использовать аргументы, используя вместо этого состояние объекта:
СОМНИТЕЛЬНЫЙ СОВЕТ
… Аргументы создают массу проблем с точки зрения тестирования. Только представьте, как трудно составить все тестовые сценарии, проверяющие правильность работы кода со всеми комбинациями аргументов.
В чем разница с точки зрения тестирования между статическим методом с четырьмя аргументами и экземплярного метода объекта с четырьмя полями? Количество граничных условий в обоих случаях одно и тоже; просто в первом случае мы все входные данные протаскиваем явно, а во втором случае – неявно через this.
Стилю кодирования в книге уделено много внимания, при этом большинство советов вполне разумными. Но встречаются и очень неоднозначные:
СОМНИТЕЛЬНЫЙ СОВЕТ
Стандарт кодирования определяет, где объявляются переменные экземпляров; как именуются классы, методы и переменные; как располагаются фигурные скобки и т.д. Документ с явных описанием этих правил не нужен – сам код служит примером оформления.
Перевод
Перевод, мягко говоря, не порадовал. Иногда он «радовал» настолько, что приходилось лезть в оригинал, чтобы понять, о чем идет речь:
ЦИТАТА
Когда у вас появился полный набор тестов, можно заняться чисткой кода и классов.
Для этого код подвергается последовательной переработке (рефакторингу). Мы добавляем несколько строк кода, делаем паузу и анализируем новую архитектуру.
WTF! Как добавление двух строк может повлиять на архитектуру. Есть два варианта: либо мы имеем дело с очень длинными строками, или же авторы здесь говорят не об архитектуре, а о чем-то ином, например, о дизайне! Вся глава 12 (из которой взята эта цитата) полностью испорчена тем, что термин “design” переведен в ней как «архитектура», что делает многие советы автора спорными.
Как обычно, переводчики не потрудились оставить оригинальные термины, так что приходилось догадываться, что же имеется ввиду под терминами «логическая привязка» или «многопоточность» (оказывается, что это high coupling и concurrency, соответственно).
Поскольку содержимое книги относительно простое, то переводчикам удалось лишь «подпортить» впечатление, но не запороть ее полностью.
Дельные советы
На самом деле, все не настолько плохо и «дядюшка» Боб сотоварищи дают много хороших советов. Вот некоторые из них:
Об именовании
Не используйте имена, передающие информацию о реализации. Имена должны отражать уровень абстракции, на котором работает класс или функция.
О понятности кода
Легко писать код, понятный для нас самих, потому что в момент его написания мы глубоко понимаем решаемую проблему. У других программистов, которые будут заниматься сопровождением этого кода, такого понимания не будет.
О длине и выразительности имен
Длина имени должна быть связана с длиной его области видимости. Переменным с крошечной областью видимости можно присваивать очень короткие имена, но у переменных с большей областью видимости имена должны быть длинными. … Таки образом, чем длиннее область видимости, тем более длинным и точным должно быть ее имя.
О содержимом функций
Все операторы в функции должны находиться на одном уровне абстракции, который должен быть на один уровень ниже операции, описываемой именем функции.
Совет кажется простым, но он очень ценен. Код должен читаться, как хорошая проза, а для этого просто необходимо, чтобы функция содержала операторы одного уровня.
Юнит-тесты в качестве инструмента изучения библиотек
Изучение чужого кода – непростая задача. Интеграция чужого кода тоже сложна. Одновременное решение обоих задач создает двойственные сложности. А что, если пойти по другому пути? Вместо того, чтобы экспериментировать и опробовать новую библиотеку в коде продукта, можно написать тесты. Проверяющие наше понимание стороннего кода. Джим Ньюкирк (JimNewkirk) называет такие тесты «учебными тестами».
Этому совету я следую давно и использую юнит-тесты для изучения новых инструментов и библиотек. Это позволяет лучше разобраться с библиотекой и легко возвращаться к примерам использования, поскольку они всегда под рукой.
Очень повеселил один совет. Давайте так: что вам говорит магическая константа 5280? Ничего? Странно, по словам автора, это одно из значений, которое можно спокойно «хардкодить»!
Некоторые числа так легко узнаются, что их не обязательно скрывать за именованными константами – при условии, что они используются в сочетании с ясным кодом. … Число 5280 – количество футов в миле – настолько хорошо известно и уникально, что читатель сразу узнает его, даже если оно будет располагаться вне какого-либо контекста.
Качество книги можно косвенно оценить по количеству интересных цитат и количеству блог-постов, появившихся в процессе чтения. И если в процессе чтения «Принципов, паттернов и методик» появились критические статьи типа «Контракты, состояние и юнит-тесты», то в результате «Чистого кода» появились «Пять принципов чистых тестов» и «Лучшая метрика для определения качества кода».
Достоинства: приличное количество приличных советов
Недостатки: часть советов весьма спорны; иногда советы непоследовательны; перевод хромает на обе ноги.
Что такое «чистый код» в 2020-м?
«Чистый код» и чистый кот
Разработчиков хлебом не корми, дай поспорить о чистоте кода: например, недавно шумиху навёл пост Дэна Абрамова «Goodbye, Clean Code».
Но при этом у самого понятия «чистый код» нет чёткого определения. Главная книга по этому вопросу — «Clean Code», где Роберт «Дядюшка Боб» Мартин сразу заявляет: «сколько программистов, столько и определений». Впрочем, из этого он делает не вывод «говорить об этом бесполезно», а вывод «стоит сравнить разные определения». Поэтому в книге он привёл мнения нескольких выдающихся программистов о том, что такое чистый код.
Нам стало интересно: в 2020-м представления человечества о чистом коде остались теми же, или с выхода книги как-то изменились? Различаются ли мнения у разных айтишников: может, бэкендеры видят всё с одного ракурса, а тестировщики с другого?
А поскольку тема холиварная, наверняка кто-то из вас окажется не согласен с какими-то из мнений. В таком случае айда спорить в комментариях, это тоже весело!
UPD: Когда мы писали эту статью, Роберт планировал приехать на наши конференции. К сожалению, ситуация изменилась. Также из-за запрета на проведение массовых мероприятий мы перенесли конференции на другие даты. Следите за обновлениями на сайте конференции. 13 марта мы обновили этот пост, чтобы он не содержал некорректную информацию.
DotNext
Джон Скит
Джон — легенда Stack Overflow, автор книги «C# in Depth» и один из самых известных дотнетчиков планеты. Он дал нам такое определение:
«Для меня чистый код — это скучный код, с точки зрения имплементации. Единственный сюрприз в нём — это то, насколько он лишён сюрпризов. Я должен чувствовать „Да, я бы мог такое написать”, даже если бы на самом деле я и не мог — по тому, насколько хорошо он спроектирован.
Когда абстракция выбрана верно, имплементация выводится естественным образом. В идеале эта абстракция также должна ощущаться простой и очевидной — хотя на самом деле доведение её до такого состояния могло занять часы, недели, месяцы размышления и экспериментов.
Упомянутое отсутствие сюрпризов позже переносится и на использование: когда я пишу код, используя хорошо спроектированный API, мой код тоже становится очевидным и скучным».
Андрей Акиньшин
Когда мы спросили, что он думает про чистый код, Андрей сослался на два своих старых хабрапоста: «Совершенный код и реальные проекты» и «Комментировать или не комментировать». И мы выбрали для вас пару абзацев оттуда, с которыми кто-то наверняка захочет поспорить:
«Я люблю совершенный код. Ведь это не только правильный подход к написанию программ, но и настоящее искусство. От чтения хорошего листинга я получаю не меньше удовольствия, чем от чтения хорошей книги. Проектировать архитектуру большого проекта ничуть не легче, чем проектировать архитектуру большого здания, а в случае хорошей работы — результат не менее прекрасен. Порой меня завораживает то, как изящно переплелись паттерны проектирования в создании совершенной программной системы. Меня восхищает внимание к деталям, когда абсолютно каждый метод настолько прост и понятен, что претендует на место классического примера совершенного кода.
Но, увы, всё это великолепие разбивается о суровую действительность и реальные проекты. Если мы говорим о продакшн-проекте, то пользователей не волнует, насколько красив ваш код и насколько хороша архитектура, их волнует, чтобы проект хорошо работал. Но я всё равно считаю, что в любом случае нужно стремиться писать правильно, просто при этом фанатизма быть не должно».
Дилан Битти
Хабрачитатели могут помнить Дилана по его вдумчивому и яркому докладу про работу с легаси-кодом: для Хабра мы делали его расшифровку. И когда мы обратились к Дилану по поводу чистого кода, он написал такой развёрнутый и продуманный текст, что его хоть отдельным постом публикуй:
«Для меня интересно, что понятие «чистый код» распространилось далеко за пределы круга людей, читавших книгу Роберта Мартина. Я общался с многими, многими разработчиками, которые слышали слова «clean code», но не читали книгу. Я даже встречал их в кодревью: «Тут всё довольно хорошо, но можешь немного почистить?» — и такая просьба может быть раздражающе неточной, если неочевидно, что «чистый» означает в данном конкретном контексте.
В английском есть слова, которые часто встречаются вместе — «clean», «tidy», «organised», «neat» — и для меня как носителя английского они все означают немного разные вещи. Я думаю, что полезно рассмотреть некоторые коннотации этих слов применительно к разработке софта.
Представим, например, кухню ресторана. У слова «clean» в этом контексте будут очень конкретные коннотации. Всё вымыто, стерилизовано, нет никакой угрозы заражения из-за сырых продуктов, и тому подобное.
Но это не означает автоматически, что здесь всё хорошо организовано. Если вы когда-нибудь пытались приготовить еду у друга или в квартире, снятой на Airbnb, вы знаете, как раздражает, когда вещи не на своих местах. Средство для мытья посуды стоит в буфете, в котором рассчитываешь увидеть кастрюли, а чеснокодавилка вообще непонятно где. Да, всё чистое — нет угрозы, что кто-то отравится едой, приготовленной на этой кухне — но работать в таких условиях раздражает.
А теперь представим деревообрабатывающий цех. В этом месте грязь тоже может вызывать проблемы, но здесь у вас совсем не такое определение «чистоты», как на кухне. Вы можете начищать зубило, пока оно не начнёт сверкать, но вы всё равно вряд ли стали бы резать им сосиски. Так что слово «clean» может быть очень субъективным.
Но для меня слова вроде «tidy» и «organised» работают и таких контекстах, где «clean» не очень хорошо подходит. «Organised» означает, что кто-то как следует подумал, как расположить элементы конкретного рабочего места, а «tidy» означает, что эти элементы действительно находятся на отведённых им местах. Как говорится в старой поговорке, «всему есть своё место и всё на своём месте».
Возможно, в случае с кодом нам стоит думать о словах «clean», «tidy» и «organised» как о трёх разных понятиях. «Clean» означает, что вы смотрите на составные части кодовой базы — методы, функции, интерфейсы — и не видите никаких причин для беспокойства. В именовании придерживаются конвенций; названия переменных и методов написаны без ошибок; в деталях вроде отступов и скобок придерживаются единого стиля; куда ни посмотри, видишь подтверждения того, что на базовом уровне этим заправляют люди, подходящие к делу серьёзно. Это противоположность «грязного кода» — такого, где в названиях куча опечаток, фигурные скобки и отступы захотичны, несоответствующие названия файлов. Это те вещи, которые магически оказываются исправлены, когда вызываешь инструмент «code cleanup» в чём-то вроде ReSharper.
«Organised» — это про архитектуру. Это о том, что можно нырнуть в незнакомую кодовую базу и найти вещи там, где ожидаешь их увидеть. Интерфейсы и доменные границы помогают, а не мешают; методы и переменные названы не просто корректно названы с точки зрения языка, но и отражают единый язык бизнеса, с которым работаешь.
А «tidy» — это про уважение локальных конвенций… кодовая база, в которой можешь найти нужные вещи в соответствующих им местах, даже если конкретная организационная модель вам в этот момент не слишком понятна.
Я думаю, что когда говорят о борьбе за «чистый код», этим обычно подразумевают все три эти вещи. Но ставить целью полностью «чистый код», особенно когда работаешь со сложным легаси-проектом, может быть довольно устрашающей перспективой. Возможно, нам всем было бы полезно задуматься немного глубже и разобраться, что именно из составляющих «чистого кода» по-настоящему принесёт пользу проекту, над которым мы в данный момент работаем».
Heisenbug
Это «конференция по тестированию не только для тестировщиков»: она на стыке тестирования и разработки. Поэтому многие её спикеры понимают специфику обоих этих миров сразу.
Иван Крутов и Анна Чернышева
Иван и Анна работают в разных компаниях, но кое-что их объединяет: оба много знают про Selenium. Мы общались с ними одновременно, так что получилось совместное определение:
Иван: «Для меня самое простое определение чистого кода — это код, который понятен без комментариев, «самодокументирующийся». Код, который завален комментариями, которые пытаются объяснить, что он делает — это не чистый код».
Анна: «У меня похоже: это код, в котором можно быстро разобраться, исправить баг, легко расширить его, дополнить».
Иван: «Ещё можно сказать, что это «код, за который не стыдно». Вообще говорят, что если ты смотришь код, который написал полгода назад, и не ужасаешься, то ты не развиваешься. Получается, что с каждым годом твой код должен становиться всё чище».
Себастиан Дашнер
Себастиан — Lead Java Developer Advocate в IBM, и его часто можно увидеть на Java-конференциях. Но поскольку сейчас он прилетает на Heisenbug, мы спросили его о чистом коде именно в контексте тестирования, и он ответил:
«По моему опыту, качество кода важно не только в случае продакшн-кода, но и для наших тестов. В тестах чистым кодом, правильными абстракциями и делегацией часто пренебрегают, что приводит к такому коду тестов, который не поддержишь. Когда мы рефакторим продакшн-код и меняем его поведение, становится видно, хорошую работу ли мы проделали над моделированием и имплементацией наших тестов».
Андрей Лушников
Андрей работает над инструментом для браузерной автоматизации Playwright, о котором мы недавно писали. Его определение оказалось самым лаконичным:
«Чистый код — это тупой, очень понятный код. И чем тупее, тем лучше».
Александра Сватикова
Александра — эксперт по информационной безопасности в Одноклассниках, которая «начинала в IT как Java-разработчик, но свернула не туда». Её определение оказалось таким:
«Чистый код обладает тремя свойствами: другой разработчик может легко прочесть и понять его, незначительные доработки требуют соразмерных усилий, и эффект от конкретного изменения может быть предсказан.
На деле это означает, что код структурирован, лаконичен, следует общепринятым практикам для того языка на котором написан, не содержит неявных зависимостей или побочных эффектов и покрыт тестами».
HolyJS
Андрей Мелихов
Андрей известен многим по проекту «Девшахта». Неудивительно, что человек, постоянно формулирующий свои мысли в «Девшахта-подкасте», и свою позицию сформулировал чётко:
«Роберт «Дядя» Мартин тремя своими главными книгами («Clean Code», «The Clean Coder» и «Clean Architecture»), как мне кажется, пытается для себя ответить на вопросы: кто, что и как должен писать. Можно поспорить о корректности некоторых его выводов, но вот что, неоспоримо — эти книги построены на богатом личном опыте и здравом смысле. И в рамках этой идеи я могу сказать, что для меня чистый код — это код, который написал бы человек, споткнувшийся о немалое количество подводных камней в своей жизни и в этом болезненном процессе научившийся идти осторожной походкой, позволяющей этих камней избегать.
Вам может быть неудобен стиль этого человека, но если вы будете ему следовать, вы точно останетесь целы. Вы можете взять этот стиль и переработать его, сделать удобным для себя. Или вы можете отчаянно нырнуть в реку набивать собственные шишки, вырабатывать свой собственный стиль, пока остальные будут смотреть на вас с недоумением, двигаясь под присмотром старших товарищей.
Чистый код — это код, который легко читается, легко поддерживается и легко расширяется. Именно эти три аспекта закладывают прочный фундамент под приложениями, которым предстоит жить годами в условиях изменчивости внешних требований. А именно такие приложения мы пишем в крупных компаниях»
Александра Калинина
Александра состоит в программном комитете HolyJS, у неё большой опыт в программировании — и, хотя она не с Heisenbug, с тестами она тоже знакома не понаслышке (unit, integration, E2E, B2B). Вот её текст:
«Clean code — сейчас это простое понятие, но понять его довольно трудно. Мне кажется, что чистый код может получиться при соблюдении следующих правил:
— каждый отдельный кусочек кода должен иметь возможность масштабироваться или расти/улучшаться независимо от других кусочков, но при этом сохранять изначальную идею/интеграцию с другими кусочками (в этом очень помогает SOLID, а также правило «все решения, которые могут быть приняты позже, должны приниматься позже»).
— код должен читаться как увлекательная книга, с понятными типичными названиями и обозначениями. К примеру, если вы задумаете писать рассказ, то у него, вероятнее всего будет типичная структура вроде вступления, завязки, кульминации и развязки. Даже если только вы один работаете на проекте, код должен читаться легко вами же спустя любое время, независимо от выбранной архитектуры, языка, фреймворков.
— код должен иметь понятную структуру. Т.е. должна быть понятна причина, по которой тот или иной кусочек кода находится в проекте.
— каждая строчка кода должна быть обоснована с точки зрения бизнеса»
Nicolò Ribaudo
Николо, будучи ещё студентом, стал одним из ключевых разработчиков компилятора Babel (об этом мы его уже расспрашивали отдельно). Его вариант оказался таким:
«Чистый код — это код, который можно легко разделить на маленькие атомарные составляющие.
«Атом кода» — это наименьший возможный набор инструкций, обладающий самостоятельным смыслом, не зависящие излишне от окружающего контекста: имена переменных и операций достаточно описательные, чтобы читающему их не требовалось выделять в голове дополнительную память для хранения их значений и возможных модификаций, а также не требовалось смотреть ещё куда-то в коде, чтобы понять, что означает результат этого «атома». Чем меньше атомы, тем проще понять, что делает код.
Код может быть чистым вне зависимости от языка программирования или парадигмы: атомы можно реализовать как маленькие объекты, функции, или даже как маленькие фрагменты кода, не изолированные синтаксически».
Заключение
Наконец, когда мнения были собраны, мы показали их самому Дядюшке Бобу и спросили, хочется ли ему что-то сказать. Ответ оказался таким:
«Я полностью поддерживаю комментаторов выше. Я бы добавил только одну вещь, которую когда-то сказал Майкл Фезерс: “Чистый код всегда выглядит так, будто его писал человек, которому не всё равно”».
Его заключение звучит очень дружелюбно. Но чистый код — такая дискуссионная тема, что пока кто-то из вас согласно кивает, кто-то другой наверняка горит, ощущая что-то такое: