Что я понял, благодаря неуспехам. Реджинальд Брейдвайт

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

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

Глава 1. Что я понял, благодаря неуспехам

Разработка проекта может провалиться, если хотя бы одна из составляющих хромает:

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

Люди

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

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

Внешние навязанные ограничения (государственные, например) как правило вредят.

Действие

Получать обратную связь надо быстро — fail fast! Чем раньше узнаете, что у вас проблемы, тем проще найти решение. Работает как в отношении кода, так и продукта в целом.

Детали

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

Расписание

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

Сила

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

История

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

«Если бы мы больше времени уделили планированию, то спланировали бы лучше; так было в прошлом проекте» — вовсе не факт.

Уметь заканчивать

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

Глава 2. Дизайн софта

Строить лучше

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

Лучшая архитектура

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

Глава 3. Теория, которая подтверждает наблюдения

Теория D, теория P, и почему они важны

Если мы верим в теорию D (deterministic — что процесс можно описать полностью, и если что‑то не сходится, то у нас просто не хватает данных), то мы верим, что проект можно спланировать заранее и полностью.

Если мы верим в теорию P (probabilistic — что предсказать полностью ничего нельзя, а только какие‑то части и только с какой‑то точностью), то мы верим, что планировать следует только какие‑то части проекта, и когда что‑то идёт не так, нам надо добавить в план новую информацию, чтобы скорректировать его.

Вера определяет поведение

Адепты теории D верят, что:

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

Адепты теории P верят, что:

  • спланировать можно только какие‑то части проекта, но не проект целиком;
  • при расчёте времени все, все, все лгут, зачастую — неосознанно;
  • самое важное в разработке — обучение, зачастую — на ошибках;
  • командная работа лучше одиночной;
  • использовать фреймворки следует осторожно, сравнив, какие проблемы они позволят решить и какие принесут с собой.

Глава 5. Проект‑менеджмент как рынок с информацией

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

Проект похож на рынок, где какая‑то информация ценится там, где её не хватает. Задача менеджеров в том, чтобы уметь отделять ценную информацию (которая поможет предсказать исход проекта или его части) от неценной; уметь «покупать» её у разработчиков и «продавать» её руководству или инвесторам.

Глава 6. Кирпичи

Софт делается не из кирпичей

Аналогия с кирпичами опасна. Кирпичи — слишком простые. Если понятно, как работать с одним кирпичом, понятно, как работать с остальными. В софте не так. Мало того, что там не всегда понятно, как обращаться с «кирпичами», там ещё и непонятно, сколько их надо, чтобы собрать проект.

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

Разработку трудно параллелить

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

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

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

Важную роль также играет и «куда положить какой кирпич» и «как его соединить с другими». Одна ошибка может откатить отметку прогресса сильно назад.

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

Глава 7. Цикл «попытка‑провал»

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

Обратная связь

Есть ошибка, при которой софт разрабатывается инкрементами вместо итераций.

Инкремент — часть софта, которая сама по себе закончена, но не несёт ценности для бизнеса. Итерация — законченная часть, которая несёт ценность бизнесу.

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

Глава 8. Облысение софта

Технический долг приводит к нерабочему продукту.

Глава 9. Мышиная ловушка

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

Глава 10. Утиное программирование

Это менеджерский антипаттерн; мнение, что всё, что не связано с программистами, языками программирования или инструментами — программированием не является.

Глава 11. Не получается найти хороших продажников

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

Эту книгу дополнят

Некоторые другие книги о программировании:

И вообще:

Чистая архитектура. Часть 3

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

Глава 20. Бизнес‑правила

Коротко:

  • Бизнес‑правила — это правила или процедуры, которые приносят или сохраняют бизнесу деньги.
  • Специфические для приложения правила — это дополнение к бизнес‑правилам.

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

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

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

Юзкейсы зависят от входных данных и производят выходные данные, но при этом не зависят от формы, в которой эти данные передаются.

Глава 21. Говорящая архитектура

