×Закрыть

Идеальный код

Мы часто слышим об идеальном (или совершенном) коде. Однако, что это? Кто-нибудь видел его в реальной жизни? Можно ли вообще описать требования к такому коду? Я давно думал о написании пособия, где хотел бы рассмотреть эти вопросы, но только несколько лет опыта преподавания на курсах Java и общения со студентами дали мне необходимый посыл и материалы для создания такой книги. Здесь я буду в основном рассматривать вопросы кодирования, однако аналогично можно рассмотреть идеальный дизайн или архитектуру. Данная статья является более короткой версии моей книги.

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

Правила игры

Я уже предчувствую вопросы об идеальности кода. Разве можно сделать что-то идеальное, тем более в такой постоянно меняющейся среде как ИТ? Кто будет оценивать степень идеальности? Я согласен, что наша индустрия постоянно меняет правила, появляются новые языки и платформы, развивается синтаксис языков. Не меняются лишь разработчики и потребители их продуктов — пользователи.

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

Что отличает совершенный продукт? Качество. Здесь под качеством прежде всего понимают стабильность продукта, количество ошибок и их критичность, а также эффективность продукта (объем потребляемых ресурсов и скорость работы). Под стабильностью понимают отсутствие ошибок и сбоев в течение всего время использования продукта, а также качество поддержки — оперативность исправления критических ошибок и добавления фич, которые нужны пользователям.

Для меня одним из примеров такого качественного продукта является космический аппарат «Вояджер», который с 1977 года бороздит просторы Солнечной системы, работает без сбоев и глюков, усердно передавая информацию на Землю.

Теперь взглянем на программный продукт глазами разработчика. Основные задачи, которые он выполняет в ходе работы над проектом:
— Чтение кода;
— Модификация кода;
— Запуск и отладка.

Чем быстрее он будет выполнять эти задачи, чем меньше будет побочных эффектов, тем быстрее он сможет выпустить очередную версию продукта, тем динамичнее будет развиваться его карьера. Таким образом, если обобщить все показатели, которые приведены в этой главе, то к проекту (а, значит и коду) следует предъявить следующие требования:
— Легко читать и разбирать;
— Легко изменять (исправлять);
— Быстро запускать (и перезапускать) на разных ОС;
— Можно переиспользовать и тестировать;
— Использует бесплатную платформу и библиотеки;
— Минимально допустимое количество ошибок (или полное отсутствие);
— Минимальное потребление ресурсов;
— Стабильность при изменении конфигурации/окружения;
— Безопасность.

Если проанализировать этот список требований, то можно прийти к выводу, что наш идеальный код должен быть:
— Читабельный (readable);
— Очевидный (obvious);
— Компактный (precise);
— Само-объясняющийся (self-describing);
— Современный (modern);
— Гибкий (flexible);
— Расширяемый (extensible);
— Эффективный (effective);
— Масштабируемый (scalable);
— Безопасный или надежный (safe);
— Защищенный (secured);
— Кроссплатформенный (cross-platform);
— Бесплатный (free).

Понятно, что невозможно достичь всех характеристик сразу и возникает вопрос о приоритете. Что важнее? Чего легче достичь? Чем можно пренебречь?

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

Читабельный код

Почему мы начали с читабельности? Разработчики тратят большую часть времени (иногда до 90%) на чтение кода. Учитывая, что разработка — это командная работа, чаще всего они просматривают именно чужой код. Это может быть и код проекта, и код сторонней библиотеки, и код JDK.

Почти каждый разработчик может сказать о коде, читабельный он или нет, только взглянув на него. С моей точки зрения, читабельный код — это код, который можно одинаково легко понять, независимо от того, какой объем этого кода вы будете рассматривать.

Насколько читабельность интуитивна или ее можно достичь, если следовать некоторым правилам и конвенциям? Есть несколько рекомендаций:

  • Сложные методы должны быть короткими, а простые могут быть длинными;
  • Избегайте вложенных циклов и принудительного выхода из цикла, где это возможно;
  • Избегайте вложенных try/catch и анонимных классов;
  • Старайтесь писать каждое выражение на отдельной строке;
  • Избегайте чрезмерного количества методов, вызываемых в цепочке один за одним;
  • Используйте Java конвенции.

Фактически читабельность основана на объеме информации, который должен держать в голове разработчик, чтобы понимать любую строку вашего кода. Тут многое зависит и от опыта, поэтому я всегда советую новичкам читать больше чужого кода. Читать и анализировать.

Возьмем для примера данный код. Он хоть и понятен, но тяжел для восприятия:

private static final long COUNTER = 126684343434343l;
private static final String KEY_DATA = "OURINTERNALKEYVALUE";

Если же мы его немного изменим, то код сразу прибавит в читабельности:

private static final long COUNTER = 126_684_343_434_343l;
private static final String KEY_DATA = "OUR_INTERNAL_KEY_VALUE";

Компактность

С компактностью вроде все просто. Чем меньше кода, тем меньше читать и тем меньше ошибок можно сделать. Но где грань, за которой компактность переходит в нечитабельность и ухудшает гибкость?

С моей точки зрения, компактный код — это код, который нельзя уменьшить в объеме, не навредив остальным характеристикам.

Исходя из моего опыта, я бы разделил все случаи нарушения компактности на три группы:

  • Незначительные (minor) случаи;
  • Дублирование кода или нарушение принципов ООП;
  • Использование языковых конструкции или API в контексте, который не для этого предназначен.

Таким образом, компактность или развернутость кода зависит от знаний и навыков разработчика (как языка программирования, так и стороннего API). Чем больше и глубже разработчик знает API, тем лучше он его может использовать. Это кстати приводит к интересному выводу: делая свой код переиспользуемым, вы помогаете делать его компактным.

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

public static List<String> toList(String item) {
    List<String> items= new ArrayList<>();
    items.add(item);
	
    return items;
}

