![image](https://habrastorage.org/webt/0_/ws/qg/0_wsqgbmx_fixyswnjhthypj5t4.jpeg)
Незаменимые практические советы по написанию кода в устойчивом темпе и по управлению сложностью, из-за которой проекты часто выходят из-под контроля. В книге описываются методы и процессы, позволяющие решать ключевые вопросы: от создания чек-листов до организации командной работы, от инкапсуляции до декомпозиции, от проектирования API до модульного тестирования. Автор иллюстрирует свои выводы фрагментами кода, взятыми из готового проекта. Написанные на языке C#, они будут понятны всем, кто использует любой объектно-ориентированный язык, включая Java, C++ и TypeScript. Для более глубокого изучения материала вы можете загрузить весь код и подробные комментарии к коммитам.
Для кого эта книга
Книга предназначена для опытных программистов. Предполагается, что они уже имели дело с неподдерживаемым кодом, с неудачными проектами по разработке ПО и стремятся к решению этих проблем.
Основная аудитория книги — разработчики корпоративного ПО, в частности бэкенд-разработчики. Я много лет работал в этой области — по сути, книга отражает мой личный опыт. Но если вы фронтенд-разработчик, разработчик игр, DevOps-инженер и т. п., то, думаю, вы все равно извлечете для себя много полезной информации.
Надеюсь, вам будет удобно читать код на компилируемом, объектно-ориентированном языке семейства C. Хотя большую часть своей карьеры я программировал на C#, я многому научился из книг, где код был написан на C++ или Java. В этой книге примеры кода приведены на C#, но я надеюсь, что программисты на Java, TypeScript и C++ тоже найдут их полезными.
Основная аудитория книги — разработчики корпоративного ПО, в частности бэкенд-разработчики. Я много лет работал в этой области — по сути, книга отражает мой личный опыт. Но если вы фронтенд-разработчик, разработчик игр, DevOps-инженер и т. п., то, думаю, вы все равно извлечете для себя много полезной информации.
Надеюсь, вам будет удобно читать код на компилируемом, объектно-ориентированном языке семейства C. Хотя большую часть своей карьеры я программировал на C#, я многому научился из книг, где код был написан на C++ или Java. В этой книге примеры кода приведены на C#, но я надеюсь, что программисты на Java, TypeScript и C++ тоже найдут их полезными.
Глава 3. ПРЕОДОЛЕНИЕ ТРУДНОСТЕЙ
Прислушайтесь к своей интуиции и попробуйте решить следующую простую задачу. Не пытайтесь прибегнуть к математике или вычислениям.
Бейсбольная бита и мяч стоят 1 доллар 10 центов. Бита стоит на 1 доллар дороже, чем мяч. Сколько стоит мяч?
![image](https://habrastorage.org/webt/y6/e4/em/y6e4emw9xwwnziy9q10jm5jd7t0.png)
Обратите внимание на свой ответ. Кажется, что это слишком простой вопрос для такой книги, поэтому вы начинаете искать здесь подвох.
Чуть позднее мы вернемся к этой задаче.
Читая эту главу, вы делаете шаг назад и пытаетесь ответить на фундаментальный вопрос: «Почему разрабатывать ПО так сложно?»
Ответ на него столь же фундаментален. Он связан с принципом работы человеческого мозга. Это главная тема всей книги. Прежде чем обсуждать, как писать код, который умещается в вашей голове, мы должны обсудить, что еще укладывается в ней.
В последующих главах мы всё будем рассматривать на примерах.
3.1. ЦЕЛЬ
Скорее всего, после прочтения первых двух глав вы будете озадачены. Возможно, вы думали, что программная инженерия — интеллектуально сложная и таинственная дисциплина. Так и есть, но нужно с чего-то начать. И почему бы не начать с чего-то простого? Каждый успешный
человек начинал свой путь, делая небольшие шаги в выбранном направлении. Помните, что и восхождение в гору всегда начинается у ее подножия (рис. 3.1).
![image](https://habrastorage.org/webt/02/2s/vl/022svlqtuveqsxheink81k8dvaq.png)
Прежде чем мы продолжим, думаю, нам следует сделать паузу и обсудить проблему, которую мы пытаемся решить. Что это за проблема?
Основная тема, которая поднимается в этой книге, связана с надежностью программного обеспечения, которое впоследствии должно стать важной и полезной составляющей вашей организации или вашего бизнеса.
3.1.1. Надежность
Организация создает ПО по разным причинам. Как правило, чтобы заработать или сэкономить. Иногда государство запускает программные проекты для развития цифровой инфраструктуры граждан. Само программное обеспечение не предполагает прямой прибыли или экономии, но перед ним всегда стоит задача, которую нужно выполнить.
Часто на разработку сложного ПО уходят месяцы или даже годы.
Многие программы могут существовать десятилетиями. В течение этого времени могут выпускаться обновления, внедряться новые функции, исправляться ошибки и т. д. Процесс разработки ПО — это постоянная, непрерывная работа.
Программное обеспечение предназначено для помощи в организации рабочих процессов компании. Обновляя функционал или исправляя ошибки, вы оказываете существенную поддержку вашей фирме. И хорошо, когда такое сопровождение ПО осуществляется постоянно, как сейчас, так и через полгода.
Разработка, обновление и сопровождение ПО требуют постоянных усилий по его поддержанию.
Как говорит Мартин Фаулер, не обращая внимания на внутреннее качество, вы очень быстро потеряете способность вносить улучшения в свою кодовую базу.
«Вот что происходит, когда качество оставляет желать лучшего. Сначала мы видим быстрый прогресс, но со временем добавлять новые функции становится все сложнее. Даже незначительные изменения требуют от программистов понимания больших объемов сложного для восприятия кода. После внесения изменений начинают возникать неожиданные проблемы, что приводит к длительному тестированию и появлению ошибок, которые нужно исправлять».
Это та ситуация, которую должна решать программная инженерия. Важно сделать процесс разработки ПО более регулярным, что положительно повлияет на поддержку вашей организации на месяцы, годы и десятилетия.
Разработка, тестирование и сопровождение программного обеспечения — это регулярный и непрерывный процесс. Качественное и надежное ПО — это залог успеха организации.
3.1.2. Ценность
Цель программного обеспечения — приносить пользу организации. С его помощью можно повысить ценность вашего бизнеса. Часто профессионалы в области разработки ПО думают, что если их код не представляет ценности, то его было бессмысленно создавать.
Определенный акцент на создании ценности может быть оправдан. Я знаком с некоторыми разработчиками, которые, если их предоставить самим себе, будут проводить часы, бездельничая с какой-нибудь умной структурой собственного изобретения.
Такие ситуации свойственны и для коммерческих компаний. Ричард П. Габриэль рассказал о взлете и падении Lucid так: пока они возились с идеальной коммерческой реализацией языка Common Lisp, появился C++ и захватил рынок кросс-платформенных языков разработки ПО.
Сотрудники Lucid считали, что C++ хуже Common Lisp, но Габриэль в конце концов понял, почему клиенты выбрали именно его. Язык C++, возможно, был менее последовательным и более сложным, но он работал и был доступен клиентам. Это побудило Габриэля сформулировать афоризм: «Хуже значит лучше». Так компания Lucid ушла с рынка.
В правой части рис. 3.2 изображены специалисты, которые сосредотачиваются только на одной какой-то технологии, не обращая внимания на ее назначение.
![image](https://habrastorage.org/webt/w1/i4/1n/w1i41nrrr8uoturtir8xkdc7rbm.png)
Акцент на ценности будет возможной реакцией на аналогичный тип мышления. Важно узнать, служит ли код какой-то определенной цели. Термин «ценность» часто используется в качестве условного обозначения, несмотря на то что его нельзя измерить. Есть школа управления проектами, которая базируется на следующих принципах.
- Сформулировать гипотезу о влиянии планируемых изменений.
- Внести изменения.
- Измерить влияние и сравнить его с прогнозом.
Мое пособие вовсе не об управлении проектами, но такой подход кажется мне разумным, о чем я уже писал ранее.
Представление о том, что код должен быть полезным, к сожалению, ведет к логической ошибке, согласно которой код, не несущий ценности, запрещен. Идея «чем хуже, тем лучше» становится все ближе.
Это в корне неверно, так как не каждый код сможет дать немедленно измеримое значение. С другой стороны, можно измерить ценность его отсутствия. Простой пример — безопасность. Возможно, у вас не получится измерить ценность включения аутентификации в онлайн-систему, но вы точно сможете измерить ее отсутствие.
То же самое относится и к аргументу Фаулера о внутреннем качестве. Отсутствие архитектуры можно измерить, но, когда появится такая возможность, будет уже слишком поздно. В своей жизни я уже наблюдал, как из-за низкого внутреннего качества разорялись целые компании.
На рис. 3.2 среднее место занимает надежность. Здесь не поощряются технологии ради технологий, но и рекомендуется не слишком фокусироваться на ценности.
Разработка ПО должна способствовать надежности. Следуя чек-листам, рассматривая предупреждения как ошибки и т. д., вы сможете предотвратить появление нежелательного кода. Ни одна из методологий и эвристик в этой книге не сможет гарантировать идеального результата, но их знание поможет вам двигаться в верном направлении. Так или иначе вам придется использовать свой опыт и навыки. В конце концов, это и есть искусство разработки программного обеспечения.
3.2. ПОЧЕМУ ПРОГРАММИРОВАТЬ ТАК СЛОЖНО?
Почему программировать сложно? Причин много. Во-первых, как обсуждалось в разделе 1.1, мы проводим неверные аналогии, которые заводят нас в тупик. Но это не единственная причина.
Еще одна причина в том, что компьютер — это не мозг. Кстати, это еще одно неподходящее сравнение.
3.2.1. Аналогия с мозгом
Сходство компьютера и мозга кажется очевидным (рис. 3.3). Между ними определенно есть поверхностное сходство. Оба могут выполнять вычисления, вспоминать прошлые события, хранить и извлекать информацию.
Компьютер похож на мозг? Я думаю, что различий между ними больше, чем сходств. Компьютер не может делать интуитивные выводы, плохо интерпретирует изображение и звук. В конце концов, у него нет внутренней мотивации.
![image](https://habrastorage.org/webt/ur/ka/p1/urkap1dcq1ul3-atay_orhlbues.png)
Мозг похож на компьютер? По сравнению с компьютером наша способность к вычислениям невероятно медленная, а память слишком ненадежна: воспоминания могут исчезнуть или быть искажены, ими можно манипулировать, а вы даже не будете подозревать об этом. Например, вы уверены, что 20 лет назад были на вечеринке со своей лучшей подругой, тогда как она утверждает, что никогда там не была. Получается, что у кого-то из вас ложные воспоминания.
А что насчет оперативной памяти? Благодаря ей компьютер может отслеживать миллионы действий, тогда как кратковременная память человека может хранить от четырех до семи единиц информации.
Все вышеперечисленное играет в программировании важную роль. Даже скромная подпрограмма может легко создать десятки переменных и команд. Пытаясь понять, что делает исходный код, вы, по сути, запускаете в уме эмулятор языка программирования. И если происходит слишком много всего, вы никогда не сможете это проконтролировать.
Слишком много — это сколько?
В этой книге число семь используется как символ предела кратковременной памяти мозга. Иногда вы можете отслеживать и девять действий, но семь из них всегда будут под контролем.
3.2.2. Код больше читается, чем пишется
Постепенно мы приближаемся к фундаментальной проблеме программирования.
Вы тратите больше времени на чтение кода, чем на его написание.
Вы пишете строку кода один раз, а читаете ее по нескольку раз. С нетронутой кодовой базой редко приходится иметь дело, а прежде, чем приступить к работе с уже существующей, нужно в коде разобраться. Чтобы добавить новую функцию, вы читаете данный код, чтобы понять, какой можно использовать повторно, а куда лучше добавить новый. Чтобы исправить ошибку, важно сначала понять причину ее выведения. Обычно вы тратите большую часть своего рабочего времени на чтение существующего кода.
Оптимизируйте свой код для удобства чтения.
Вы постоянно слышите о новых языках программирования, библиотеках, платформах или функциях IDE, позволяющих быстрее создавать больше кода. Как показывает история компании Lucid, вряд ли это будет хорошей стратегией для устойчивого развития ПО. Быстрое создание большого количества кода скорее будет означать больше кода, который нужно будет прочесть, — чем больше вы пишете, тем больше вам приходится читать. Автоматическая генерация кода может только усугубить ситуацию.
Мартин Фаулер сказал о низком качестве кода следующее:
«Даже небольшие изменения требуют от программистов понимания больших объемов сложного для восприятия кода».
Трудный для понимания код замедляет работу, а каждая минута, которую вы тратите на то, чтобы этот код упростить, окупается десятикратно.
3.2.3. Удобочитаемость
Легко сказать, что вы должны отдавать предпочтение читабельному коду, а не тому, который легко написать, но что же такое этот читабельный код?
Бывало ли такое, что, читая код, вы спрашивали себя: «Кто написал эту ерунду?!» — а потом оказывалось, что именно вы и написали?
Такое бывает со всеми: когда вы пишете код, вы понимаете, о чем пишете, но когда читаете его, все понимание куда-то исчезает.
В конце концов, важность кода неоспорима. Документация может быть устаревшей или ее может не быть вовсе. Автор кода может быть в отпуске или уволиться.
Вдобавок ко всему, мозг плохо работает при чтении и оценке формальных утверждений. И все-таки как вы ответили на вопрос о бейсбольной бите и мяче в начале этой главы?
Число, которое сразу же пришло вам в голову, — 10. Этот ответ дает большинство людей. Но он неверный. Если мяч стоит 10 центов, то бита должна стоить 1 доллар 10 центов, а общая стоимость составит 1,20 доллара. Правильный ответ — 5 центов.
Дело в том, что мы постоянно ошибаемся. Особенно, когда решаем тривиальные математические задачи или читаем код.
Чтобы написать удобочитаемый код, вам не следует доверять своей интуиции. Нужно что-то более эффективное: эвристика, чек-листы… программная инженерия. Мы будем возвращаться к этой теме на протяжении всей книги.
3.2.4. Интеллектуальный труд
Бывало ли, что вы едете на машине и через десять минут вдруг «просыпаетесь», в ужасе спрашивая себя: «Как я сюда попал?»
У меня однозначно было. Не то чтобы я в прямом смысле заснул за рулем, но я настолько погрузился в свои мысли, что забыл, что я за рулем. Однажды я проехал на велосипеде мимо собственного дома и попытался попасть в соседнюю квартиру вместо своей.
Я понимаю, что после прочитанного вы едва ли захотите садиться ко мне в машину. Но дело не в том, что меня легко отвлечь, а в том, что мозг работает, даже когда вы этого не осознаёте.
Вы знаете, что ваш мозг контролирует ваше дыхание, даже когда вы об этом не думаете. От его работы зависят ваши двигательные функции, и это тоже происходит неосознанно.
Я был очень потрясен, когда вдруг оказался за рулем своей машины, не помня, как я там оказался. Я ехал по своему родному Копенгагену и, возможно, выполнил ряд сложных маневров. Останавливаясь на красный, поворачивая налево, затем направо, при этом остерегаясь велосипедистов, я прибыл к месту назначения. Но я не помнил, что я делал и как добрался.
Для сложной интеллектуальной работы неважна такая составляющая, как осознанность.
Вы когда-нибудь были настолько погружены в работу, что, оторвавшись от монитора, поняли, что на улице уже стемнело и вы просидели так несколько часов? В психологии это называется оказаться в состоянии потока. Находясь в нем, вы настолько полностью
поглощены какой-либо деятельностью, что теряете контроль на собой и временем.
Вы можете программировать, даже абсолютно не задумываясь над этим, или же писать код, осознавая весь процесс. Дело в том, что в вашей голове происходит многое, о чем вы явно не подозреваете. Ваш мозг выполняет работу, тогда как сознание — это пассивный зритель.
Вам может казаться, что умственный труд — это преднамеренное мышление, но это не так. Психолог и лауреат Нобелевской премии Даниэль Канеман предлагает модель мышления, состоящую из двух систем: системы 1 и системы 2.
«Система 1 интуитивная. Она отвечает за быстрое мышление, практически без усилий и без чувства произвольного контроля.
Система 2 тяжелая, медленная. Она направляет внимание на требующие усилий умственные действия, включая сложные вычисления. Действия системы 2 часто связаны с субъективным опытом свободы действий, выбора и концентрации».
Вы, наверное, думаете, что программирование основано только на системе 2, но это не совсем так. Когда вы пытаетесь разобраться в коде, система 1 всегда работает в фоновом режиме. Проблема в том, что она работает быстро, но не очень точно, что может привести к ошибочным
выводам. Это и происходит, когда 10 становится первым числом, приходящим вам на ум во время поиска решения задачи о бейсбольной бите и мяче.
Чтобы организовать исходный код так, чтобы наш мозг смог его понять, вы должны научиться контролировать свою систему 1. Канеман пишет:
«Важная особенность системы 1 в том, что она обрабатывает только активную информацию, тогда как не извлеченная (даже бессознательно) из памяти может вообще для нее не существовать. Система 1 отлично справляется с выстраиванием лучшей возможной истории, включающей активированные в данный момент идеи, но не учитывает (и не может учитывать) информацию, которой у нее нет.
Система 1 ловко придумывает связную историю причин и следствий, объединяющую доступные ей кусочки информации. В случае неуверенности она делает ставку на тот или иной ответ, исходя из опыта. Система 1 постоянно отслеживает, что происходит внутри и снаружи разума, и генерирует оценку разных аспектов ситуации без конкретного намерения и почти, или совсем, без усилий. Система 1 — это эмоции».
Ваш мозг всегда склонен делать поспешные выводы, что может повлиять на ваш код. Лучше организовать код так, чтобы активировалась актуальная информация. Как выразился Канеман, то, что вы видите —это все, что здесь есть (WYSIATI, акроним от англ. What You See Is
All There Is).
Становится ясно, почему глобальные переменные и скрытые зависимости делают код непонятным. Когда вы смотрите на фрагмент кода, глобальная переменная обычно не видна. Даже если ваша система 2 знает об этом, это знание неактивно, поэтому система 1 не учитывает его.
Сделайте так, чтобы связанный код было видно полностью. Все зависимости, переменные и необходимые решения должны быть видны одновременно. Позже я приведу много примеров на эту тему, особенно в главе 7.
3.3. НАВСТРЕЧУ ПРОГРАММНОЙ ИНЖЕНЕРИИ
Целью разработки программного обеспечения должна быть поддержка организации, которой оно принадлежит. Вы всегда должны быть готовы вносить изменения в устойчивом темпе.
Но писать код сложно. Вы тратите больше времени на его чтение, чем на написание, поэтому мозг легко ввести в заблуждение — даже такими тривиальными задачами, вроде той, что была в начале главы.
Программная инженерия призвана решить эту проблему.
3.3.1. Связь с computer science
Может ли информатика помочь? Почему бы и нет? Но computer science — это не программная инженерия, точно так же, как физика — это не машиностроение.
Такие дисциплины могут взаимодействовать, но это не одно и то же. Ученые могут получить от специалистов-практиков важные данные, а результаты исследований можно применять в инженерии (рис. 3.4).
![image](https://habrastorage.org/webt/z5/5g/fw/z55gfwlcgdsrgpdniwsbacexpao.png)
Например, результаты computer science можно сравнить с многоразовыми пакетами.
У меня было несколько лет профессионального опыта разработки ПО, прежде чем я узнал об алгоритмах сортировки. Образования в области computer science у меня нет — я научился программированию самостоятельно. И если бы мне нужно было отсортировать массив в C++,
Visual Basic или VBScript, я бы вызвал метод.
Для сортировки коллекций вам не обязательно иметь возможность реализовать быструю сортировку или сортировку слиянием. Чтобы выполнять запросы к базе данных, вам не нужно знать о хеш-индексах, таблицах SST, LSM- и B-деревьях.
Computer science помогает индустрии разработки программного обеспечения развиваться, но полученные в ней знания часто можно упаковать в многоразовое ПО. Разбираться в computer science никогда не помешает, но это совсем не обязательно, чтобы быть программным инженером.
3.3.2. Гуманный код
Алгоритмы сортировки можно инкапсулировать и распространять в виде повторно используемых библиотек. Сложные структуры хранения и извлечения данных могут быть упакованы в виде программного обеспечения БД общего назначения или предлагаться в виде облачной инфраструктуры.
Но вам все равно придется писать код.
Вы должны структурировать его так, чтобы он «умещался в вашей голове».
Как говорил Мартин Фаулер,
«любой дурак может написать код, который будет понятен компьютеру. Хороший программист пишет код, который будет понятен людям».
Когнитивные ограничения мозга полностью отличаются от ограничений компьютера. Последний может отслеживать миллионы действий в оперативной памяти, тогда как ваш мозг — только семь.
Компьютер будет принимать решения только на основе информации, к которой ему приказано обратиться, а ваш мозг склонен делать поспешные выводы. То, что вы видите, — это все, что здесь есть.
Очевидно, что код нужно писать так, чтобы итоговое ПО работало так, как вы хотите. Это больше не главная проблема программной инженерии. Теперь основная задача: организовать код так, чтобы он «умещался в вашей голове».
Код должен быть гуманным, что подразумевает написание небольших автономных функций. В этой книге я использую число семь в качестве показателя пределов кратковременной памяти человека. Исходя из этого гуманный код подразумевает менее семи зависимостей.
Но дьявол кроется в деталях, поэтому готовьтесь к большому количеству примеров.
Об авторе
Марк Симан — бывший экономист, который в итоге нашел себя в программировании. Трудился веб-разработчиком и разработчиком корпоративных продуктов с конца 1990-х годов. В молодости Марк мечтал стать рок-звездой, но, к сожалению, по его словам, не обладал ни талантом, ни внешностью — зато потом стал сертифицированным разработчиком Rockstar. Он написал удостоенную премии Jolt книгу о внедрении зависимостей, принял участие в сотнях международных конференций и записал видеокурсы для Pluralsight и Clean Coders. Марк регулярно ведет блог с 2006 года. Сейчас он живет в Копенгагене с женой и двумя детьми.
Более подробно с книгой можно ознакомиться на сайте издательства:
» Оглавление
» Отрывок
По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
Для Хаброжителей скидка 25% по купону — Код