Коротко:

  • Архитектура — это не про фреймворки.
  • Хорошая архитектура рассказывает, какую систему она описывает, а не на чём построена.

Фреймворки — это инструмент, а не материал. Архитектура должна основываться на юзкейсах, а не на фреймворках. Хорошая архитектура позволяет откладывать решения о выборе фреймворка и менять их, если придётся.

Веб — это механизм ввода‑вывода.

Хорошая архитектура рассказывает, какую систему она описывает, а не на чём построена.

Глава 22. Чистая архитектура

Коротко, чистая архитектура:

  • не зависит от фреймворков;
  • легко тестируется;
  • не зависит от пользовательского интерфейса;
  • не зависит от базы данных;
  • не зависит от каких‑либо внешних агентов.

Внешние слои могут зависеть от внутренних, но не наоборот: 

Сущности инкапсулируют критичные бизнес‑правила. Юзкейсы содержат правила, специфические для приложения. Адаптеры интерфейсов конвертируют данные из формата, удобного для юзкейсов, в формат удобный для внешних слоёв.

Главы 23–25

Коротко:

  • Сделать систему тестируемой помогает шаблон простого объекта.
  • Скрыть сложную логику можно за фасадами.
  • Стоит почаще задаваться вопросом «а понадобится ли мне это?».

Глава 26. Корневой компонент

Коротко:

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

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

Глава 27. Сервисы

Коротко:

  • Сервисы слабо‑связаны, но так бывает не всегда.
  • Сервисы помогают достичь независимого деплоя, но так тоже бывает не всегда ¯\_(ツ)_/¯
  • Архитектура определяется не сервисами как таковыми, а границами между значимыми компонентами системы.

Глава 28. Тесты

Коротко:

  • Тесты — часть системы.
  • Если тесты сильно связаны с компонентами, то небольшое изменение может уронить сотни тестов.

Что дальше

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

Вместе с этой книгой советую прочитать пару других:

Предыдущие части:

И ссылки из конспекта:

Чистая архитектура. Часть 2

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

Глава 12. Компоненты

Коротко:

  • Компонент — наименьшая сущность, которую можно задеплоить, как часть системы.
  • Компоненты, DLL — плагины к бизнес‑правилам.

Глава 13. Связность компонентов

Коротко — чтобы определить, к какому компоненту относится тот или иной класс, следует пользоваться 3 принципами:

  • принцип эквивалентности переиспользования и перевыпуска;
  • принцип общей причины для изменения;
  • принцип совместного переиспользования.

Принцип эквивалентности переиспользования

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

Принцип общей причины для изменения

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

Принцип совместного переиспользования

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

Глава 14. Сочетаемость компонентов

Коротко — чтобы определить отношения между компонентами, следует пользоваться 3 принципами:

  • принцип ацикличности зависимостей;
  • принцип стабильных зависимостей;
  • принцип стабильности абстракций.

Принцип ацикличности зависимостей

Не допускайте зацикленности в графе зависимостей компонента. Если в зависимостях есть цикл, его можно разорвать одним из 2 способов:

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

Принцип стабильных зависимостей

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

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

Нестабильность = Кол‑во выходных / (Кол‑во входных + Кол‑во выходных)

Принцип стабильности абстракций

Компонент должен быть настолько же абстрактным, насколько он стабилен.

Абстрактность = Кол‑во абстрактных классов и интерфейсов в компоненте / Общее количество классов в компоненте

По оси Х — нестабильность, по оси Y — абстрактность. Следует придерживаться линии main sequence и избегать зон по углам:

Глава 15. Что такое архитектура

Коротко:

  • Цель хорошей архитектуры — облегчить разработку, деплой и поддержку системы.
  • Хорошая архитектура выделяет бизнес‑логику и считает её важнейшим элементом системы.
  • Хорошая архитектура пытается построить систему так, будто количество непринятых решений относительно деталей — максимально.

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

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

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

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

Глава 16. Независимость