Хотя более изящным вариантом будет

public static List<String> toList(String item) {
    return Collections.singletonList(item);
}

И напоследок вернемся ко строкам. Я думаю, что вы достаточно часто видели такой код:

public static String replace(String text) {
    return text.replaceAll("a", " ").
                replaceAll("b", " ").
                replaceAll("c", " ");
}

Код достаточно понятный, однако в случае большого числа замен еще и громоздкий. Не все знают, что метод replaceAll принимает первым параметром не просто строку, а еще и регулярное выражение. Поэтому более компактная форма записи:

public static String replace(String text) {
    return text.replaceAll("[abc]", " ");
}

Она еще и работает намного быстрее.

Само-объясняющийся код

Я думаю, что среди читателей это книги есть две большие группы людей. Одни считают, что комментарии в коде не нужны. Другие, наоборот, считают, что комментарии обязательно нужны всегда и везде. Каково мое мнение?

Давайте посмотрим на следующий код:

class Info {
    private String cronExpression = "0 0 0/2 1/1 * ? * ";
}

Что хранит поле cronExpression? Не каждый, даже знакомый с CRON выражениями, может абсолютно точно и уверенно сказать об этом. Сторонники второго подхода выбрали бы такой подход. Мы напишем комментарий, который опишет эту строку.

class Info {

    /**
     * This is expression to run process every two hours
     */
    private String cronExpression = "0 0 0/2 1/1 * ? * ";
}

Теперь стало более понятно. Но что если у нас такие выражения встречаются в других местах проекта? Нам придется в каждом из них писать этот комментарий. Кроме того, если кто-то поменяет значение cronExpression, поменяет ли он комментарий? Все зависит от его порядочности и наличия свободного времени. Не стоит забывать, что написание комментариев требует времени.

Как бы поступил сторонник первого подхода? Он бы добавил константу, на которую можно ссылаться в любом месте проекта:

class Info {
    private static final String 
            TWO_HOURS_DELAY = "0 0 0/2 1/1 * ? * ";

    private String cronExpression = TWO_HOURS_DELAY;
}

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

class Info {
    private static final String 
            TWO_HOURS_DELAY = "0 0 0/2 1/1 * ? * ";

    /**
     * We use two-hour delay as it's maximum time
     * to finish the previous job
     */
    private String cronExpression = TWO_HOURS_DELAY;
}

Заключение

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

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

Совершенный код — не догма и не истина. Он не вечен и меняется вместе с теми правилами, которые диктует отрасль. В этой статье я хотел показать, что такой код — это не столько результат работы гения-программиста, сколько следование определённым требованиям и стандартам ИТ-индустрии.

LinkedIn

Лучшие комментарии пропустить

Я думаю, что во время прочтения этой статьи вам не раз приходила мысль о целесообразности всех этих принципов.
Не могу говорить за всех, но скорее приходил в голову один вопрос: Если это все уже было в книге Макконнелла почти с тем же названием «Code Complete» vs "perfect code"(взял из URL) то для чего повторно излагать все тоже самое ?

Спасибо за статью.
Вот, рекомендую: «Чистый Код» Боб Мартин.
Нектороые подходы пересекаются с подходами в этой статье.
Я применяю много из этой книги на практике, однакого иногда встречаю негодование со стороны коллег. Мол зачем ты тут разбил это «полотно» на несколько компактных методов. Мы к этому «полотну» уже привыкли, знаем что там к чему, а ты типа всё испортил. Или а зачем ты так дробишь код ? Нам большие методы со вложенными циклами более понятны. Читая такой код мы как книгу читаем. Вот первая глава (очередной цикл), а вот глава 1.1. (вложенный цикл)... Ну и в таком духе. А так мы перестаём улавливать общий смысл. В общем было много конфликтов на тему чистоты кода.
Поэтому решения о применении подобных стандартов, должны приниматься на коммандном уровне. То есть разработчики должны ясно осознать что им даст переход на написание «чистого кода», возможно в начале получить какое то поощренине.
Опять же скольких я пытался переубедить, типа, а давай не будем хардкодить в код, куски XML и т.д., на что не получал ни каких ответов, а такого кода становилось всё больше.
Поэтому если на командном уровне не были приняты стандарты, то не стоит и ожидать от коллег что они начнут писать «чистый» код. Они привыкли писать так как они привыкли. У них уже семьи, дети, куча проблем, а ты им со своим «чистым» кодом. Вот если бы пришёл проджект менеджер и сказал, я Вас всех уволю нах#й колоуны, если увижу хоть один вложенный цикл, они бы сразу осознали приемущества написания «чистого» кода ИМХО.... :) Не со всеми так, есть и сознательные товарищи, которым интересно совершенствоваться, но к сожалению не все такие.
Вспомнил картинку с осликом, на которой есть ещё две морковки. Одна перед осликом, а вторая сздади :)

все очень просто
идеальный код — тот который пишу я
говнокод — любой другой код, который достался в наследство и приходится править

Идеальный код — влажные мечты джунов и мидлов

132 комментария

Подписаться на комментарииОтписаться от комментариев Комментарии могут оставлять только пользователи с подтвержденными аккаунтами.

Да что вы все так боитесь вложенных циклов? )))))))))))) Опыта — вагоны, а они боятся!))))

А внутри циклов в случайном порядке должны быть разбросаны goto, break и return.
Но после поллитры и такой код нормально зайдет.

Это как минимум неэффективно с точки зрения дизайна алгоритмов

Давно уже на ДОУ хожу и пишу о том что хватит уже говорить о том каким идеальный код должен быть. Об это можно писать вечно!
Есть уже куча проектов которые заствляют писать «иделаьный» код их надо просто настоить для проекта и говрить больше не нужно, работники всегда следуют этим правилам!
Проекты: checkstyle, pmd, finbugs, sonarqube, intelij idea inspections, .........

