Главная » Хабрахабр » Как поделить архитектуру и реализацию и не поругаться

Как поделить архитектуру и реализацию и не поругаться

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

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

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

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

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

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

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

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

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

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

Все метрики будут проходить путь backend-сервис -> telegraf -> Kafka -> InfluxDB. Допустим, мы выработали архитектуру: в качестве хранилища будем использовать InfluxDB, передавать метрики по сети с помощью UDP на telegraf, а для обхода проблемы недоступности хранилища будем складировать метрики в Kafka, реплицированную по нескольким серверам. Для записи метрик backend'ом решили написать модуль, реализующий функционал передачи метрик в telegraf с помощью UDP.

Эта подзадача имеет много вариантов решения и вопросов на которые нужно ответить: метрики будут отправляться синхронно или асинхронно; как будет синхронизироваться одновременный доступ нескольких потоков backend'а, какие будут основные классы/функции. Модуль для записи метрик — это отдельный компонент системы, его написание — отдельная подзадача, которую можно поручить разработчику.

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

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

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

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

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

Проведение границы между этими двумя стадиями проектирования зависит от ряда факторов, вот основные из них:

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

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

Эта задача возникла в рамках выбранной архитектуры для новой epic-фичи. Компонент имеет строгое API
Например, в моей практике была задача: реализовать поверх UNIX-сокета API для захвата/высвобождения ресурсов ОС, используемых существующим демоном. В рамках архитектуры было достаточно высокоуровнево описать API, а детальное проектирование было сделано позже, во время реализации.

Модуль, выделяемый в отдельную подзадачу, не должен быть слишком большим. Модуль/класс с заданным интерфейсом
Самый простой способ делегировать проектирование части монолитной системы — выделить некоторый модуль или класс, описать его интерфейс и решаемые задачи. С другой стороны, задача реализации слишком маленького класса окажется тривиальной. К примеру, клиентская библиотека для доступа к шардированной базе данных несомненно является отдельным модулем, но задачу реализации этой библиотеки будет сложно оценить по трудозатратам без более детального проектирования. Например, если возникает подзадача "реализовать функцию, проверяющую существование заданной папки по заданному пути", то архитектура явно продумана слишком детально.

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

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

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

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

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

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

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

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

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


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Крупнейший дамп в истории: 2,7 млрд аккаунтов, из них 773 млн уникальных

Каждый может проверить там свой email на предмет утечки. Известный специалист по безопасности Трой Хант уже несколько лет поддерживает сайт Have I Been Pwned (HIBP) с миллионами записей об украденных аккаунтов. Но он никогда не видел, чтобы на продажу выставляли ...

Подборка @pythonetc, декабрь 2018

Это седьмая подборка советов про Python и программирование из моего авторского канала @pythonetc. Предыдущие подборки: Множественные контексты Иногда бывает нужно запустить какой-то блок кода в нескольких менеджерах контекста: with open('f') as f: with open('g') as g: with open('h') as h: ...