Коротко — архитектура должна поддерживать:

  • юзкейсы;
  • поддерживаемость;
  • разработку;
  • лёгкий деплой системы.

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

Следует делить систему на слои. Например:

  • независимые бизнес‑правила;
  • бизнес‑правила под конкретно это приложение;
  • пользовательский интерфейс;
  • база данных и др.

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

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

Разделять систему можно по‑разному:

  • на уровне исходников;
  • уровне развёртывания;
  • и сервисном уровне.

Какой способ подходит, зависит от самого проекта, стадии, на которой он находится и других параметров.

Глава 17. Границы

Коротко:

  • знание одного компонента системы о других должно быть ограничено;
  • границы должны отделять сущности, которые имеют значение (для бизнес‑логики) от тех, которые не имеют значения;
  • основа — бизнес‑логика.

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

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

В хорошей архитектуре бизнес‑логика — это основа, а всё остальное: устройства ввода‑вывода, БД и т. д. — плагины к ней:

Что дальше?

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

Чистая архитектура. Роберт Мартин

Это первая из 3 частей конспекта. В ней мы затронем понятие архитектуры, обзор парадигм программирования и объяснение принципов SOLID.

Предисловие и введение

Коротко:

  • Правила построения архитектуры одинаковы для любых программных систем.
  • Трудности в поддержке возникают из‑за плохой архитектуры.

Железо меняется радикально и быстро, софт — не меняется. Он состоит из условий, присвоений и циклов. Есть разные парадигмы и приёмы, но в конечном счёте всё преобразовывается в условия, присвоения и циклы. И именно поэтому правила построения архитектуры универсальны.

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

Глава 1. Что такое дизайн и архитектура

Коротко:

  • Цель хорошей архитектуры — минифицировать потребные ресурсы для поддержки и улучшения софта.
  • Говнокод тормозит разработку не только в будущем, но и прямо сейчас.

Разницы между дизайном и архитектурой софта нет. На примере архитектора (не программного): он продумывает технические аспекты здания, но также описывает то, как здание выглядит.

Цель хорошей архитектуры — минифицировать потребные ресурсы для поддержки и улучшения софта.

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

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

Глава 2. Сказка о двух ценностях

Коротко:

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

Разработчики отвечают за два аспекта софта: его поведение и структуру. Поведение — это то, как софт удовлетворяет бизнес‑потребностям. Ради поведения разработчиков нанимают и платят им деньги. Но думать, что имеет значение только поведение, — значит не видеть картины в целом.

В термине software есть часть soft, которая значит, что система должна быть «мягкой», легкоизменяемой. Если каждое изменение противоречит предыдущим, не вписывается в уже созданную модель, то вносить именения в будущем будет сложнее. Хорошая архитектура не зависит ни от какой модели и не отдаёт предпочтений ни одной из них.

Что важнее? На примерах:

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

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

Глава 3. Обзор парадигм

Коротко: и структурное, и объектно‑ориентированное, и функциональное программирование — все парадигмы что‑то запрещают. Парадигмы точно знают и говорят нам, чего не стоит делать.

Глава 4. Структурное программирование

Коротко:

  • Понимать программу полностью сложно, её надо разбивать.
  • Необдуманный прямой контроль (goto) — плохо.
  • Тесты помогают найти и доказать, что баг есть. Они не доказывают, что багов нет.

Дейкстра заметил, что понимать программу полностью — сложно, а goto мешают разбивать её на более мелкие части. При этом простые управляющие конструкции — if/else, do/while — наоборот, облегчали работу. Из таких элементарных управляющих конструкций может быть построена любая программа.

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

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

Глава 5. Объектно‑ориентированное программирование

Коротко:

  • ООП: инкапсуляция, полиморфизм, наследование?
  • Делайте важное ядром, а всё остальное плагинами к нему.

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

Наследование по сути просто переобъявление каких‑то полей или методов. Есть не только в ОО языках, хотя в них наследование работает удобнее.

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

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

Код бизнес‑правил может меняться и выкатываться в прод независимо от чего‑либо ещё.