У вас новая идея как должен выглядеть код — эти прокты модно легко разширить.
Пользуясь случаем преглашаю людей к вам в проект — checkstyle — github.com/checkstyle/checkstyle
Хватит писать тексты о хорошем коде — помогите это автоматизировать и насладжайтесь автоматической проверкой кода.

Кстати мы в проекте используем все знакомые нам утилиты автоматической проверки кода, и делаем все что бы код был идеальный.

а сам код чекстайла идеальный?
налицо гипер-избыточность комментов которые комментируют очевидные вещи

/**
* Sets the severity level.
*
* @param severity The new severity level
* @see SeverityLevel
*/
public final void setSeverity(SeverityLevel severity) {
this.severity = severity;
}

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

а вот это то зачем

/** Newline pattern. */
private static final Pattern NEWLINE = Pattern.compile("\n");
/** Return pattern. */
private static final Pattern RETURN = Pattern.compile("\r");
/** Tab pattern. */
private static final Pattern TAB = Pattern.compile("\t");

Идеальный это понятие очень субьективное. Все в зависимости от того такой религии придерживаются разработчики проекта (ооп, чистый ооп, функциональный, микс стилей .....) и мода на стиль кода в текущее время.

Мое личное мнение джавадоки нужны только для астратных методов и интерфейсов. Во всех остальных случиях код должен быть понятен и читаться легко.
Но тут есть нюанс .... checkstyle это библиотека которая встаивается во огромное количество приложений, IDEs , .... . Приходится писать доки, их читают. Не многие, но читают и баги еще откывают если их надо обновить, дополнить, .... .

второй момент это что используем 100% то что предлагаем другим. Есть несколько Чеков которые накладывают правила на джавадоки. И мы им следуем. К сожалению эти проверки не идеальные и есть много проблем которые еще не решены. Как только найдем время их исправить можно будет разслабить эти правила. Поможешь автоматизировать обнаружуние «очевидного кода» ?

а вот это то зачем

у тебя есть идея как сделать лучше ? присылай Пул Реквест :) .

---------------------------

Кода у нас много , команда маленькая , делаем что можем в свободное врея.
Мы используем почти все известные статичиские анализаторы — есть некая притензия на идеальность в каком то общем понимании ( среди фанитов статического анализа :) ).
Но улучшать всегда есть где , еще не все автоматизировано, идей масса а рук не хватает.

Если сделаешь нам код ревью :) , так мы большое спасибо скажем.
А лучше присодининяйся к проекту и атоматизируй поиск плохого кода.

Є невелике зауваження до назви цього методу.

public static List<String> toList(String item) {
    return Collections.singletonList(item);
}
Я б назвав його toImmutableList(String item). Collections.singletonList() створює immutable list з одним елементом, спроба модифікації якого призведе до RuntimeException. А це не очевидно з назви методу toList(String item).

все очень просто
идеальный код — тот который пишу я
говнокод — любой другой код, который достался в наследство и приходится править

Книжка для всіх програмістів. Стив Макконнелл Совершенный код.

www.ozon.ru/...ontext/detail/id/5508646

Прочитайте її всією командою, прийміть певні стандарти і пишіть хороший код.

і пишіть хороший код.
Который вне зависимости от твоего желания будет обрастать костылями и превращаться в говнокод.

А code review зачем? У нас был случай, когда merge request не зашел с 6-ой попытки из-за качества написанного кода. И разработчик был уволен.

А code review зачем?
Потому как это карго-культ и не более. Всё чем модет помочь кодревью, это немного замедлить обрастание костылями непосредственно в коде, но кодревью ничем не поможет с точки зрения архитектуры проекта, которая тоже будет обрастать костылями.

Это обсуждение может вылиться в полемику, поэтому скажу просто, что я вижу выхлоп от code review.

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

Если при изменении требований в проекте приходится городить костыли в архитектуру, то тут уже стоит пересмотреть саму архитектуру. Значит, она не достаточно гибкая, чтобы реагировать на изменяющиеся требования бизнеса.

А время сколько уходит на это? Большинство заказчиков не готовы на такие временные затраты.

Выходит, там повезло з заказчиком :)

На самом деле, на разработку архитектурного решения уходит в районе 2х часов на каждого из программистов, которые участвуют в обсуждении.

Допустим, если это проект, который будет обслуживать нужны других проектов (микро-сервисы), в обсуждении участвует 2-3 программиста уровня Senior + 2 программиста уровня Middle.

Итого:

  • 6 часов Senior разработчика
  • 4 часа Middle разработчика

Мне кажется, что если проект обрастает костылями несмотря на code review и все старания менеджмента, то это говорит либо о низкой квалификации либо мотивации команды.

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

А как же рефакторинг ?

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

Вы же сначала говорили про костыли. Откуда вдруг взялся гавнокод ?

Viktor Chyzhdzenka я бачу 2 типи костилів.
1. Не можливо реалізувати по іншому так як не має достатньо часу. Цей випадок виправляється просто. Знайдіть час і виправте якщо потрібно. Якщо не потрібно тоді який сенс читати статті про хороший код ;)

2. Не можливо реалізувати по іншому так як не дозволяє архітектура.
Тут все складніше, виправлення потребує великих затрат: час програмістів. Хай сенйори оцінюють вигоду від рефакторінгу і вперед.
Якщо у вас замовники не бажають надавати час на фікси таких костилів, значит продукт не має довготривалої підтримки програмістів або заказчик не вміє розпоряджатися своїми коштами.
Ви ж прекрасно розумієте, якщо ваш код складається з костелів і не має хорошої архітектури тоді у вас на кожній наступній реалізації фічі або ламається продукт або ви її будете реалізовувати у 5 разів довше ніж потрібно.

Я також за принцип працює не рухай. Але якщо в код треба вносити костиль тоді краще написати тести і змінити архітектуру.

