Роберт Мартин нехило так повлиял на айти‑индустрию. Он придумал принципы SOLID, о которых спрашивают на собесах, пишут статьи на Хабре и спорят в комментариях. Он написал книгу «Чистый код» и сделал это словосочетание айтишным мемом. Если зайти на хэдхантер, вбить в поиске слове «чистый», выбрать специализацию «Программист, разработчик» и нажать «Найти», получим больше семисот вакансий. Про чистоту кода и архитектуры спорят на код‑ревью, в комментариях и статьях по всему интернету. Разговоров о чистоте внутри айти‑тусовки бывает так много, словно мы находимся в сообществе клинеров, а не программистов.

Мартин называет себя «дядюшкой Бобом». В своих работах он выступает в образе опытного мудрого и взрослого родственника, который несёт свет и знания таким зелёным и неопытным племянникам. И у него отлично получилось втереться в доверие! Типичный хороший программист‑анальник бессилен перед таким добрым дядей. И я знаю, о чём пишу. Восемь лет назад я сам запоем читал книги дядюшки, а потом до усрачки защищал чистоту кода на код‑ревью. Я на себе почувствовал, насколько Роберт Мартин отличный агитатор и пропагандист. Работая с другими людьми, читая статьи и обсуждения на Хабре и хакерньюс, анализирую требования к вакансиям, я понимаю, что не я один попался на отличную пропаганду от «дядюшки Боба».

Книга «Чистый код» вышла 2008 году. Кажется, что за это время десятки тысяч программистов должны были обжечься на советах Роберта Мартина, испоганить ни одну сотню кодовых баз и сделать выводы. Но ничего подобного! «Чистый код» продолжают советовать новичкам, спрашивать о нём на собесах, использовать агитки Мартина в кодовых базах и как аргументы на код‑ревью. Пора с этим кончать. Пора развенчать культ личности «дядюшки Боба». Ведь никакой он не дядюшка. И программист тоже никакой. Сейчас я это докажу. Погнали вместе читать листинги из книги.

У меня российское издание 2016 года. Ниже я переписал листинг 10.6. В книге код на джаве, но я не джавист, поэтому повторил код один‑в-один на тайпскрипте. Это пример чистого кода от Роберта Мартина. С точки зрения Мартина, в этом листинге содержательные имена переменных, а имена функций отлично комментируют код. Шестьдесят строчек чистого кайфа и наслаждения.

class PrimeGenerator {
  private static primes: number[];
  private static multiplesOfPrimeFactors: number[];

  static generate(n: number): number[] {
    this.primes = new Array(n);
    this.multiplesOfPrimeFactors = [];
    this.set2AsFirstPrime();
    this.checkOddNumbersForSubsequentPrimes();
    return this.primes;
  }

  private static set2AsFirstPrime() {
    this.primes[0] = 2;
    this.multiplesOfPrimeFactors.push(2);
  }

  private static checkOddNumbersForSubsequentPrimes() {
    let primeIndex = 1;
    for (let candidate = 3; primeIndex < this.primes.length; candidate += 2) {
      if (this.isPrime(candidate))
        this.primes[primeIndex++] = candidate;
    }
  }

  private static isPrime(candidate: number): boolean {
    if (this.isLeastRelevantMultipleOfNextLargerPrimeFactor(candidate)) {
      this.multiplesOfPrimeFactors.push(candidate);
      return false;
    }
    return this.isNotMultipleOfAnyPreviousPrimeFactor(candidate);
  }

  private static isLeastRelevantMultipleOfNextLargerPrimeFactor(candidate: number) {
    let nextLargerPrimeFactor = this.primes[this.multiplesOfPrimeFactors.length];
    let leastRelevantMultiple = nextLargerPrimeFactor * nextLargerPrimeFactor;
    return candidate === leastRelevantMultiple;
  }

  private static isNotMultipleOfAnyPreviousPrimeFactor(candidate: number) {
    for (let n = 1; n < this.multiplesOfPrimeFactors.length; n++) {
      if (this.isMultipleOfNthPrimeFactor(candidate, n))
        return false;
    }
    return true;
  }

  private static isMultipleOfNthPrimeFactor(candidate: number, n: number) {
   return candidate == this.smallestOddNthMultipleNotLessThanCandidate(candidate, n);
  }

  private static smallestOddNthMultipleNotLessThanCandidate(candidate: number, n: number) {
    let multiple = this.multiplesOfPrimeFactors[n];
    while (multiple < candidate) {
      multiple += 2 * this.primes[n];
    }
    this.multiplesOfPrimeFactors[n] = multiple;
    return multiple;
  }
}

