Роберт Мартин нехило так повлиял на айти‑индустрию. Он придумал принципы 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. Подтверждений у Мартина никаких нет. Всё учение о чистом коде строится лишь на вере.

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

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

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


  1. bugy
    21.01.2025 16:10

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

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


    1. Arioch
      21.01.2025 16:10

      именно потому, что спорные?

      как можно "фанатеть" от утверждения, что "2+2=4" или "2+2<>4" ? Проверил, принял к сведению. Фанатеть же - с любым знаком - как раз можно только от "истина где-то рядом"


  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. ToyJoy
      21.01.2025 16:10

      Звучит как название какого-то аниме :Д


      1. Arioch
        21.01.2025 16:10

        ...какого-то недавнего аниме.

        Если взять аниме, которым 10-20-30 лет, то там вроде такой фигни не было.

        Скорее это звучит, как заголовок главы из развлекательных романов 19-го века.

        Глава 123-я

        ...в которой Робинзон узнаёт...

        ...и дальше полстраницы мелким текстом :-)


  1. powerman
    21.01.2025 16:10

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


    1. opusmode
      21.01.2025 16:10

      Конда-то давно я был джуном и пытался вкатится в мир энтерпрайз разработки на джаве.

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

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

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

      Именно из этого растут ноги принципов и рекомендаций. Чем проще механизмы, тем сложнее их сломать.

      Это же касалось неймингов - Мартин предлагал именовать классы намерениями, а не действиями. Т.е чего я ожидаю от исполнения класса.

      Более того, сам Мартин писал, что чистый код это не о правилах, а о дисциплине и он создаётся в процессе практик, а не с первого раза.

      А если добить, то сам же дядя Боб говорил, что книга его, это не уникальные идеи, а компиляция. Там есть ссылка на того же Тима Оттингера.

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


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

        Сам Мартин в первой главе на страница 35 пишет вот так:

        Мы излагаем свои мнения в виде беспрекословных истин и не извиняемся за свою категоричность. Для нас, на данном моменте наших карьер, они являются беспрекословными истинами. Они составляют нашу школу мысли в области чистого кода.
        [...]
        Мы утверждаем, что если вы последуете нашему учению, то это принесёт вам такую же пользу, как и нам, и вы научитесь писать чистый и профессиональный код.

        Довольно близко к библии, заповедям и догмам.


        1. opusmode
          21.01.2025 16:10

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

          Давайте начнём с простого и примитивного - дядя Боб на Русском не пишет и вряд-ли его знает. Вы согласны с этим?

          Впрочем, это мелка придирка.

          Куда большая придирка - вырывать фразы из контекста. А теперь давайте откроем эту книжку в оригинале, ту самую 1 главу. ней есть часть о том, что ряд изестных разрабтчиков делятся мыслями о том, что такое чистый код. И вот приведённая вами цитата (которая находится на 43 листе оригинала) выглядит вот так. Буквально копи-паст

          Schools of Thought
          What about me (Uncle Bob)? What do I think clean code is? This book will tell you, in hideous detail, what I and my compatriots think about clean code. We will tell you what we think makes a clean variable name, a clean function, a clean class, etc. We will present these opinions as absolutes, and we will not apologize for our stridence. To us, at this point in our careers, they are absolutes. They are our school of thought about clean code.

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

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

          Немного отличается от вашей трактовки, не находите? Уже звучит не как догма, а больше похоже на... инженера. Т.е. человек сразу говорит о том, что хочет донести мысль в том виде, в котором она имеется. И да, не смотря на то, что у неё есть несовершенства, они не хотят заострять на них внимание в текущий момент. Вот именно потому тут хорошо перекликается absolute и strident. По нашему это было бы "пою, как умею петь сейчас".

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

          Martial artists do not all agree about the best martial art, or the best technique within a martial art. Often master martial artists will form their own schools of thought and gather students to learn from them. So we see Gracie Jiu Jistu, founded and taught by the Gracie family in Brazil. We see Hakkoryu Jiu Jistu, founded and taught by Okuyama Ryuho in Tokyo. We see Jeet Kune Do, founded and taught by Bruce Lee in the United States. We Are Authors 13 Students of these approaches immerse themselves in the teachings of the founder. They dedicate themselves to learn what that particular master teaches, often to the exclusion of any other master’s teaching. Later, as the students grow in their art, they may become the student of a different master so they can broaden their knowledge and practice. Some eventually go on to refine their skills, discovering new techniques and founding their own schools. None of these different schools is absolutely right

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

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

          Ну и так как текста и так много, давайте уж последний кусочек, который вы привели?

          Consider this book a description of the Object Mentor School of Clean Code. The techniques and teachings within are the way that we practice our art. We are willing to claim that if you follow these teachings, you will enjoy the benefits that we have enjoyed, and you will learn to write code that is clean and professional.

          Мне всё ещё надо переводить и трактовать или вы уже сами поняли, что не правы и прежде, чем что-то утверждать, стоит всё же немного изучить предмет дискуссии?

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

          Или вот знаете, Селин Дион, когда её попросили исполнить песню My heart will go on хотела отказаться. Потому, что песня ей не понравилась и даже вызвала отвращение. Как это возможно? Да собственно композитор, который писал музыку, решил презентовать песню. Пригласил Селин и её мужа к себе в номер, начал играть музыку на фортепиано, а потом ещё решил спеть. Песня абсолютно не понравилась певице, но вот муж сказал, что типа давайте мы там через месяц на студии звукозаписи что-нибудь накидаем.

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

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

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


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

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

            Дал ли я свою трактовку прочитанному? Да! Всё люди пропускают потребляемую информацию через собственный фильтр. Но если посмотреть на то, насколько сильно российское айти-сообщество тащиться от этой книги, такую трактовку дал не только я.


    1. funca
      21.01.2025 16:10

      LSP из SOLID это просто медийный шум.

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

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

      В оригинале, Барбара и ко размышляли над поведением программы в рантайме и в итоге зашли со своим тезисом в тупик на уровне логики (что, в принципе, нормально для науки). Т.е., если замена одного компонента другим не меняет поведения программы, то зачем вообще это делать? Ведь обычно цель у разработчиков как раз обратная - мы заменяем один компонент на другой, для того что-то изменить в поведении программы.

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

      Через несколько лет Дядя Боб вспомнил эту историю, которую когда-то услышал на совместной конференции, и решил использовать в качестве названия для только что придуманного им принципа, применительно к ООП. Позже Барбара говорила, что хоть LSP и не является тем, что она изначально имела ввиду, но благодарна Дяде за то, что тот увековечил её имя.


  1. SamDark
    21.01.2025 16:10

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


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

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

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


      1. SamDark
        21.01.2025 16:10

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


      1. SimSonic
        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. slonopotamus
      21.01.2025 16:10

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

      Нет, статичный класс делает кое-что ещё. Он ломает работу в многопоточном окружении.


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

      Можно провести эксперимент и написать эти длинные названия по-русски

      private static isPrime(candidate: number): boolean {
        if (this.являетсяНаименееРелевантнымКратнымСледующегоБольшегоПростогоМножителя?(candidate)) {
          this.кратныеПростымМножителям.push(candidate);
          return false;
        }
        return this.неКратенНиОдномуИзПредыдущихПростыхМножителей?(candidate);
      }
      


      1. tolyanski
        21.01.2025 16:10

        у вас случайно в роду нет 1С-ников?


      1. FanatPHP
        21.01.2025 16:10

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

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


        1. tolyanski
          21.01.2025 16:10

          function но_увы_поезд_уже_ушёл_и_верблюжий_стал_незыблемым_стандартом_при_том_что_длинные_строки_объективно_читать_легче_в_змеином(const А_можно_не_извращаться& а);


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

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

      Длиннющий код — это не совсем то, что написал Кнут. Такой код получается, если конвертировать Кнутовский WEB в обычный код. Улучшать его — это что-то вроде рефакторинга JS файлов, скомпилированных из тайпскриптовых сорсов. Можно, но зачем?

      Сам код написан в концепции Literate Programming и разбит на аккуратные составные части. Кнут тоже отделяет генерирование простых чисел от записи этих чисел в файл, и не сваливает весь код в одну кучу.

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


  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. Sabbone
        21.01.2025 16:10

        Прочитал - спрашивают про чистый код на небесах


        1. GospodinKolhoznik
          21.01.2025 16:10

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


    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. GospodinKolhoznik
        21.01.2025 16:10

        Посмотрел что за книга Algebra Driven Design, автор знакомый...

        Ба, да это же Sandy Maguire! Я читал его книгу Thinking with Types. Прикольный чувак - он в книге про себя сам написал, что уволился с солидной рабыты в серьёзном оффисе, чтобы вести полубродяжническую жизнь в трейлере, много играть на гитаре, ваять игру мечты и писать концептуальные книги по программированию. Уже за одно только это респект ему и уважуха!


  1. WASD1
    21.01.2025 16:10

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


    1. mclander
      21.01.2025 16:10

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


    1. Innominatam
      21.01.2025 16:10

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


      1. WASD1
        21.01.2025 16:10

        Это уже учтено в решении автора.
        Это уже в предлагаемом решении, о чём я прямо написал вот тут:
        > Учитывая, что список простых чисел у вас уже есть, причём в отсортированном виде (*).

        Так что я не очень понимаю, что вы хотели сказать?
        *) исходя из этого вся разница сведётся к диффу:

        <         for divisor in range( 2, check_upper_bound + 1):
        ---
        >         for divisor in primes:
        >             if divisor > check_upper_bound:
        >                 break
        


      1. PNSpasskiy
        21.01.2025 16:10

        Ну ещё можно вместо деления (относительно тяжёлой операции) проводить проверку на делимость нескольких первых простых чисел.


        1. WASD1
          21.01.2025 16:10

          Мы заведомо проверяем только делимость на заведомо простые, причём меньшеие, чем sqrt(candidate))
          Какую ещё оптимизацию вы предлагаете?


          1. PNSpasskiy
            21.01.2025 16:10

            Ну например не брать в выборку чётные числа. А так же числа оканчивающиеся на 5. Насчёт остальных проверок хз, что быстрее будет. Поделить на 3 или проверить признак делимости?


  1. DasMeister
    21.01.2025 16:10

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

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

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


    1. gorodskih
      21.01.2025 16:10

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

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


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

      В книге есть примеры и реального кода. В другой ветке его прокомментировал: https://habr.com/ru/articles/875426/comments/#comment_27818290


    1. cupraer
      21.01.2025 16:10

      Что же делать несчастному старику, который пытался донести идеи […]

      Простой рецепт для желающего доносить идеи, а не продавать свои безумные иллюзии:

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

      2. Выкристаллизуй идеи из написанного (и выкаченного в продакшн) кода из п.1 (а не из болтовни в переписке с такими же скучающими бездельниками).

      3. Сформулируй эти идеи, подкрепив их примерами кода попроще, и дав ссылку на полновесные решения из п.1. Сформулируй идеи, болван, а не подгоняй их под придуманный не тобой среднего качества метаакроним.

      Как видим, Мартин не справился ни с одним из этих простых пунктов. А его уровень как разработчика — легко увидеть по последним коммитам в его репозиторий на гитхабе (AoC 2024 на Clojure — ссылки не даю, потому что это сродни детской порнографии, но найти несложно).


  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

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


      1. aleksandy
        21.01.2025 16:10

        Двойку сразу в результат положить, кандидатов проверять с 3 и шаг инкремента увеличить до 2.


        1. WASD1
          21.01.2025 16:10

          Да согласен.


  1. tolyanski
    21.01.2025 16:10

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

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

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

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

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


    1. Vitaly_js
      21.01.2025 16:10

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


      1. tolyanski
        21.01.2025 16:10

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

        В то же время хороший код - это такой, который не требует комментирования :) Где истина? А истина где-то рядом))

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

        Короче, я сторонник самодокументированного кода. Но не в простых функциях поиска каких-то там чисел, т.е. я против доведения правил до абсурда


        1. Vitaly_js
          21.01.2025 16:10

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

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

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


    1. Vitaly_js
      21.01.2025 16:10

      дабл


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

      А в чём здесь идея? В длинных нечитаемых названиях методов, скрытых сайд-эффектах и неявном дата-флоу? Код для бизнеса от такого лучше не станет.

      Если что, в книге есть не только такой алгоритмический пример. Всю третью главу Мартин ссылается на листинг 3.7. Это реальный код из библиотеки для тестирования Fitnesse. В нём чуть больше ста строк кода, поэтому уберу под спойлер, но его обязательно стоит почитать.

      Пример реального чистого кода
      package fitnesse.html;
      
      import fitnesse.responders.run.SuiteResponder;
      import fitnesse.wiki.*;
      
      public class SetupTeardownIncluder {
        private PageData pageData;
        private boolean isSuite;
        private WikiPage testPage;
        private StringBuffer newPageContent;
        private PageCrawler pageCrawler;
      
      
        public static String render(PageData pageData) throws Exception {
          return render(pageData, false);
        }
      
        public static String render(PageData pageData, boolean isSuite)
          throws Exception {
          return new SetupTeardownIncluder(pageData).render(isSuite);
        }
      
        private SetupTeardownIncluder(PageData pageData) {
          this.pageData = pageData;
          testPage = pageData.getWikiPage();
          pageCrawler = testPage.getPageCrawler();
          newPageContent = new StringBuffer();
        }
      
        private String render(boolean isSuite) throws Exception {
           this.isSuite = isSuite;
          if (isTestPage())
            includeSetupAndTeardownPages();
          return pageData.getHtml();
        }
      
        private boolean isTestPage() throws Exception {
          return pageData.hasAttribute("Test");
        }
      
        private void includeSetupAndTeardownPages() throws Exception {
          includeSetupPages();
          includePageContent();
          includeTeardownPages();
          updatePageContent();
        }
      
      
        private void includeSetupPages() throws Exception {
          if (isSuite)
            includeSuiteSetupPage();
          includeSetupPage();
        }
      
        private void includeSuiteSetupPage() throws Exception {
          include(SuiteResponder.SUITE_SETUP_NAME, "-setup");
        }
      
        private void includeSetupPage() throws Exception {
          include("SetUp", "-setup");
        }
      
        private void includePageContent() throws Exception {
          newPageContent.append(pageData.getContent());
        }
      
        private void includeTeardownPages() throws Exception {
          includeTeardownPage();
          if (isSuite)
            includeSuiteTeardownPage();
        }
      
        private void includeTeardownPage() throws Exception {
          include("TearDown", "-teardown");
        }
      
        private void includeSuiteTeardownPage() throws Exception {
          include(SuiteResponder.SUITE_TEARDOWN_NAME, "-teardown");
        }
      
        private void updatePageContent() throws Exception {
          pageData.setContent(newPageContent.toString());
        }
      
        private void include(String pageName, String arg) throws Exception {
          WikiPage inheritedPage = findInheritedPage(pageName);
          if (inheritedPage != null) {
            String pagePathName = getPathNameForPage(inheritedPage);
            buildIncludeDirective(pagePathName, arg);
          }
        }
      
        private WikiPage findInheritedPage(String pageName) throws Exception {
          return PageCrawlerImpl.getInheritedPage(pageName, testPage);
        }
      
        private String getPathNameForPage(WikiPage page) throws Exception {
          WikiPagePath pagePath = pageCrawler.getFullPath(page);
          return PathParser.render(pagePath);
        }
      
        private void buildIncludeDirective(String pagePathName, String arg) {
          newPageContent
            .append("\n!include ")
            .append(arg)
            .append(" .")
            .append(pagePathName)
            .append("\n");
        }
      }
      

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

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

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

      Я согласен с цитатой на все сто процентов. Но что мы видим в коде, который Мартин считает чистым, правильным и достойным находиться в его книге? Метод render принимает объект pageData и делает скрытую мутацию этого объекта в методе updatePageContent. Я бы никогда в жизни не ожидал, что передав pageData в статический метода SetupTeardownIncluder.render я получу мутацию. Почему рендер вносит какие-то изменения в контент страницы? Где здесь пресловутый принцип единственной ответственности?

      У меня очень много вопросов к этому реальному коду! Зачем он сохраняет переменную isSuite на уровне инстанса класса? Если явно прокинуть её в методы, то код получится более... явный и простой в чтении. Почему он не использует собственный принцип "Один уровень абстракции на функцию" из этой же главы (страница 61), а вместо этого в метод render пихает сборную солянку: установку значения для переменной, два вызова приватных методов, возврат результата выполнения pageData.getHtml()? Я не джавист, но решил немного отрефакторить этот код. После рефакторинга сразу видно, что класс просто утилитарный, которому не нужно никакое внутреннее состояние, а мутация пусть и осталась (её здесь никак не убрать), но зато находится на самом видном месте. При желании весь этот код можно просто поместить внутрь класса PageData.

      public class SetupTeardownIncluder {
        public static String render(PageData pageData) throws Exception {
          return render(pageData, false);
        }
      
        public static String render(PageData pageData, boolean isSuite) throws Exception {
          if (!pageData.hasAttribute("Test")) return pageData.getHtml();
      
          StringBuffer newPageContent = new StringBuffer();
          newPageContent
            .append(getContent(pageData, "SetUp", "-setup"));
            .append(pageData.getContent());
            .append(getContent(pageData, "TearDown", "-teardown"));
          
          if (isSuite) {
            newPageContent
              .insert(0, getContent(pageData, SuiteResponder.SUITE_SETUP_NAME, "-setup"));
              .append(getContent(pageData, SuiteResponder.SUITE_TEARDOWN_NAME, "-teardown"));
          }
      
          pageData.setContent(newPageContent.toString());
          return pageData.getHtml();
        }
      
        private static String getContent(PageData pageData, String pageName, String arg) {
          String pagePathName = getPagePathName(pageData, pageName);
          if (!pagePathName) return "";
      
          StringBuffer content = new StringBuffer();
          return content
            .append("\n!include ")
            .append(arg)
            .append(" .")
            .append(pagePathName)
            .append("\n")
            .toString();
        }
        
        private static String getPagePathName(PageData pageData, String pageName) {
          WikiPage testPage = pageData.getWikiPage();
          WikiPage inheritedPage = PageCrawlerImpl.getInheritedPage(pageName, testPage);
          
          if (inheritedPage == null) return null;
      
          PageCrawler pageCrawler = testPage.getPageCrawler();
          WikiPagePath pagePath = pageCrawler.getFullPath(inheritedPage);
          return PathParser.render(pagePath);
        }
      }
      

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


  1. Gromilo
    21.01.2025 16:10

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

    Пруф


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

      Ни одна из этих разных школ не обладает абсолютной истиной. Тем не менее в рамках конкретной школы мы действуем так, будто её учение и арсенал приёмов верны.
      [...]
      Считайте, что эта книга является описанием Школы учителей Чистого кода. [...] Мы утверждаем, что если вы последуете нашему учению, то это принесет вам такую же пользу, как и нам, и вы научитесь писать чистый и профессиональный код. Но не стоит думать, что наше учение "истинно" в каком-то абсолютном смысле. Существуют другие школы и мастера [...].

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

      Если бы у книги цель была не в погружении в своё "учение", то альтернативные "школы" хотя бы были приведены списком. Я нашёл этот отрывок (Глава 1, страница 35). Ни до, ни после нет никаких примеров других "школ". Но зато есть несколько восхитительных цитат в начале страницы:

      Мы излагаем свои мнения в виде беспрекословных истин и не извиняемся за свою категоричность. Для нас, на данном моменте наших карьер, они являются беспрекословными истинами. Они составляют нашу школу мысли в области чистого кода.
      [...]
      Они [ученики школы] посвящают себя изучению того, чему учит конкретный мастер, часто отказываясь от учений других мастеров.

      Короче, для меня вся эта страница -- очень мощная агитка, которая призывает следовать "учению" Роберта во имя написания профессионального кода. Смотря на то, как глубоко эта книга сидит в головах многих программистов, агитка сработала на "отлично".


      1. Gromilo
        21.01.2025 16:10

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

        В первую очередь напрягает передача контекста через переменные класса.

        А супермелкие функции вообще не уменьшают когнитивную нагрузку.


  1. markshevchenko
    21.01.2025 16:10

    Насколько я знаю, Мартину не нравится прозвище "дядюшка Боб". Это прозвище он получил от читателей, когда был редактором журнала C++ Report.
    Так что зря вы пишите, что он "втирается в доверие".

    Впрочем, я согласен, что ваша версия кода генерации простых чисел проще и понятнее, чем у Мартина.


    1. Newbilius
      21.01.2025 16:10

      Ну так если не сделать себе соломенное чудище - можно же и не победить в споре)


  1. Sadler
    21.01.2025 16:10

    Всегда воспринимал речи Uncle Bob как некую сказку, жвачку для ума, не более. У каждого есть какое-то своё понимание хорошей архитектуры, и представление Боба -- не истина в последней инстанции. Ради интереса попросил Claude AI накидать архитектурные принципы, которыми я руководствуюсь в своём коде, на основе краткой беседы с ним:

    Вот так получилось
    ## 1. Фундаментальные принципы
    
    ### 1.1. Принцип Управляемой Сложности
    - Система должна быть достаточно простой для понимания отдельным разработчиком
    - Сложность должна соответствовать решаемой задаче, любое усложнение системы должно иметь явное обоснование
    - При выборе между двумя эффективными решениями предпочтение отдается более простому
    
    ### 1.2. Принцип Модульности
    - Система разделяется на четко определенные, слабо связанные модули
    - Каждый модуль имеет единую ответственность
    - Взаимодействие между модулями происходит через четко определенные интерфейсы
    - Внутренняя реализация модуля может меняться без влияния на другие части системы
    
    ### 1.3. Принцип Предсказуемости
    - Поведение системы должно быть понятным и предсказуемым
    - Похожие задачи должны решаться похожим образом
    - Система должна следовать принципу наименьшего удивления: изменения в одной части системы не должны вызывать неожиданных эффектов в других частях, сообщения об ошибках должны явно давать представление об их местонахождении и причинах
    
    ## 2. Практические принципы реализации
    
    ### 2.1. Организация кода
    - Структура проекта должна отражать предметную область, связанные компоненты располагаются рядом
    - Код организуется вокруг бизнес-концепций, а не технических деталей реализации
    - Используется последовательная система именования
    
    ### 2.2. Управление зависимостями
    - Зависимости должны быть явными и минимальными. Циклические зависимости не поощряются
    - Модули верхнего уровня не должны зависеть от деталей реализации
    - Внешние зависимости изолируются через адаптеры, либо их использование минимизируется
    
    ### 2.3. Работа с данными
    - Структуры данных должны быть простыми и понятными
    - Сложные структуры данных вводятся только при явной необходимости
    - Потоки данных в системе должны быть прозрачными и отслеживаемыми
    - Доменные объекты, обогащенные бизнес-логикой структуры данных, специфичные для конкретного домена, хранятся вместе с бизнес-логикой
    
    ## 3. Принципы эволюции системы
    
    ### 3.1. Управление изменениями
    - Система должна быть готова к изменениям требований
    - Изменения локализуются в минимальном количестве модулей
    - Новая функциональность добавляется через расширение, а не модификацию
    
    ### 3.2. Технологический стек
    - Используются проверенные и широко распространенные технологии
    - Новые технологии вводятся постепенно и обоснованно, поддерживается баланс между инновациями и стабильностью
    - Технологический стек должен соответствовать команде и задачам
    
    ### 3.3. Документация и обмен знаниями
    - Неочевидные архитектурные решения всегда документируются вместе с обоснованием их полезности
    - Поддерживается актуальная документация по API и интеграциям
    - Знания о системе распределяются между членами команды
    
    ## 4. Принципы качества
    
    ### 4.1. Тестируемость
    - Архитектура должна упрощать написание тестов
    - Модули проектируются с учетом возможности их изолированного тестирования
    - Критические пути в системе имеют максимальное покрытие тестами
    
    ### 4.2. Самотестирование кода
    - Ключевые инварианты системы проверяются через assertions
    - Проверки инвариантов включают:
      - Входные параметры функций и методов
      - Выходные значения критически важных операций
      - Состояние объектов после важных изменений
      - Корректность форматов данных
    - Assertions используются для раннего обнаружения проблем:
      - Нарушений бизнес-правил
      - Некорректных состояний системы
      - Нарушений контрактов между компонентами
    - Самотестирование не заменяет модульные тесты, а дополняет их
    - В production-окружении assertions могут быть отключены для оптимизации производительности
    
    ### 4.3. Производительность
    - Производительность учитывается на этапе проектирования
    - Оптимизации вводятся на основе измерений, а не предположений
    - Система масштабируется в критических направлениях
    - Производительность не должна ухудшать читаемость кода без веской причины
    
    ### 4.4. Безопасность
    - Безопасность встраивается в архитектуру изначально
    - Все внешние входы в систему проходят валидацию
    - Чувствительные данные изолируются и защищаются
    - Система устойчива к типовым атакам и уязвимостям
    

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


    1. Gromilo
      21.01.2025 16:10

      А что такое - Каждый модуль имеет единую ответственность в твоём понимании? Ведь каждый модуль часть чего-то большего, что включает ответственность всех его модулей.

      Я для себя сформулировал так: ответственность можно кратно и понятно описать. Т.е. множество функций должны как-то обобщаться во что-то одно.


      1. Sadler
        21.01.2025 16:10

        Это просто переложение https://en.wikipedia.org/wiki/Unix_philosophy#Do_One_Thing_and_Do_It_Well . Необязательно каждый модуль является частью чего-то большего: в горизонтальных структурах, вроде микросервисов, у нас большинство сервисов имеет равный ранг, ни один не входит в другой, и, тем не менее, каждый имеет ограниченную зону ответственности, которую должен обслуживать хорошо. Естественно, зона ответственности описывается на естественном языке в терминах целевой области знания, формально определять её было бы странно.
        Условно, даже в монолитной архитектуре модуль работы с БД не должен хорошо уметь форматировать дату или записывать логи, для этого есть другие модули, которые он может использовать через ограниченный интерфейс.

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


        1. Gromilo
          21.01.2025 16:10

          Так в этом и прикол. Модуль - очень размытое понятие. Модулем даже отдельный класс могли называть. Тем более что SOLID и S для классов мы считаем чем-то хорошим.

          И как не круги, одни модули входят в другие. Ваши равные микросервисы - всего-лишь сервис в рамках более крупной структуры, типа архитектуры предприятия.

          Поэтому для себя принцип единственности ответственности понимаю примерно так: можно эту самую ответственность как-то нормально/кратно сформулировать.

          Исходя из этого определения - у монолита тоже одна ответственность: обслуживать бизнес.


  1. jbourne
    21.01.2025 16:10

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


    1. arturdumchev
      21.01.2025 16:10

      Прошу прощения, но кажется, что в случае с книгой “Чистый код“ большой опыт не нужен, чтобы понять, что с функцией с названиемset2AsFirstPrime что-то не так.


    1. icya
      21.01.2025 16:10

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


  1. arturdumchev
    21.01.2025 16:10

    Спасибо за статью!

    Сталкивался на работе с фанатиками, приходилось спорить со ссылками на авторитеты (книга Чистый код, цитаты Боба Мартина).

    Находил тогда статью “Вероятно, хватит рекомендовать «Чистый код»“ , и рад, что новые статьи пишутся, потому что люди до сих пор думают, что книга 2008 все еще является образцом чистого кода.


  1. Yura_PST
    21.01.2025 16:10

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


  1. kot_mapku3
    21.01.2025 16:10

    В процессе чтения, поразительно, что некоторые примеры я просто пропустил (вероятно, не слишком внимательно читал листинги на Java). С примерами согласен, но не всё в его книге ерунда, тут, как уже высказались ранее, необходимо постоянно думать, оценивать предлагаемые советы и решения, а не мельком пробежаться по книге. Разве плохо давать содержательные имена, или декомпозировать гигантскую функцию, избегать супер-классов :-)
    Проблема в том, что некоторые его утверждения воспринимаются как догма, ну и масла в огонь подливают такие его "качественные" листинги на Java :)


  1. Jhayphal
    21.01.2025 16:10

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

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

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

    И вот, спустя 6 лет я могу детально объяснить почему конкретную строку или выражение написал именно так. Да, я не следую его рекомендациям. Я даже смутно помню их. Но способ мыслить оказался бесценен.

    P. S. То же самое с паттернами проектирования. Знакомство с ними объяснило саму суть ООП и для чего все это нужно. Я вряд ли смогу по памяти повторить один в один какой-либо паттерн, кроме простейших. Но я понимаю идеи, которые за ними стоят. И реализую решения, которые нужны для конкретной ситуации со всеми её нюансами.


    1. tolyanski
      21.01.2025 16:10

      Я вряд ли смогу по памяти повторить один в один какой-либо паттерн, кроме простейших.

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

      Но наши IT-фанатики этого не поняли, и теперь все же как ни крути, но паттерны наизусть помнить нужно, чтобы проходить собеседования :)


      1. cupraer
        21.01.2025 16:10

        Сам Мартин Фаулер в своей книге по паттернам писал, что знать наизусть паттерны не нужно, нужно просто использовать его книжку как справочник […]

        Завидую людям с таким непрошибаемым воздействием эффекта Даннинга-Крюгера. Странно, что он не написал ещё сводную книжку математических аксиом, штук на триста, в которую надо заглядывать в любой непонятной ситуации: от сложного рефакторинга до поноса.


      1. Jhayphal
        21.01.2025 16:10

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

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


        1. tolyanski
          21.01.2025 16:10

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


  1. cupraer
    21.01.2025 16:10

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

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


  1. GidraVydra
    21.01.2025 16:10

    Позиция ТС мне сразу напомнила откровения тех интересных личностей, которые пытаются с двух ног вкатиться в асу тп со словами: "да нафига тут эти ваши промышленные плк за многоденяк, щас всë за шапку сухарей на голом esp32 замастырю.".


  1. stanislavskijvlad
    21.01.2025 16:10

    Оффтопик

    Вы не заметили, что URL строка в Яндексе иногда сходит с ума ?


  1. vvbob
    21.01.2025 16:10

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


  1. Prizrakileni
    21.01.2025 16:10

    1. чистую архитектуру точно стоит прочитать

    2. точно не стоит делать ее единственной книгой о качестве кода, которую ты читал

    На роль единственной (если уж совсем влом читать больше одной книжки) отлично подходит упомянутый в коментах «совершенный код». А чистая архитектура отлично его дополняет. И после «совершенного кода» очень бросается в глаза неумение Мартина давать имена переменным и методам :)


  1. gen1lee
    21.01.2025 16:10

    Джавист не может быть крутым программистом по определению. Статья в точку, и убожество ОПП и джавы демонстрирует отлично.


    1. tolyanski
      21.01.2025 16:10

      А кто может? ДжаваСкриптист?)


      1. gen1lee
        21.01.2025 16:10

        Говнокод в статье на java, а на TS (~JS) как раз таки как правильно. И это особенность языка и ООП.

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


        1. woof_woof
          21.01.2025 16:10

           В убогой жаве такой возможности нет.

          вообще-то, есть


          1. woof_woof
            21.01.2025 16:10

            https://openjdk.org/jeps/445 но с недавних пор :)


      1. IUIUIUIUIUIUIUI
        21.01.2025 16:10

        Хаскелист, конечно.


        1. cupraer
          21.01.2025 16:10

          Коканат, агдаит и идристун ещё.


          1. tolyanski
            21.01.2025 16:10

            брейнфакерист


          1. Khelevaster
            21.01.2025 16:10

            Прологнатор


  1. fpinger
    21.01.2025 16:10

    А меня смущает сравнение императивного и декларативного подхода.

    a = b +1

    это разве не декларативно?

    А если учесть асинхронность скрывающуюся где-то там глубже?

    И декларативность вроде как улучшает читаемость, но развращает магией.


  1. PainKKKiller
    21.01.2025 16:10

    Вообще статья хороший пример того что восприятие кода, как и в целом восприятие текста, субъективно. Кому-то нравится Толстой, а кому-то Достоевский. Хороший код - тот с которым тебе удобно работать, и это тоже субъективно. Меня скорее отпугивают те программисты, которые это не осознают, и начинают возводить вкусовщину в абсолют, не важно Мартин это или кто то другой.


    1. powerman
      21.01.2025 16:10

      Хороший код - тот с которым тебе удобно работать

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