П.С. ці всі статті про хороший і правильний код проявляються на світ що б показати одну просту істину: краще економити час при попередніх ітераціях у розробці коду. Тобто економити кошти заказчиків і енергію програмістів.

Иван, спасибо за коментарий. Вы написали, исправьте если нужно. Но как исправить? Откуда джуниор или интерн будет знать, как правильно исправить?

Якщо джуніор вносить костиль, скоріш за все він не знає що це костиль. Він зможе це зрозуміти після певного періоду, або якщо хтось підкаже. Також джуніор може прочитати відповідну літературу і перейняти досвід більш досвідчених кодерів ;)

Саме тому для високоякісних продуктів потрібно наймати кваліфікованих спеціалістів.

Но как он поймет, что “більш досвідчених кодері” тоже не вставили костыль или гавнокод?

Тільки з свого досвіду. Якщо джуніор вставив для прикладу 30 іфів в один метод, а через пів року він потрапив багато часу на виправлення і доповнення цього методу. Тоді в голові загорається лампочка: можливо я щось роблю не так. І починаються пошуки більш досконалих рішень. Відповідно цей урок джун засвоїв і він знає чому так не можна писати.

Тобто тільки свій досвід, спостереження і інформація від старших по команді.

30 іфів в один метод
Это просто смешно. Такое можно даже говнокодом не считать, обычный switch и не более, если он влазит на 1-2 страницу экрана.
А вот если каждый иф из себе через несколько лет станет представлять страницу кода, то уже и говнокод получиться.

Лично у меня начинают немного дергаться пальцы на ноге, когда я вижу метод больше 1 экрана, не говоря о switch на экран.

Мы для таких случаев используем map of callables, но это в PHP.

У кожнїй спільноті/команді свої стандарти якості коду.

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

Якщо код хороший то через рік все що можна змінити так це використання нового арі у мові або арі оновлених бібліотек. А костилів тут ні до чого.

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

Но ведущие разработчики должны построить систему таким образом, чтобы добавление в нее новых компонентов не вызывало зуд в одном известном месте.
Они уже стали богами? А не «тыжпрограммистами».

Я не считаю себя богом, но за 2,5 года после запуска системы в production, не было воткнуто ни одного костыля. Ибо в начале для взаимодействия между компонентами договорились использовать Inversion of Control + Chain of Responsibility.

Сейчас система работает полностью в автоматическом режиме без участия человека. Конечно, не считая крайне исключительный ситуаций.

Нужно для определенного поставщика в определенном импорте добавить пост-процессинг датапака? Ок, пожалуйста, вот вам новый механизм Logic Hooks + LogicHookDispatcher.

Нужно достать CSV из ZIP, который закодированный в base64 внутри XML-ответе от SOAP?
Пожалуйста — контент процессор на транспорт, компонент для работы с архивами или целыми каталогами, выкачанными в FTP.

Как говорил один мною нелюбимый преподаватель — нужно зреть в корень в самом начале.

Так и хочется сказать: «Эх, молодые, ничего вы еще в жизни толком не видели».

Почему ж не видели? Я видел откостыляный SugarCRM, который пришлось тягать 3 года. Я видел и поддерживаю и рефакторю проект, которому уже лет 10.

Не можливо передбачити всі нюанси. Але при плануванні архітектури ви можете прикинути які класи/алгоритми можуть змінитись. І взагалі планування створене для того що б робити хорошу архітектуру яка буде легка для змін, швидка і стійка для помилок.

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

Я не считаю себя богом, но за 2,5 года после запуска системы в production, не было воткнуто ни одного костыля.

Все що ми повинні записано у трудовому договорі. Прийшов на роботу, хочеш пиши гавнокод і закривай задачі а хочеш думай як написати хороший код. Кожен сам для себе рішає що він повинен. Ви колись замислювались як написати код який у майбутньому можна буде розширити без змін?

Мой код уже лет 20 в некоторых местах работает до сих пор.
А спорю я с тобой потому, что пытаюсь объяснить, что жизнь очень часто выходит за рамки твоих представлений о ней и в части написания кода в том числе.

Суть немного не в том, что архитектурный подход был угадан верно.

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

Мы же старались донести необходимость разработки 1-2 сопутствующих компонента поддержки, чтобы потом, если снова будет необходимость навесить дополнительную логику, это решится простым JSON-конфигом и конкретной реализацией компонента с логикой. А само приложение уже будет готово, и дополнительных затрат не будет.

Клиенту профит — система становится гибче. Разрабу повышает ЧСВ — не слепили из говна и палок, а гибкое архитектурное решение.

Мы же старались донести необходимость разработки 1-2 сопутствующих компонента поддержки, чтобы потом, если снова будет необходимость навесить дополнительную логику, это решится простым JSON-конфигом и конкретной реализацией компонента с логикой.
Ты не заметил насколько узка задача?
Но пройдет некоторое время и появиться потребность в логике, что не уложится в эти рамки. А потом еще и еще и еще ...

В чём проблема? Эта логика реализуется рядом. Если возможно — унифицируется.
Если приложение состоит из мелких юнитов у которых только одна причина для изменеия и зависимости которых инвертированы — проблемы не вижу.

А можно интегрировать Groovy в проект и не парится вокруг 90% костылей, оберток и прочих «улучшителей» вокруг Java, из-за того что язык изначально вышел «невдалый» и без костылей и подпорок ничего не умеет.

Какой язык без костылей и подпорок ничего не умеет?

Да просто смешно же. Java можно ругать за много boilerplate кода (с этим очень активно борятся) и, как некоторым кажется, излишнюю explicit-ность конструкций (как по мне так это иногда преимущество).
А вы какую Java имеете в виду? Может просто речь о древних версиях ещё до пятёрки? В восьмёрке много чего очень лаконично.
Да в любом случае язык — это только инструмент. Можно или нельзя сделать что-то

