Boilerplate code
При использовании языков, которые считаются многословными, программист должен написать много кода, чтобы выполнить только незначительную функциональность.
Такой код называется шаблонным.
Потребность в шаблонном коде может быть уменьшена с помощью высокоуровневых механизмов, таких как:
Происхождение
Родственным термином является бухгалтерский код, относящийся к коду, который не является частью бизнес-логики, но перемежается с ней для того, чтобы обновлять структуры данных или обрабатывать вторичные аспекты программы.
Преамбула
Одна из форм шаблонного кода состоит из объявлений, которые, хотя и не являются частью логики программы или основного синтаксиса языка, добавляются в начало исходного файла как обычай. Следующий пример Perl демонстрирует шаблонный шаблон:
#!/usr/bin/perl use warnings; use strict;
Первая строка-это shebang, который идентифицирует файл как Perl-скрипт, который может быть выполнен непосредственно в командной строке (в системах Unix/Linux.)
Два других-это прагмы (директивы), включающие предупреждения и строгий режим, которые предписаны модным стилем программирования Perl.
Следующий пример-шаблонный шаблон языка программирования C/C++, #include guard.
Это проверяет и устанавливает глобальный флаг, чтобы сообщить компилятору, является ли файл myinterface.h уже был включен.
Поскольку в компиляции модуля может быть задействовано много взаимозаменяемых файлов, это позволяет избежать многократной обработки одного и того же заголовка (что привело бы к ошибкам из-за нескольких определений с одним и тем же именем).
В объектно-ориентированном программировании
В объектно-ориентированных программах классы часто снабжаются методами получения и установки переменных экземпляра.
Определения этих методов часто можно рассматривать как шаблонные.
Хотя код будет варьироваться от одного класса к другому, он достаточно стереотипен по структуре, чтобы его лучше генерировать автоматически, чем писать от руки.
Большая часть шаблона в этом примере существует для обеспечения инкапсуляции.
Если бы имя переменных и владелец были объявлены общедоступными, методы доступа и мутатора не были бы нужны.
Чтобы уменьшить количество шаблонных шаблонов, было разработано много фреймворков, например Lombok для Java.
Тот же код, что и выше, автоматически генерируется фреймворком Lombok с использованием аннотаций Java, что является формой метапрограммирования:
@AllArgsConstructor @Getter @Setter public class Pet
В некоторых других языках программирования можно добиться того же с меньшим шаблоном, когда язык имеет встроенную поддержку таких общих конструкций.
Например, эквивалент приведенного выше кода Java может быть выражен в Scala, используя только одну строку кода:
case class Pet(var name: String, var owner: Person)
Или в C# с использованием автоматических свойств с созданными компилятором резервными полями:
Шаблонный метод
В дополнение к объявлениям, методы в языках ООП также вносят свой вкладhttps://sprutio.beget.com/# в количество шаблонных шаблонов.
Проведенное в 2015 году исследование популярных Java-проектов показывает, что 60% методов могут быть однозначно идентифицированы по появлению 4,6% его токенов, что делает оставшиеся 95,4% шаблонных неуместными для логики.
Исследователи полагают, что этот результат будет переведен в подпрограммы на процедурных языках в целом.
В HTML следующий шаблонный шаблон используется в качестве базового пустого шаблона и присутствует на большинстве веб-страниц:
Потребность в шаблонном коде может быть уменьшена с помощью высокоуровневых механизмов, таких как метапрограммирование (при котором компьютер автоматически записывает необходимый шаблонный код или вставляет его во время компиляции ), соглашение по конфигурации (которое обеспечивает хорошие значения по умолчанию, уменьшая необходимость указывать программу. детали в каждом проекте) и проектирование на основе моделей (в котором используются модели и генераторы преобразования модели в код, что устраняет необходимость в ручном стандартном коде).
СОДЕРЖАНИЕ
Источник
Преамбула
Это проверяет и устанавливает глобальный флаг, чтобы сообщить компилятору, был ли уже включен файл myinterface.h. Поскольку в компиляцию модуля может быть вовлечено множество взаимозависимых файлов, это позволяет избежать многократной обработки одного и того же заголовка (что может привести к ошибкам из-за нескольких определений с одним и тем же именем).
В объектно-ориентированном программировании
Джава
Scala
В некоторых других языках программирования может быть возможно достичь того же с меньшим количеством шаблонов, когда язык имеет встроенную поддержку таких общих конструкций. Например, эквивалент приведенного выше кода Java можно выразить на Scala, используя всего одну строку кода:
Или в C # с использованием автоматических свойств с полями поддержки, созданными компилятором:
Шаблон метода
В дополнение к объявлениям, методы на языках ООП также вносят свой вклад в количество шаблонов. Исследование популярных Java-проектов в 2015 году показывает, что 60% методов можно однозначно идентифицировать по появлению 4,6% их токенов, в результате чего оставшиеся 95,4% шаблонов не имеют отношения к логике. Исследователи полагают, что этот результат можно применить к подпрограммам процедурных языков в целом.
В HTML следующий шаблон используется как базовый пустой шаблон и присутствует на большинстве веб-страниц:
Введение в HTML5 Boilerplate
Даже для самых опытных ветеранов-дизайнеров начинать работу с новым стандартом может быть непросто, даже если язык разработан для упрощения кодирования, например HTML5. Говоря об этом, глубокое знание HTML 4.01 — это то, что может быть построено на основе, это просто случай, чтобы узнать, какие теги есть, а какие нет.
HTML5 Boilerplate помогает дизайнерам начать работу с новым стандартом, предлагая профессиональный интерфейсный шаблон, который позволяет создавать быстрый, надежный и адаптируемый сайт с набором HTML5-готовых функций и элементов.
Он был создан Paul Irish и Divya Manian и представляет собой проект с открытым исходным кодом, который идеально подходит для создания кросс-браузерных сайтов, которые работают со старыми браузерами, в то же время будучи готовыми для HTML5.
Его можно загрузить с веб-сайта HTML5 Boilerplate в его полной форме или в урезанной версии, которая не включает всю пояснительную документацию. Более легкая версия рекомендуется для тех, кто работал с ней раньше, и ее можно использовать, когда вы ознакомитесь с шаблоном. Для тех из вас, кто уверен, что вы можете работать с ним полностью, есть также настраиваемая опция, которая позволяет вам выбирать нужные элементы.
Что в коробке?
Основные функции, которые можно найти в HTML5 Boilerplate, включают в себя все необходимые элементы, которые вам понадобятся для начала, а также сопроводительную документацию:
Modernizr также включен для того, чтобы позволить вам стилизовать новые элементы HTML5 в IE, и помогает обнаруживать функции HTML5 или CSS3 во всех браузерах, включая более ранние версии IE (до v9).
Давайте сначала посмотрим на HTML, который по своей сути состоит из ряда условных комментариев IE для соответствующих IE-специфичных классов и CSS для более старых версий IE. Они дают определенное количество преимуществ для дизайнера, использующего технику условных классов, например, более простую интеграцию с CMS, такими как WordPress и Drupal.
Также исправлено множество CSS для более старых версий IE, а также классов IE, которые применяются к тег. Это проверяется как HTML5, а класс no-js HTML также включает в себя те же элементы, что и Modernizr и Dojo, а также параметры для Google Frame, Google CDN для jQuery и код отслеживания Google Analytics в области содержимого.
Вы также можете использовать HTML5 Boilerplate с Initializr ( ознакомьтесь с демонстрационной страницей здесь ), который генерирует шаблоны на основе Boilerplate, которые позволяют выбирать элементы, которые вы хотите, и те, которые вам не нужны. При этом у вас также есть возможность использовать адаптивный шаблон с самого начала, а не начинать с пустой страницы.
Начиная
После того, как вы загрузили HTML5 Boilerplate, пришло время настроить базовую структуру сайта, добавить контент, стиль и функциональность и начать тестирование! Вначале базовая структура будет выглядеть примерно так:
Итак, давайте посмотрим, как их можно использовать, начиная с CSS, каталог, который должен содержать все файлы CSS вашего сайта. Обратите внимание, что этот каталог уже содержит некоторые CSS, чтобы помочь вам начать работу, а также нормализовать CSS.
Стартовый CSS включает в себя:
Это не зависит от условных имен классов, таблиц условных стилей или Modernizr и может использоваться «из коробки» независимо от ваших предпочтений при разработке.
Общие помощники
Детализация фрагментов для всех классов CSS потребует огромного количества текста, поэтому мы рассмотрим наиболее релевантные. Однако имейте в виду, что существуют следующие классы:
Знакомьтесь: «все и сразу», React Boilerplate от Maximilian Stoiber v3.6.0
Сначала о грустном
Сначала немного о грустном. Значение стартовых сборок для react-проектов просто огромно. Как известно, собственно React — это всего лишь библиотека реализующая уровень представления и для того, чтобы создать полноценное приложение, потребуется применить множество других библиотек, благо экосистема React предлагает немало таковых. По этой причине конфигурация проекта React представляет собой серьезную проблему, решение которой отдельному разработчику найти крайне сложно. Это прекрасно понимает и сам Dan Abramov, заявляя: “Конфигурация не должна стоять на пути к началу работы”. И предлагает решение — Create React App. В этой стартовой сборке, снискавшей значительную популярность (более 51 тыс. Stars GitHub), авторы постарались максимально оградить разработчика от вопросов конфигурирования, пряча конфигурацию по умолчанию, урезав набор модулей до минимума, предоставляя возможности автоматической генерации составных частей, создавая тем самым ощущение простоты и легкости разработки react-приложения. Более того, они же, исходя из того же посыла, предлагают свой список рекомендуемых сборок. Подход, возможно, хорош для привлечения новичков, но быстро себя исчерпывающий себя своей ограниченностью и вопросы конфигурации, вопросы применения лучших модулей, лучших практик все так же требуют своего решения. И как тут быть, например, разработчику, который разрабатывает свой первый react-проект?
Выход есть
Сборка react-boilerplate представляет собой альтернативный подход к решению проблемы и предлагает «все и сразу». Это набор модулей, признанных де-факто стандартными и проверенных в работе, набор, в котором применены лучшие практики в организации проекта, в том числе и с учетом масштабируемости и производительности, внедрены технологии помогающие программисту быстрее разрабатывать и отлаживать код.
Разработчики сборки позиционируют ее, как высокомасштабируемую, «offline-first» (однажды загруженное приложение работает и в офлайн-режиме), учитывающую опыт лучших разработчиков, нацеленную на высокую производительность и построенную с учетом «best practice», крепкую, «production ready», проверенную в боях основу для разработки react-приложения.
Применяемые библиотеки
Подробнее о применяемых модулях:
Масштабируемость и производительность
В сборке последовательно внедрены принципы поддержки масштабируемости и увеличения производительности, детально изложенные разработчиком сборки Max Stoiber в его лекции «Масштабирование ReactJS приложений» и Dan Abramov в лекции «Компонены презентационные и компоненты-контейнеры».
В частности, эти принципы включают в себя:
Кодирование, отладка, тестирование
Сборка предоставляет широкие возможности в содействии программисту в написании и отладке кода:
Командная строка
Сборка предоставляет, как уже упоминалось, набор команд для командной строки, в котором реализованы многие необходимые разработчику возможности, среди которых:
Есть что-то лишнее?
Если же вы решите, что какие-то модули вам не нужны, их можно удалить и сборка вам поможет своими инструкциями (здесь и тут).
Вам показалось, что чего-то не хватает?
Дополнительные возможности, предоставляемые сборкой:
Документация
Обновления, баги
Проект регулярно обновляется, своевременно откликаясь на изменения в составляющих его модулях, (последнее обновление 26.06.2018), активно решает проблемы: например, на 11.08.2018 решено 1367 и 9 проблем находятся в процессе решения.
Итак, сборка дает отличный пример полновесной конфигурации React проекта, создает условия программисту для ускорения написания и отладки кода, предлагает примеры реализации лучших практик применяемых в разработке высокомасштабируемых и производительных приложений.
И в заключение — послание от автора сборки Maximilian Stoiber и его команды:
“Если вам нужна крепкая, проверенная в боях основа для построения вашего следующего суперпродукта, и вы обладаете некоторым опытом работы с React, это идеальный вариант для вас!”
Пишите код, который легко удалять, а не дополнять