Глава 6. Функциональное программирование

Коротко: иммутабельность данных — хорошо.

ФП учит иммутабельности данных. Но есть несколько компромиссов. Например, наличие в приложении как чистых компонентов, так и компонентов с сайд‑эффектами.

ФП предлагает идею event sourcing (над переводом не уверен, поэтому пусть остаётся ивент‑сорсинг, прим. автора статьи) — когда мы не храним состояние, а храним транзакции (переходы между состояниями). Чтобы получить стейт в какой‑то момент времени, надо применить к начальному стейту все транзакции, которые произошли к нужному моменту времени.

Глава 7. Принцип единой ответственности

Коротко: функция должна решать только одну задачу.

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

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

Глава 8. Принцип открытости для расширения и закрытости для модификации

Коротко:

  • Поведение сущности должно быть расширяемым без необходимости менять саму сущность.
  • Если компонент А должен быть защищён от изменений в компоненте Б, то компонент Б должен зависеть от компонента А.

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

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

О зависимостях: если компонент А должен быть защищён от изменений в компоненте Б, то компонент Б должен зависеть от компонента А. Только так можно выстроить такие отношения между компонентами, что изменения в требованиях не будут выворачивать кишки всему приложению.

Глава 9. Принцип подстановки Барбары Лисков

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

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

Глава 10. Принцип разделения интерфейса

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

Если представить ситуацию, где User1 зависит только от op1, то первая архитектура первой диаграммы может привести к лишним перекомпиляциям:

Архитектура на второй диаграмме эту проблему решает:

Глава 11. Принцип инверсии зависимостей

Коротко:

  • Модули должны зависеть от абстракций, а не от деталей реализации.
  • Абстракции не должны зависеть от деталей реализации.

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

Что дальше?

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

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

Отдебажь это! Часть 2

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

Глава 6. Определяем, что у нас проблемы

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

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

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

Пользователи не будут писать вам о багах, если составить баг‑репорт им слишком сложно. Упрощайте процесс:

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

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

Попробуйте поработать в команде поддержки. Это поможет понять реальное состояние продукта и даст возможность вникнуть в проблему без игры в глухой телефон с поддержкой.

Глава 7. Прагматичная нетерпимость к багам

Баги надо чинить в первую очередь, это уменьшает неопределённость и технический долг. Чем больше багов будет копиться, тем более пофиг на них будет — такого допускать нельзя.

Между «ой да плевать, пусть идёт как идёт» и «в проекте всё будет идеально вылизано» следует выбирать нечто посередине, чуть ближе к правому краю:  Прагматичная нетерпимость находится почти по середине, чуть ближе к перфекционизму

Быстрых фиксов не существует — разбираться в проблеме придётся каждый раз. Чтобы было проще:

  • настройте систему версий, сборку проекта, автотесты, CI, CD;
  • отделите хороший код: чистый, оттестированный, отдебаженный — от плохого;
  • выделите важные части системы, найдите и исправьте ошибки в ней;
  • найдите и исправьте ошибки в остальных частях системы;
  • проводите недели чисток, когда все занимаются только поиском ошибок и дебагингом.

Глава 8. Особые случаи

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

Иногда багфиксы ломают обратную совместимость. Добавьте пункты для проверки совместимости в фикс‑чеклист. Если багфикс ломает совместимость, создайте как можно менее болезненный путь перехода на новую версию. Полагайтесь на создание режима совместимости, как на крайнее средство — этот способ очень дорогой и может наплодить ещё больше багов (привет, Ворд!).

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

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

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

  • вы профилируете версию продукта, максимально приближенную к продакшену;
  • окружение максимально похоже на боевое;
  • программа работает с реальными данными либо почти такими же.

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

Глава 9. Идеальное окружение для отладки

У вас должны быть автотесты. Критерии хороших тестов:

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

Настройте систему контроля версий.

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

Глава 10. Учим программу дебажить себя

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

Глава 11. Антипаттерны

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

Ссылки по теме

Раньше ↓