Этот код генерирует список из N простых чисел. Понять алгоритм, по которому он это делает, очень непросто. Если бы я попросил кандидата решить подобную задачу и в ответ получил бы такие шестьдесят строк, то кандидат не прошёл бы собеседование.

Мартин советует давать методам названия длиной в 46 символов. Пока читаешь такое название, можно забыть, что вообще происходит. Вот эта колбаса isLeastRelevantMultipleOfNextLargerPrimeFactor похоже скорее на какой-то прикол, а не на попытки сделать код понятным для других членов команды.

Но даже если сократить эти колбасы до бэйби-сарделек, лучше не станет. Эти названия нехило так вводят в заблуждение. Не знаю, кем нужно быть, чтобы ожидать мутации данных в методе с названием isPrime. Принимаем number на вход, на выходе даём boolean, а ещё заодно данные мутируем, но ты не переживай, всё хорошо. Эта мутация размазана ровным слоем по семи приватным методам, чтобы ты точно не решил разбираться в происходящем. Программист, которому в эту лапшу придётся вносить изменения, охренеет в голове выстраивать дата-флоу, сделает git blame, распечатает листинг на ватмане, вычислит автора по айпи и засунет этот листинг автору в анальное отверстие. Мне сложно представить другое развитие событий.

Нормальный человек решит эту задачу внутри одной функции на 19 строчек кода. Тут не нужны кучи вспомогательных методов с нечитаемыми названиями и скрытыми мутациями. Всего одна функция или статический метод.

function generatePrimes(count: number) {
  const primes: number[] = [];
  let primeCandidate = 2;

  while (primes.length !== count) {
    let candidateIsPrime = true;
    for (let i = 2; i <= Math.sqrt(primeCandidate); i++) {
      if (primeCandidate % i === 0) {
        candidateIsPrime = false;
        break;
      }
    }

    if (candidateIsPrime) primes.push(primeCandidate);
    primeCandidate += 1;
  }

  return primes;
}

Этот код проще, понятнее и его легче поменять. Хотя бы потому что в нём в три раза меньше строк. Чистый ли это код? Если сравнивать с кодом Мартина, то точно не чистый. Но мне плевать! В этом коде явный дата-флоу, явные мутации, он короткий и атомарный. И это главное, этого достаточно.

Давайте глянем ещё на один короткий листинг и на этом закончим. Вот пример теста прямо на джаве. Переписывать его не стал.

@Test
  public void turnOnLoTempAlarmAtThreashold() throws Exception {
    hw.setTemp(WAY_TOO_COLD);
    controller.tic();
    assertTrue(hw.heaterState());
    assertTrue(hw.blowerState());
    assertFalse(hw.coolerState());
    assertFalse(hw.hiTempAlarm());
    assertTrue(hw.loTempAlarm());
  }

Нормальный тест, на мой вкус. Как и в прошлом листинге, у меня здесь вопросы к неймингу. Я бы назвал методы типа isHeaterEnabled, но это вкусовщина. В целом, тест довольно наглядный. Но Роберту этот тест не понравился, и он отрефакторил его вот так:

@Test
  public void turnOnLoTempAlarmAtThreshold() throws Exception {
    wayTooCold();
    assertEquals(“HBchL”, hw.getState());
  }

Он называет функцию wayTooCold более понятной. Но название этой функции ничего не говорит! Что в ней происходит, что она абстрагирует? Функция должна что-то делать, поэтому в названии она, как правило, содержит глагол. Но тут нет никакого глагола, здесь только прилагательное. Не знаю, каким гением нужно быть, чтобы, читая этот код, не проваливаться в объявление wayTooCold. Ненужного абстрагирования Роберту мало, поэтому он решил изобрести свой доменный язык для отображения состояния системы. Большая буква -- значит true, маленькая -- false. Говорит, что в такой записи сложнее допустить ошибку, чем в прямых как палка assertTrue и assertFalse. Подтверждений у Мартина никаких нет. Всё учение о чистом коде строится лишь на вере.

Вообще, Роберт Мартин в своей книге делает очень много заявлений и лозунгов, иллюстрирует их отвратительными примерами кода и не приводит никаких подтверждений. Этим книга "Чистый код" кардинально отличается от "Совершенного кода" Макконнелла. В "Совершенном коде" нет лозунгов, зато есть ссылки на научные работы и комплексный взгляд на разработку ПО. Да, книга Макконнелла старенькая, но если хочется что-то почитать про написание понятного и поддерживаемого кода, то лучше взять в руки именно её, а не пропагандистскую агитку от "дядюшки Боба", которая точно испортит вашу кодовую базу.

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