без костылей и подпорок
зависит скорее от людей что этим инструментом пользуются

Не считайте придиркой, но пример с singletonList как оптимизации читабельности, имхо плох, потому что этот код не эквиалентен первому методу:

singletonList(T o)
Returns an immutable list containing only the specified object.
Immutable. Если этот лист будет использоваться где-то, кроме простых тест-кейсов с одним элементом, то UnsupportedOperationException почти гарантирован, и мы все равно вернемся к первому варианту.

Абсолютно согласен. Но часто у вас бывает такое, что вы создаете и заполняете список, потом передаете его не в ваш код, чтобы этот код его еще раз дополнил ?

Идеальный код — это код за который заплатили. Говнокод — код, который не покупают. Все остальное — ложь и провокация.

А как назвать код, за который заплатили, но который потом невозможно поддерживать ?

У меня есть для вас ответ — это legacy, который может поддерживать только один программист-старожил, который явно не останется без работы :)

Я так понимаю, вы и есть тот программист-старожил ? :)

Нет, я как раз «новая кровь» на проекте :) Борюсь с говнокодом, godlike-классами, singleton’ами и ветряными мельницами

любой код, в котором больше одной операции — говно

Спасибо за статью.
Вот, рекомендую: «Чистый Код» Боб Мартин.
Нектороые подходы пересекаются с подходами в этой статье.
Я применяю много из этой книги на практике, однакого иногда встречаю негодование со стороны коллег. Мол зачем ты тут разбил это «полотно» на несколько компактных методов. Мы к этому «полотну» уже привыкли, знаем что там к чему, а ты типа всё испортил. Или а зачем ты так дробишь код ? Нам большие методы со вложенными циклами более понятны. Читая такой код мы как книгу читаем. Вот первая глава (очередной цикл), а вот глава 1.1. (вложенный цикл)... Ну и в таком духе. А так мы перестаём улавливать общий смысл. В общем было много конфликтов на тему чистоты кода.
Поэтому решения о применении подобных стандартов, должны приниматься на коммандном уровне. То есть разработчики должны ясно осознать что им даст переход на написание «чистого кода», возможно в начале получить какое то поощренине.
Опять же скольких я пытался переубедить, типа, а давай не будем хардкодить в код, куски XML и т.д., на что не получал ни каких ответов, а такого кода становилось всё больше.
Поэтому если на командном уровне не были приняты стандарты, то не стоит и ожидать от коллег что они начнут писать «чистый» код. Они привыкли писать так как они привыкли. У них уже семьи, дети, куча проблем, а ты им со своим «чистым» кодом. Вот если бы пришёл проджект менеджер и сказал, я Вас всех уволю нах#й колоуны, если увижу хоть один вложенный цикл, они бы сразу осознали приемущества написания «чистого» кода ИМХО.... :) Не со всеми так, есть и сознательные товарищи, которым интересно совершенствоваться, но к сожалению не все такие.
Вспомнил картинку с осликом, на которой есть ещё две морковки. Одна перед осликом, а вторая сздади :)

Боб Мартин.
По ссылке Роберт Мартин, Мика Мартин

Роберт Сесил Мартин (англ. Robert Cecil Martin), также известный как Дядя Боб (англ. Uncle Bob[1]) . В общем те кто в теме — те поняли :) Но формально да — Роберт Сесил Мартин...

Bob — сокращение от Robert. Или Body Opponent Bag.

Ростислав, я с вами согласен. Идеальный код — это результат работы всей команды, а не одного человека.

Раз люди не хотят следовать рекомендациям индустрии, оправдывая это тем что «так привычней», то команда в которой вы работаете — низкопрофессиональная. Есть стандарты индустрии, лучшие практики, описание антипаттернов, понятие технического долга. И на проекте для управления всем этим есть лиды, код ревью. Продакт менеджер в код не лезет.

то команда в которой вы работаете
Работал :)
Есть стандарты индустрии, лучшие практики, описание антипаттернов, понятие технического долга. И на проекте для управления всем этим есть лиды, код ревью.
Именно, +1. Не зря же все мы наступаем на тысячи граблей, а те у кого есть возможность и желение, стандартизируюет этот отпыт и кристаллизуют в виде best practices. Что бы другие на повторяли уже осознанные ошибки.
Продакт менеджер в код не лезет.
Да, он то не лезет. Но если ему явно говорят, что дружище, у тебя на проекте творяться страшные вещи, люди не хотят писать нормальный код, то как бы тут есть и его ответственность.

стандарты важнее на самом деле, хуже кода в «разнобой» ничего нет, даже если часть хорошо написано, как ни странно но если принять писать говнокод — то лучше всем его писать, за исключением когда принимается всей командой его искоренить, тогда как транзишенл стейт может быть разнобой

Да, у меня какраз стал выбор, начать писать «говнокод», или свентить с проекта. Я честно пытался больше года там что-то изменить.
Отклонился от стандарта под кодовым названием «говнокод» на том проекте, что бы показать ребятам, что возможно писать качественный код, за котрый тебе не стыдно, а наоборот, который тебя вдохнавляет и мотивирует совершенствоваться дальше, развиваться. К сожалению, у них там был конфликт и обида на руководство, на менеджмент, и большая часть комманды находилась в подавленном состоянии, поэтому в такой атмосфере сложно как-то кого-то вдохновить.

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

Clean Code для меня одна из книг, наверное основная которая дала понимание что такое порядок и чистота в коде. Наверно это соме главно, все по полочкам, все по ящикам, одно к одному, другое к другому, как в жизни, если на столе бардак и в жизни ты не опряшный все в куче так и в коде будет. Ментальность