Каждая новая строка кода приносит с собой затраты в виде необходимости ее поддержки. Чтобы избежать подобных затрат на работу с большим количеством кода мы прибегаем к его повторному использованию. Недостаток применения этого метода заключается в том, что он начинает мешать нам, в случае если мы захотим что-либо поменять в будущем.
Чем больше у вашего API пользователей, тем больше кода приходится переписывать для введения новых изменений. Верно и обратное: чем больше вы полагаетесь на сторонний API, тем больше проблем испытываете когда он изменяется. Упорядочивание взаимодействия и взаимосвязей разных частей кода является серьезной проблемой в больших системах. И по мере развития проекта, растет и масштаб этой проблемы.
Перевод статьи на русский язык подготовлен компанией PayOnline, провайдером платежных решений для вашего онлайн-бизнеса.
Я говорю о том, что если уж мы так хотим считать количество строк кода, нам следует смотреть на них не как на «произведенные строки», но как на «потраченные строки», — Э. Дейкстра, рукопись 1036.
Если относиться к «строкам кода» как к «потраченным», тогда, удаляя их, мы снижаем стоимость поддержки. Вместо создания повторно используемых программ, нам следует стремиться к созданию программ одноразового употребления. Думаю, не нужно объяснять вам, что удалять код гораздо веселее, чем писать его.
Чтобы написать легко поддающий удалению код, старайтесь всячески избегать зависимостей в общем и как можно чаще отказываться от их упорядочивания. Разбивайте свой код на уровни: пишите простые в использовании API, создавая их на основе более простых в применении, но в целом менее удобных по отдельности решений. Разделяйте код, изолируя сложные в написании и наиболее изменчивые части от остального программного кода и друг от друга. Не делайте жестких определений для всех возможных случаев: в некоторых ситуациях лучше оставить возможность выбора во время работы программы. Не пытайтесь заниматься всем этим одновременно и подумайте, следует ли вам вообще писать так много кода.
Шаг 0: Не пишите код
Количество строк кода само по себе мало о чем говорит, а вот эффект от 50, 500, 5 000, 10 000, 25 000 строк и т. д. отличается существенно. Монолит размером в миллион строк попортит вам больше нервов, чем структура в 10 тыс. строк. Если же говорить о времени, деньгах и усилиях, которые вам придется потратить на его замену, то здесь разница будет ощущаться гораздо сильнее.
Чем больше у вас кода, тем сложнее от него избавиться. Тем не менее, сохранение одной строчки кода не дает никаких результатов само по себе. Как бы то ни было, удалять проще всего такой код, от которого вы успели отказаться еще до того, как приняться за его написание.
Шаг 1: Пользуйтесь копипастой
Писать «многоразовый» код гораздо легче задним числом, имея на руках несколько примеров использования, нежели пытаться оценить, какие из них могут потребоваться в будущем. Впрочем, положительные стороны этого метода вы ощущаете на себе, уже просто работая с файловой системой. Поэтому с повторным использованием кода вроде бы все в порядке: небольшая избыточность пойдет программе только на пользу.
Пользоваться копипастой время от времени лучше, чем создавать библиотечную функцию, только лишь для того, чтобы понять, как эта функция будет себя вести в вашем случае. То есть следует хорошенько подумать, надо ли вам в данный момент писать функцию вместо копипасты, потому что как только вы превращаете свои наработки в общедоступный API, процесс их последующего изменения становится сложнее.
Всегда помните, что написанная вами функция будет вызываться как по прямому назначению, так и для других вещей, о которых вы даже и не думали в момент ее создания. Использующие ее программисты будут полагаться на собственные наблюдения, а не на то, что вы написали в документации. Ну и, конечно, легче будет удалить содержимое функции, нежели саму функцию.
Шаг 2: Не пользуйтесь копипастой
Небольшое отступление: создайте для util специальную директорию и сохраняйте каждую утилиту в отдельный файл. Использование одного util-файла приведет к тому, что он в конечном счете вырастет до огромных размеров и тогда делить его на отдельные части будет очень сложно. Всегда помните, что ведение одного util-файла — плохая практика.
Чем менее специфичен тот или иной код для вашего приложения или проекта, тем легче вам будет использовать его повторно, и тем меньше вероятность его изменения или удаления. Это, как правило, библиотечный код, описывающий запись данных, работу со сторонними API, дескрипторы файлов или процессы. Еще примеры, которые вам не нужно будет удалять — списки, хеш-таблицы и другие наборы данных. Не из-за частой простоты их интерфейсов, но потому, что они не будут расти с точки зрения области применения с течением времени.
Не пытайтесь специально облегчить себе задачу удаления кода. Вместо этого на данном этапе мы должны стараться держать трудно поддающиеся удалению части программы как можно дальше от частей, удалить которые легко.
Шаг 3: Пишите больше boilerplate-кода
Библиотеки пишутся, чтобы избежать постоянной копипасты. И тем не менее, часто получается так, что в процессе их написания мы добавляем еще больше копипасты, но называем ее по-другому: boilerplate. Создание бойлерплейтов во многом похоже на копипастинг, с той разницей, что в нем вы каждый раз меняете разную часть кода, а не одну и ту же. Как и в случае с копипастой, вы дублируете части кода, чтобы избежать представления зависимостей и добиться гибкости, взамен получая еще большую избыточность.
Библиотеки, которым требуются бойлерплейты — это часто разработки вроде сетевых протоколов, форматов передачи данных, инструментов для парсинга и вообще все те кодовые базы, в которых сложно объединить политики (то, что программа должна делать) с протоколом (что она может делать) без накладывания каких-либо ограничений. Такой код сложно удалить: как правило, он требуется для общения с другими компьютерами или обработки различных файлов. При этом загрязнять его бизнес-логикой — это последнее, чего мы хотим. Нет, речь не идет о каком-то дополнительном упражнении в повторном использовании кода. Мы просто стараемся держать все подверженные частым изменениям блоки кода подальше от относительно статических. То есть мы занимаемся минимизацией зависимостей библиотечного кода, хоть пусть нам и приходится писать для него бойлерплейт. В итоге получается, что вы пишите больше строк кода, но все они приходятся на те части, которые легко поддаются удалению.
Шаг 4: Не пишите boilerplate-код
Бойлерплейты лучше всего работают, когда предполагается, что библиотеки будут соответствовать самым разным вкусам разработчиков, но иногда такой подход приводит к чрезмерной избыточности. Тогда наступает время поместить вашу гибкую библиотеку внутрь другой, которая обладает своими взглядами на правила, схемы и состояния. Создание простых в использовании API заключается в превращении вашего boilerplate в библиотеку. И это не такая уж и редкость, как вы могли бы подумать. В качестве примера можно привести один из самых популярных и любимых http-клиентов для Python, requests, который успешно справляется с предоставлением простого интерфейса, работая на основе более избыточной библиотеки urllib3. Requests позволяет пользоваться многими типовыми схемами работы с http, скрывая большинство подробностей от глаз пользователя. В то же время urllib3 выполняет конвейеризацию, управление соединением и ничего от пользователя не прячет.
Дело здесь не столько в том, чтобы скрывать какие-то подробности, помещая одну библиотеку внутрь другой, сколько в разделении ответственности: requests можно сравнить с турагентством, которое дает на выбор путевки для популярных в мире http путешествий, тогда как urllib3 нужна, чтобы удостовериться, что у вас есть все необходимое, чтобы это путешествие прошло как надо.
Нет, я не призываю вас немедленно пойти и создать директории /protocol/ и /policy/. Однако, возможно, это станет необходимостью, ведь вы наверняка захотите содержать вашу util-директорию свободной от всякой бизнес-логики и при этом продолжать работать над созданием своего тандема библиотек. Вы вполне можете работать над ними параллельно, не дожидаясь, пока работа над базовой библиотекой будет завершена.
Часто бывает полезным также делать «обертки» для сторонних библиотек, даже если они выполнены в виде протокола. Вы можете создать библиотеку, подходящую именно для вашего кода, вместо того, чтобы использовать общие для всего проекта решения. Часто бывает так, что создаваемый вами API не может быть одновременно приятным в использовании и хорошо расширяемым. Эти два понятия идут вразрез друг с другом.
Разграничение ответственности позволяет нам порадовать некоторых пользователей, не перекрывая кислород другим. Деление на уровни легче всего делать, когда у вас изначально есть хороший API, однако написание хорошего API поверх плохого едва ли придется вам по вкусу. Хорошие API проектируются с учетом того, как их будут видеть пользователи (т. е. программисты), и создание иерархии в этом смысле означает понимание того, что вы не можете угодить всем одновременно.
Смысл разделения кода на уровни состоит не столько в том, чтобы написать код, который вы впоследствии сможете удалить, сколько в том, чтобы сделать трудноудаляемый код приятным в использовании (не загрязнять его бизнес-логикой).
Шаг 5: Напишите большой блок кода
Сколько бы вы ни копипастили, ни рефакторили, ни делили на уровни, ни проектировали, все сводится к тому, что код должен выполнять какую-то работу. Иногда, если все идет не так, как задумывалось, лучше всего просто сдаться и написать доброе количество низкокачественного кода, просто чтобы все остальное заработало.
Бизнес-логика — это бесконечная серия пограничных случаев, а также быстрых и грязных трюков. И это нормально. Меня это устраивает. Другие стили вроде «игрового кода» и «кода основателя» представляют собой то же самое: попытка срезать на поворотах, чтобы сэкономить значительное количество времени.
Почему я предлагаю просто взять и написать много кода? Потому что избавляться от одной большой ошибки бывает гораздо легче, нежели пытаться удалить 18 маленьких, тесно переплетенных друг с другом. Программирование вообще во многом связано с исследованием. Сделать несколько ошибок и получить результат — способ более быстрый, чем пытаться продумать все с первого раза. Это особенно справедливо в случаях веселых или творческих начинаний. Если вы пишете свою первую игру, не начинайте этот процесс с движка. Аналогично этому, не пишите веб-фреймворк поперек приложения. Я говорю так, потому что знаю, что у вас все равно получится бардак, разобраться в котором не сможет ни один психически здоровый человек. Поэтому лучше сядьте и напишите сначала именно это бардак.
Монорепозитории представляют собой такой же компромисс: вы не будете заранее знать, как разделить код. Ну а разворачивать такую вот «одну большую ошибку» легче, чем 20 тесно связанных. Когда вы знаете, какую часть кода надо будет вскоре забросить, а какую удалить или с легкостью заменить, вы можете срезать гораздо больше углов. Так бывает, когда вы занимаетесь заказами по сайтам и веб-страницами, посвященными одноразовым событиям, ну или любой подобной работой, где у вас есть готовый шаблон и все что вам остается делать — это штамповать копии или просто заполнять пробелы, оставленные разработчиками фреймворка.
Нет, я не предлагаю вам писать одну и ту же ерунду по десять раз, пытаясь исправить все ее ошибки. Я говорю о другом. Как сказал когда-то Алан Перлис: «Все должно создаваться сверху вниз, за исключением первого раза». Не бойтесь совершать новые ошибки, брать на себя новые риски и пусть медленно, но верно, с помощью итерации продвигаться вперед.
Стать профессиональным разработчиком ПО означает собрать целый каталог сожалений и ошибок. Успех ничему не учит. Вы не можете заранее знать, как выглядит хороший код, но вот оставшиеся от плохого кода шрамы всегда свежи в вашем сознании. В любом случае проекты, в конце концов, либо терпят неудачу, либо становятся унаследованным кодом. Неудачи случаются чаще, чем успехи. Быстрее будет слепить десять разных комков грязи и посмотреть, что из этого получится, нежели пытаться довести до блеска одну кучу дерьма. Удалять код целиком проще, чем делать это по частям.
Шаг 6: Делите код на части
Большие комки грязи легко лепить, но вот поддерживать — сложнее всего. Попытка внести в них простое, на первый взгляд, изменение заканчивается внесением исправлений почти во все части кодовой базы.
Итак, мы создали в нашем коде иерархию для разделения ответственности как для платформенных, так и для доменных задач, и теперь нам надо найти способ разделить логику, которая находиться поверх всего этого.
«Начните со списка самых сложных проектных решений или тех из них, которые с наибольшей вероятностью изменятся. Далее проектируйте каждый модуль так, чтобы скрыть такое решение от других модулей». — Дэвид Парнас.
Вместо того чтобы разбивать код на части со схожей функциональностью, мы делим его на части, исходя из того, чем они отличаются друг от друга. Мы выделяем те из них, которые вызывают наибольшие трудности в написании, поддержке или удалении. Мы не создаем модули, исходя из того, сможем ли мы использовать их повторно, главное, чтобы их было удобно менять в будущем.
К сожалению, некоторые проблемы оказываются связанны друг с другом теснее, и их бывает сложнее отделить от других. Несмотря на принцип одной ответственности, который гласит, что «каждый модуль должен решать только одну сложную проблему», на деле гораздо важнее, чтобы «решением каждой сложной проблемы занимался только один модуль». В случаях, когда модуль занимается сразу двумя вещами, это происходит, потому что изменение одной части требует изменения другой. Работать с одним ужасным, но простым в плане интерфейса компонентом часто бывает проще, чем с двумя компонентами, требующими тщательной координации друг с другом.
«Я не стану пытаться сейчас точнее определить материал, подпадающий под это краткое описание [«слабая связанность»]; возможно, я никогда не сумею дать этому внятное определение. Однако я знаю, когда вижу, и кодовая база, рассматриваемая в этом деле, не такая». — Судья Верховного суда США Стьюарт.
Система, в которой вы можете удалять те или иные ее части без необходимости переписывания другие, часто называется слабосвязанной, однако объяснить как она выглядит на деле гораздо проще, чем заранее знать как ее создать. Слабая связанность допускает даже жесткое задание переменной или использование флага командной строки поверх нее. Смысл этой методики заключается в получении возможности менять основные решения без необходимости переделывать весь код.
В продуктах Microsoft Windows, к примеру, для этих целей используются внешние и внутренние API. Внешние API привязаны к жизненному циклу десктопных программ, а внутренний — к ядру, на основе которого они работают. Скрытие этих API дает Microsoft гибкие возможности по внесению в систему изменений без какой-либо опасности сломать кучу программ в результате этой деятельности.
HTTP также содержит примеры слабой связанности: добавление кэша перед HTTP сервером, перемещение изображений в CDN, в результате которого изменяются лишь ссылки на них. Ни тот, ни другой механизм не ломают ваш браузер. Другой пример слабой связанности — применяемые в HTTP коды ошибок. Общие для серверов по всему вебу проблемы имеют свой уникальный идентификатор. Когда вы получаете 400 ошибку, вы знаете, что выполнение тех же операции, которые к ней привели, никак не изменит ситуацию. А вот в случае с 500 ошибкой повторная перезагрузка страницы может все изменить. HTTP-клиенты могут обрабатывать множество ошибок, избавляя программистов от необходимости делать это самостоятельно.
Следует учитывать, как ваше ПО будет обрабатывать ошибки, когда вы будете раскладывать его на более малые части. И, конечно же, об этом тоже легче говорить, чем делать.
«Я решил, хоть и с большой неохотой, использовать LATEX».— Джоуи Армстронг. Создание распределенных систем, надежно работающих при наличии в них программных ошибок. 2003 г.
Erlang/OTP весьма уникален в плане способа обработки ошибок, который называется «деревья контроля». Говоря в общих чертах, каждых процесс в Эрланг-системах запускается и наблюдается супервизором. Когда процесс сталкивается с проблемой, он прекращает свою работу, после чего сразу же перезапускается супервизором. Что же касается самих супервизоров, то они запускаются начальным процессом, который также осуществляет их перезапуск, когда со сбоем сталкиваются уже они.
Ключевая идея состоит в том, что работа по схеме «ошибка-перезапуск» проходит быстрее по сравнению с попытками обработки ошибок. Подобное обращение со сбоями, когда надежность достигается путем отказа от решения возникшей проблемы, может показаться контринтуитивным, однако на практике метод выключения и перезапуска оказывается очень эффективным в деле подавления разовых и преходящих сбоев.
Обработка ошибок и восстановление лучше всего выполнять на внешних уровнях вашей кодовой базы. По-другому это называется принципом взаимодействий двух оконечностей. Он гласит, что обрабатывать ошибки легче на двух дальних концах связующей среды, нежели где-либо в ее середине. Это связано с тем, что даже если работа над ошибкой происходит где-то посередине, вам, так или иначе, придется делать проверку и на пограничных уровнях. Если каждому верхнему уровню все равно придется обрабатывать ошибки, то зачем же тогда делать это еще и где-то внутри программы?
Обработка ошибок — один из множества способов, с помощью которых система может оказаться тесно связанной внутри. Существует немало других примеров тесной связанности, и все же выделять какой-то один в качестве плохого было бы нечестно. Кроме IMAP.
В IMAP практически каждая операция, словно снежинка, обладает уникальными параметрами и алгоритмом обращением. Обработка ошибок становится очень неприятным процессом: ошибки могут появиться прямо посередине выполнения другой операции.
Вместо UUIDs, IMAP генерирует уникальный токен для идентификации каждого сообщения. Последний также может измениться прямо во время выполнения другой операции. Многие операции можно поделить на части. Потребовалось 25 лет для изобретения способа, который позволял бы надежно перемещать электронные письма из одной папки в другую. И, конечно, нельзя не отметить применения в нем весьма специфичных кодировок UTF-7 и base64.Нет, я ничего не выдумываю.
Для сравнения: как файловая система, так и база данных представляют собой гораздо лучшие примеры удаленного хранилища. Файловая система предлагает фиксированный набор операций, однако набор объектов, над которыми вы можете их производить, велик и весьма разнообразен. Может показаться, что интерфейс SQL обладает более широкими по сравнению с файловой системой возможностями. Тем не менее, он использует ту же схему работы: есть некоторое количество операций для работы с набором данных и огромное количество строк, над которыми эти операции проводятся. И хотя вы не всегда можете сделать замену одной базы на другую, найти решения, которые работали бы с SQL гораздо проще, чем аналогичные решения для любого кустарного языка запросов.
В качестве других примеров слабой связанности можно привести системы, использующие межплатформенное ПО или фильтры и конвейеры. Finagle твиттера использует обычный API для сервисов, и это позволяет без каких-либо лишних усилий добавить базовую обработку таймаутов, механизмов повторного соединения и проверки подлинности. И, конечно, я не могу не упомянуть в связи с этим конвейер UNIX. Это вызвало бы большое негодование.
Итак, сначала мы разделили свой код на уровни, однако теперь уже некоторые из этих уровней вместе используют один интерфейс: некий общий набор поведений и операций, пригодный для самых разных вариантов применения. Хорошими примерами слабой связанности часто оказываются однородные интерфейсы.
Правильная кодовая база совсем необязательно должна быть идеально поделена на модули. Просто модульность делает процесс написания кода гораздо более интересным. Это как детали Lego, играть с которыми интересно, потому что они подходят друг к другу. Здоровая кодовая база всегда обладает небольшим избытком функциональности, а также ровно таким расстоянием между движущимися частями, чтобы ваши руки в них не застряли.
Слабосвязанный код совсем необязательно легко удалить, однако заменить его или внести в него изменения всегда значительно легче.
Шаг 7: Продолжайте писать код
Умение писать код без оглядки на ранее написанные строки серьезно облегчает процесс экспериментирования с новыми идеями. Нет, я не говорю, что вам теперь всегда нужно стараться писать микросервисы вместо монолитов, но ваша система должна позволять вам провести один или два эксперимента поверх вашей основной работы.
Feature flags — один из способов изменить принятые ранее решения. Несмотря на то, что feature flags многими воспринимаются как способ экспериментирования с новыми возможностями, они также позволяют вам добавлять изменения без повторного развертывания новой версии.
Google Chrome — потрясающий пример положительных моментов, которые они в себе несут. Разработчики Chrome поняли, что самым сложным моментом поддержки регулярного цикла релизов была большая трата времени на объединение существующих давно feature-ветвей.
Возможность включить или выключить новый код в любой момент без его повторной компиляции позволяет разбивать крупные изменения на более мелкие слияния без нанесения ущерба существующему основному коду. Кроме того, благодаря заблаговременному появлению новых фич в той же самой кодовой базе, команда получила возможность предвидеть ситуации, когда разработка долгоживущей фичи повлияет на другие части кода.
Feature flag — непросто параметр командной строки. Это способ разделения feature-релизов от объединяемых ветвей или от основного кода. Возможность поменять свое решение прямо во время работы программы становиться все более важным в условиях, когда выпуск нового ПО может занимать часы, дни или недели. Спросите любого главного инженера по отказоустойчивости, и он скажет вам, что если система «будит» вас посреди ночи, значит она определенно должна предусматривать внесение изменений по ходу работы.
Речь в этом шаге идет не столько об итерациях как таковых, сколько о необходимости иметь петлю обратной связи. Не столько о написании многоразовых модулей, сколько о разделении компонентов для внесения в них изменений. Важно помнить, что введение изменений в основной код — это не только создание новых фич, но также и удаление старых. Писать расширяемый код — все равно что надеяться, что через три месяца с вашим проектом все будет хорошо. Написание кода, который вы сможете удалить — работа, основанная на противоположном предположении.
Стратегии, о которых я говорил выше по тексту — деление на уровни, изолирование, общие интерфейсы, композиция — призваны помочь вам не в том, чтобы написать хорошее ПО, но в том, чтобы создать такое ПО, которое способно меняться с течением времени.
«Вопрос управления, таким образом, заключается не в том, надо ли создавать пилотную систему и выбрасывать ее. Вы и так это сделаете.… Поэтому планируйте выбросить ее с самого начала; все равно так оно и получится». — Фред Брукс.
Конечно, это не значит, что вам надо выбрасывать абсолютно все, но некоторую часть удалить придется. Писать хороший код — не значит делать все правильно с первого раза. Хороший код — это просто унаследованный код, который не путается у вас под руками. И хороший код легко удалить.