Комментарии (23)


  1. bugy
    21.01.2025 16:10

    Согласен насчёт совершенного кода. Я сперва прочитал её и на многое открылись глаза

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


  1. NeoNN
    21.01.2025 16:10

    Есть одна интересная книга, называется A philosophy of software design, автор John Ousterhout, и она рассказывает о когнитивной сложности в разработке ПО и методах ее упрощения. И некоторые ее положения весьма далеки от догм энтерпрайзного классоклепания, от чего у кучи народа подгорает и бомбит. Как же так - не клепать классы на каждый чих, или писать комменты на высокоуровневую структуру и неявные связи компонент - ведь мой код написан по солид, а значит непогрешим! Бабах. Но если книженцию почитать и вдуматься, то это тот подход к разработке ПО, который мы потеряли где-то во времена становления "агильности".


    1. Telichkin Автор
      21.01.2025 16:10

      Спасибо за рекомендацию! Уже не первый раз встречаю положительный отзыв на эту книгу. Надо бы почитать


  1. ZhetoN
    21.01.2025 16:10

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


    1. Telichkin Автор
      21.01.2025 16:10

      Учусь маркетингу у лучших представителей айти!


  1. powerman
    21.01.2025 16:10

    Вы упускаете из виду контекст. Лично я никогда не писал на Java и примеры кода в его книжках меня тоже не восхищают. Но я вполне допускаю, что конкретно для Java того времени - этот код может быть заметно лучше среднего. То же касается и SOLID - будучи в целом здравым подходом оно не везде и не всегда полезно на практике (напр. в не ООП языках и "не совсем ООП" языках вроде Go где нет наследования от LSP пользы нет, равно как в небольших компаниях/проектах где один источник требований нет пользы от SRP, DIP стала настолько штатным и привычным механизмом что сегодня нет смысла её как-то особенно выделять, подход к интерфейсам в том же Go делает бессмысленным явное соблюдение ISP - оно само собой соблюдается, …). Но это не отменяет полезности всех частей SOLID в те времена и для тех языков и проектов, на которых SOLID был сформулирован. И тем более не отменяет полезности и адекватности идей, которые продвигает дядя Боб - просто надо понимать суть этих идей и уметь адаптировать их для своего текущего проекта, а не тупо заучивать формальные определения каждой буквы из SOLID и столь же тупо требовать использования их всех в любых проектах.


  1. SamDark
    21.01.2025 16:10

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


    1. Telichkin Автор
      21.01.2025 16:10

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

      Хотя, возможно, это я просто так рационализирую то, что когда-то сам был фанатиком...


      1. SamDark
        21.01.2025 16:10

        Ну это просто подача такая. Есть ещё, например, Бугаенко. Он в том же стиле пишет и тоже нельзя верить на слово (как впрочем и тем, кто пишет в менее уверенном стиле).


  1. mynameco
    21.01.2025 16:10

    мне кажется читать книги нужно не в поисках истины а в поисках себя.

    я когда начинал, читал книги, и много не понимал, потом перечитывал, сверялся и понимал, а потом перечитывал, и был не согласен.


    1. mynameco
      21.01.2025 16:10

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


      1. Jijiki
        21.01.2025 16:10

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


  1. Vitaly_js
    21.01.2025 16:10

    Посмотрел оригинал и все не так страшно как может показаться после прочтения данной статьи.

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

    Данный пример я бы не сказал, что корректно перенесен на ts. Сам я Джаву не знаю, но уже беглого знакомства с исходниками и последующих комментариев Мартина понятно для чего использовался класс. На ts что бы получить все теже самые ништяки и даже выглядейть будет почти как на джаве достаточно использовать замыкания и получится что-то типа:

    ```

    function primeGenerator(n: number) {

      const primes = Array<number>(n)

      const multiplesOfPrimeFactors: number[] = []

      set2AsFirstPrime();

      checkOddNumbersForSubsequentPrimes();

     

      return primes;

      function set2AsFirstPrime() {

        primes[0] = 2;

        multiplesOfPrimeFactors.push(2);

      }

      function checkOddNumbersForSubsequentPrimes() {

        let primeIndex = 1;

        for (let candidate = 3; primeIndex &lt; this.primes.length; candidate += 2) {

          if (this.isPrime(candidate))

            this.primes[primeIndex++] = candidate;

        }

      }

      ...

    }

    ```

    А вот оригинал

    ```

    public class PrimeGenerator {

      private static int[] primes;

      private static ArrayList<Integer> multiplesOfPrimeFactors;

     

      protected static int[] generate(int n) {

        primes = new int[n];

        multiplesOfPrimeFactors = new ArrayList<Integer>();

        set2AsFirstPrime();

        checkOddNumbersForSubsequentPrimes();    

        return primes;

      }

      private static void set2AsFirstPrime() {

        primes[0] = 2;

        multiplesOfPrimeFactors.add(2);

      }

      ...

    }

    ```

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

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

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

    А вот пристрастие пилить на функции в том смысле в котором это сделано сдесь - это дискуссионный вопрос. Даже в данном примере, на мой взгляд, достаточно пробелов до и после кода внутри set2AsFirstPrime вместо вынесения этого в отдельную функцию, комментариев не нужно. Да и в целом заменить `// you comment` на `youComment()` довольно сомнительное решение. Если убрать часть таких одноразовых функций и предварить код комментарием будет, как минимум, не хуже. А просто потому, что одну простыню заменяем на другую. Но одна читается за проход сверху вниз, а другая заставляет скакать глазами в произвольном порядке.


    1. brutfooorcer
      21.01.2025 16:10

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


  1. GospodinKolhoznik
    21.01.2025 16:10

    Что же вы критикуете (не)дядюшку Боба по его книге 2008 года? Не логичнее ли сегодня рассматривать его книгу 2024 года Functional Design: Principles, Patterns, and Practices, в которой он преподносит свои принципы SOLID уже в иной форме, чем раньше. (Да, теперь он топит за функциональщину!) Хотя он сам признаётся, что его новая книга по сути представляет из себя вольное переизложение старой доброй SICP.


    1. Telichkin Автор
      21.01.2025 16:10

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


      1. powerman
        21.01.2025 16:10

        Вы путаете причины со следствиями. Причина происходящего в неумении проводить собесы и делать ревью, а не в дяде Бобе. Если запретить на собесах и ревью упоминать SOLID и чистый код - их место просто займёт какой-то другой карго-культ.


    1. IUIUIUIUIUIUIUI
      21.01.2025 16:10

      Не логичнее ли сегодня рассматривать его книгу 2024 года Functional Design: Principles, Patterns, and Practices, в которой он преподносит свои принципы SOLID уже в иной форме, чем раньше.

      Так она ещё хуже, потому что:

      • Обсуждаемая книга 2008-го года о продакшен-коде написана человеком, который не писал продакшен-код 18 лет, а книга 2024-го года о продакшен-коде написана человеком, который не писал продакшен-код 34 года, и это видно.

        Пик слайтли релейтед
        Пик слайтли релейтед
      • Недядя Боб не понимает функциональщину ещё больше, чем он не понимает ООП.

      Если цель — найти причины не любить Боба, то это отличная книга. Если цель — научиться разрабатывать ПО в функциональной парадигме, то это отвратительная книга, и книги domain modeling made functional или algebra-driven design лучше в <целочисленное переполнение> число раз.


  1. WASD1
    21.01.2025 16:10

    При проверке на простоту достаточно проверить не делимость только на простые числа.
    Учитывая, что список простых чисел у вас уже есть, причём в отсортированном виде - можно и нужно идти по нему.


  1. DasMeister
    21.01.2025 16:10

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

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

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


  1. Jijiki
    21.01.2025 16:10

    а я открыл формулы и гдето итерации с 10 по коду я остановился но тут - решение в целом не правильное ) не хватает точности какогото фактора )


    1. WASD1
      21.01.2025 16:10

      решение в целом же неверное (ну вы же даблы не всерьёз используете, да?).

      primes_count(x) = log(x) - это формула примерная и на неё полагаться нельзя.
      Правильным будет то, что ниже.

      *) Ну и примитивное "решето Эратосфена" же всё равно быстрее, если можете позволить себе память.

      def calc_primes( primes_cnt):
          primes = []
          candidate = 2
      
          while ( len( primes) < primes_cnt):
              candidate_is_prime = True
              sqrt_check = (int)(candidate ** (1/2))
              for i in primes:
                  # +1 - на всякий случай не доверяем округлению флотов
                  if i > sqrt_check + 1:
                      break
                  if candidate % i == 0:
                      candidate_is_prime = False
                      break
      
              if candidate_is_prime:
                  primes.append( candidate)
              candidate += 1
      
          return primes    


      1. Jijiki
        21.01.2025 16:10

        спасибо гляну