Clean Code для меня одна из книг, наверное основная которая дала понимание что такое порядок и чистота в коде.
Да, у меня та же история. До этой книги качество моего кодирования развивалось имерически: наступил на грабли, осознал, больше так не делаешь.
А тут такой подарок — книга в которой автор смог проанализировать опыт многих людей. И на базе этого построить некую понятную систему принципов создания качественного кода, за что ему огромное спасибо.
если на столе бардак и в жизни ты не опряшный все в куче так и в коде будет.
Ну тут не всегда так. Вопрос осознаёшь ли ты беспорядок на рабочем столе. Если это осознанно и ты понимаешь, что у тебя есть сейчас более важные дела, а уборка пока может потерпеть, то это одно. Но если это хроника — то да. Как правило это переносится на все сферы жизни ИМХО...

Рабочий стол Инштейна

согласен. Но то что ты переносиш свой характер и дисциплину на код это точно. Человек который по сути слаживает вещи по полочкам убирает постоянно по дому и ненавидит грязь, не сможет мириться с грязью в коде, которая становится причиной заболеваний иногда и фатальных для программы если запустить. )

Проекты часто просто умирают от того что грязные жирные пердуны содятся кодить, с пончиком который крошиться на клавиатуру))))

к Чистому коду отношусь неоднозначно, хотя в целом положительно . С одной стороны Дядя Боб правильные вещи говорит, но с другой не нужно принимать это как догму(религию).

Полностью поддерживаю. Дядя Боб даёт хорошие закономерности, подходы, эвристики. Но его умозаключения, тоже не идеальны, как и всё в это мире. Поэтому ИМХО надо смотреть по ситуации. Включать здравый смысл. Ещё многое зависит от команды в которой это всё будет применяться. Есть такие товарищи, которые говорят — а мы не понимаем когда код структурирован по методам, нам лучше когда одно громадное полотно. Мы так привыкли. А ты нам тут свой «чистый код» пропихиваешь...

Слово «идеальный» ИМХО неправильное. Еще фон Нейман предсказал чегой-то типа что процесс совершенствования кода бесконечен: всегда будет соблазн переписать тщательно вылизанный алгоритм с учетом самых последних достижений науки и техники. Я бы в таком случае говорил скорее о стандартизации кода, и аргументация в пользу такого подхода ИМХО слехка иная, чем в пользу стремления к идеалу. Хотя бы потому, что идеал — понятие субъективное, а говоря о стандарте легче прийти к компромиссу, который устроит ± всех в команде.
А вот с порядком аргументов согласен 100%. Еще один великий, на плечах которого мы сидим и кодим :), Гарольд Абельсон, замечательно отметил: «Programs must be written for people to read, and only incidentally for machines to execute» :8)

Отказаться от вложенных циклов? Ни за что!

А так же inline и однострочных if

Если не секрет, какая у вас максимальная глубина вложенности циклов ?

Часто у меня два уровня. Три не припомню даже для практических задач.

Был проектик just for fun, мне там надо было сгенерировать все возможные положения на поле для игры в крестики-нолики. 7 уровней.

Я хоть никак не отношусь к вселенной и экосистеме Java, но вставлю свои «пять копеек».

Я считаю, что примеры на тему написания идеального (чистого) кода можно почерпнуть из книги Роберта Мартина «Чистый код». В ней представлены более реальные примеры кода до рефакторинга и его результат. Более того, вторая глава книги представляет из себя упражнения по рефакторингу. Свой результат в конце можно сравнить в результатами автора.

Вообще мера идеальности кода немного эмпирическая: кого-то вполне устраивает метод больше 1 скрина с парой тройкой циклов внутри; другой же будет идти строго по SOLID, подбирать подходящие паттерны для решения задачи.

Но это все, опять таки, эмпирически и зависит от программиста. Но не врут только цифры. Поэтому, оценка кода по метрикам — вот более-менее адекватный способ понимать, что написано «по-православному», а где программист выполнил задачу спустя рукава. Индекс цикломатической сложности, CRAP индекс — это давно не пустой звук.

Евгений, спасибо за ваше мнение. Я ни в коем случае не претендую на лавры Роберта Мартина. Я лишь попытался обобщить свой опыт как разработчика и как преподавателя

Я вовсе не имел ввиду, что вы претендуете на лавры Мартина.

Я о том, что кроме визуальной «причёсаности» кода еще есть и вполне фиксированные показатели его качества. И, мне кажется, что объяснения некоторых метрик оценки кода были бы куда интереснее. Даже начинающему разработчику, который привыкает оперировать цифровыми данными, было бы неплохо понимать, какие действия приведут к улучшению метрик.

А то сейчас оценка результат рефакторинга сводится к "о, смотри, а этот кружок уже не большой и красный, а стал средним и позеленел. Значит норм переписали."ю

А какие метрики вы использовали в своих проектах?

Я вещаю немного из своей колокольни, так как ни разу не писал на Java. Но уже больше полугода мы используем PHPMetrics (www.phpmetrics.org). Очень полезный инструмент — www.phpmetrics.org/report/latest/index.html

Для анализа в динамике — SonarQube (www.sonarqube.org). Вам, как Java программисту он уже точно ближе по духу.

Для опенсорс проектів використовую scrutinizer-ci.com. Рекомендую всім. ;)

Согласен — городить огород для opensource нету смысла, и Scrutinizer очень крут.

Но у нас отдельный закрытый Gitlab + сервер, где строятся отчеты по метрикам в каждом проекте, генерируется документация, прогоняется CodeSniffer и строит несколько отчетов.

прогоняется CodeSniffer
зачем, если не секрет? Можно же хук на коммит повесить. Если следование стандарту ставится во главу угла и есть люди что прорабатывают эти отчёты — вполне себе решение. Есть так же CSFixer — если не стрёмно. Ну и в обязательном порядке у всех кодсниффер должен быть подключён в IDE. Замечаю что назойливые варнинги форсят писать код по стандарту, принятому в команде, гораздо сильнее чем личное напоминание что то-то пишется так-то, а здесь пробел забыл.

CodeSniffer прикручен к IDE, а у тех, что пишет в Sublime или Vim ruleset.xml прицеплен через плагины тоже.

Хук на коммит не представляется возможным из-за legacy проектов, где небольшой апдейт, на который выделили 1 час, выльется в причесывание всего юнита в соответствии со стандартом. Если получается, выбиваем у клиента время на такое причесывание, но с хотфиксами так не получается. Поэтому, если навесить pre-commit hook — никто не закомиттит :)

CSFixer или CodeBeautifier стремно использовать в автоматическом режиме — был случай, когда после него пришлось очень срочно ночью хотфиксить. Проблема была в том, что очень нерадивый разработчик когда-то решил сэкономить на фигурных скобках и сделал однострочный if с вложенными однострочными if..else.

В результате из кода

if ($preCondition)
    if ($condition)
        do_stuff();
    else
        do_other_stuff();

Был сделан код

if ($preCondition) {
    if ($condition) {
        do_stuff();
    }
} else {
    do_other_stuff();
}

И вся эта херь была запрятана в 400 строках причесанного кода.

И CSFixer не напишет комментарии за разработчика ;)

О да из лаконичного кода сделали говнокод с кучей скобочек. Что сказать, круто.

Мне кажется, вы упустили момент, что логика теперь неправильна.

Был сделан код
Это как раз результат работы CodeBeautifier.
Проблема была в том, что очень нерадивый разработчик когда-то решил сэкономить на фигурных скобках и сделал однострочный if с вложенными однострочными if..else.

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

Откуда тут баги?
Причем скобки усложняют чтение этого кода и ведут к большей вероятности бага (разве что полагаться на подстветку от IDE). Просто чем меньше буковок написано, тем меньше ошибок будет (процент ошибок на количество буковок обычно не сильно колеблется).

Еще скажите, что вас не вводят в ступор тернарники тройной вложенности.

Нет, если больше уже хреново. Обычно есть два красивых числа 3 и 7. Вот вложенность до 3 вполне читабельна. А вот количество методов у классов желательно ограничивать 7 или вообще любых интерфейсов (делать иерархию).
Избыток скобочек тоже напрягает (но в тех же плюсах от них никуда не деться).
А вообще в примере выше в плюсах я бы вообще тернарный оператор сделал — читать легче. ()?foo:bar.

Не соглашусь.
В любом проекте у вас code-style документ, в котором будет написано, как ставить скобки. Даже в случае с однострочными if-else’ами лучше придерживаться консистентности в написании скобок, тогда это не убавит читаемости, скорее наоборот. Зато потом не придётся искать глупые баги типа else’a, принадлежащего не тому if’у.

По-первому, это не так. Аво многих случаях на эти бумажки просто забивают. По итогу, я пришел к выводу, что для тех же плюсов в документе должно быть по сути 5 срок:
1. Максимально избегать использования голых указателей.
2. Максимально избегать goto.
3. Максимально избегать функций более 3 екранов.
4. Максимально избегать вложенности больше 3.
5. Максимально избегать интерфейсов с более, чем 7 методов (геттер/сеттер за один считается).
Ну а по-второму, это уже вкусовщина и на баги в общем-то не влияет.

А во многих случаях на эти бумажки просто забивают.

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

1. Максимально избегать использования голых указателей.

То есть все указатели должны быть завернуты в «умную» оболочку типа unique_ptr или shared_ptr, я правильно понял?

Ну еще vector и т.д. В общем почти всее. Только в очень очень редких случаях использовать голые.

Ще простіше закинути правила від phpstorm у проект і сказати новому джуну: поглянь на історію комітів цього файлу. І ти побачиш чому і як ми так вирішили ;)

Саме головне що б у команді були хоч якісь мінімальні стандарти))

Працював із csfixer. Навіть старався контрибютити у цю лібу. Все норм але немає ревю. Тобто тулза не напише коментар автоматом. А от у code sniffer інша проблема. Немає фіксера.

Зараз займаюсь написанням ліби яка вирішить ці дві проблеми. Ревюю і фікс. Якщо фікс девелопер не робить, на сі сервері все рівно запускається ревю. Лібав опенсорсі — кому цікаво пишіть ;)

А от у code sniffer інша проблема. Немає фіксера

Извините, но вы неправы:

MacBook-Pro-Ievgenii:lib r3ntg3n$ phpcs --standard=../ruleset.xml Application/Assets.php --report=summary
E


PHP CODE SNIFFER REPORT SUMMARY
------------------------------------------------------------------------
FILE                                                   ERRORS  WARNINGS
------------------------------------------------------------------------
/Volumes/Data/Work/bInTime/***/lib/Application/Assets.php   13         0
------------------------------------------------------------------------
A TOTAL OF 13 ERRORS AND 0 WARNINGS WERE FOUND IN 1 FILE
------------------------------------------------------------------------
PHPCBF CAN FIX 8 OF THESE SNIFF VIOLATIONS AUTOMATICALLY
------------------------------------------------------------------------

Time: 158ms; Memory: 7.75Mb
Обратите внимание на последнюю строку отчета: PHPCBF это и есть встроенный фиксер, он же Code Beautifier.

Но о его поведении в некоторых нетривиальных ситуациях я уже писал.

Треба буде покласти ще раз code sniffer. Не можу знайти фіксера для UnnecessaryStringConcatSniff

Да, не пишет и да, иногда делает странное, но в основной части его работы всё ок.
Что главное — он ещё и не коммитит (с чего бы ему вдруг? Система контроля версий — совершенно другая система). Потому можно после его автофикса просмотреть в красивой диффалке типа как у IDEA, или во время отправки пулл-реквеста на стэше/гитлабе/етц.

Тут важная фраза

Потому можно после его автофикса просмотреть в красивой диффалке

Но если отформатили legacy-класс на 600+ строк, да еще если ревьювили под конец недели, то упустить такой момент — это как 2 пальца об асфальт.

Да, все люди — все могут что-то пропустить или не заметить. Можно просто не делать этот фикс стилей

под конец недели
можно дать на посмотреть многим разработчикам. В мечтах этот код вообще покрыт тестами. Просто делать всё чтоб уменьшить вероятность
упустить такой момент
Но фиксить надо — не возможно же с таким работать. Открываешь в IDE — в глазах рябить начинает.

Давно известно, что идеальный код тот, которого нет. Любой же код — по определению, будет говнокодом (в чьих-то глазах).

static методы явно не признак идеального кода. И зачем называть переменную cronExpression?

Игорь, спасибо за ваш комментарий. Я так понял, вы вообще не используете статические методы/поля в своем коде?

static методы явно не признак идеального кода.

Конечно, и внедрили в язык этот модификатор враги?

Статические методы имеют свою нишу для использования. Как например можно реализовать шаблон Factory без статических методов?

Instance-методом например? Тогда фабрику можно ещё и как зависимость внедрить будет.

Да, можно. Но тогда придется каждый раз создавать объект для вызова одного метода. Не очень эффективно.

На первый взгляд, ваши примеры на шаблон Фабрика не похожи. Также есть такое, например, мнение www.yegor256.com/...e-to-utility-classes.html

Из текста статьи — toList и replace

Зачем

каждый раз создавать объект для вызова одного метода
?
Один инстанс на весь цикл приложения. Создавать на старте в синглтон скоупе (не путать с синглтон антипаттерном). И инжектить этот инстанс куда нужно. При том это место куда нужно должно зависеть от интерфейса такой фабрики, а не реализации.
Кстати, прямая инстанциация не решает те проблемы (например покрытия изолированного юнита тестами), которые возникают и при использовании статики. Это всё ещё жёсткая зависимость что повышает coupling

А как это вы сделаете(singleton scope), если у вас нет DI контейнера ?

Зачем так жить

если у вас нет DI контейнера ?
?

Но тогда у вас есть обьект аппликейшн, что живёт со старта до стопа сервиса и вот в нём DI-контейнер руками и реализовывается.

Идеальный код — влажные мечты джунов и мидлов

Не сочтите за придирки, сам о идеальном коде радею.

0. Все примеры — статические методы. Хорошо ли это?
1. Раз мы узнали (действительно есть такая проблема?) про Collections.singletonList, то может сразу его использовать, без прослойки toList?
2. Понимаю, что пример с replace условный, но название метода не говорящее само-объясняющее.
3. cronExpression точно мутабельное поле? Если нет, почему бы просто не использовать упомяную константу? Если вдруг да, опять же, достаточно ли говорящее название переменной?

Всегда немного удивляют длинные рассуждения по поводу «чистого_идеального_супер_мега_крутого» кода.

Если мы говорим про Java, то все рассуждения по поводу хорошего кода должны заканчиваться Java Code Convention, уверен, что в других языках тоже есть что-то подобное.
Вещи, касающиеся знания «финтов ушами» в Java, в любом случае, приходят только с опытом.

Ну а если человек именует переменные по типу:
String str1;
Integer temp;
Double number;

То здесь уже никакие книги и расуждения не помогут — это диагноз :)

Идеальный код существует только в голове программиста

Немного позанудствую.
...
Сперва, объявляем задачу по захвату вселенной.

Давайте взглянем на разработку продукта с точки зрения всех задействованных участников. Главная мысль — совершенный код является частью совершенного продукта.
А потом, просто обсуждаем код с точки зрения художественного произведения (текста).
На первом месте оказалась группа, содержащая читабельность, компактность и само-объясняющийся код

Ключевым для любого продукта ПО есть внешние факторы качества: #correctness, #robustness, #extensibility, #reusability и т.д. Именно это и покупают заказчики. Идеальный текст в вакууме, нужен только идеальному разработчику в вакууме.
...

Разработчики тратят большую часть времени (иногда до 90%) на чтение кода.
Тут мне кажется нужно бороться не с тем чтобы было «удобно» читать (хотя это важно бесспорно). Нужно итеративно уменьшать потребность снова и снова читать код, код должен работать, TDD — как первый шаг.

...

Компактность
Как по мне, все примеры больше похожи на синтаксический сахар. Ну уверен про Java, но в мире С++, после компиляции и оптимизации, во всех случаях останется «пара ассемблерных команд». Преждевременную оптимизацию ещё никто не отменял.

In my simple world, Компактность === ортогоальный интерфейс.
Как пример наброса на вентилятор: CreateProcess vs fork.
...

Само-объясняющийся код
Пример откровенно слабоват. Нужны ли именованные константы? — Да, нужны. Где что-то про само-объясняющийся код в сложной иерархии классов, например, для объектов финансового домена optionOnFuturesOnWeatherSwap )

items.add("1");=>items.add(item);

Спасибо, заменил в статье.

Я пишу код которым пользуюсь только я , я постоянно оставляю себе javaDoc почти ко всем методам , и ниразу об этом не пожалел.

Я думаю, что во время прочтения этой статьи вам не раз приходила мысль о целесообразности всех этих принципов.
Не могу говорить за всех, но скорее приходил в голову один вопрос: Если это все уже было в книге Макконнелла почти с тем же названием «Code Complete» vs "perfect code"(взял из URL) то для чего повторно излагать все тоже самое ?

А для чего вы пишете код, который, я уверен, уже хотя бы один программист написал и не раз?

Скорее не для чего а почему? — Потому что не нашёл удоволетворительной реализации текущей задачи (а зачастую вообще никакой реализации не нашёл)

Не все читают книги :)
Но ссылка на перворесурс не помешала бы :)

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

Подписаться на комментарии