«Большинство людей делают ошибку, думая о дизайне как о внешнем виде. Люди думают, что это видимость, что дизайнеру дают коробку и говорят: „Сделай так, чтобы она выглядела хорошо!“ Это не то, что мы называем дизайном. Дизайн — это не просто внешний вид или восприятие вещи. Дизайн — это то, как она работает» — Стив Джобс

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

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

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

Содержание

Что такое дизайн

«Вопросы о том, необходим ли дизайн или доступен, не имеют смысла: дизайн неизбежен. Альтернатива хорошему дизайну — плохой дизайн, а не отсутствие дизайна вообще» — Дуглас Мартин

Перед тем, как начать, нужно пояснить: что вообще понимается под дизайном. Определения из словарей:

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

  • План или намерение

  • Проектирование программного обеспечения.

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

Как мы пришли к проектированию до реализации

«Цель без плана — просто желание» — Антуан де Сент‑Экзюпери

Ошибки и несовершенства в коде могут быть выявлены до передачи задачи в тестирование благодаря практике code review. Проблема в том, что обычно правки вносятся в уже написанный код, что увеличивает стоимость разработки и время выпуска задачи. Более того, необходимость исправлений может демотивировать участников команды и даже приводить к конфликтам. В результате проверка может выявить не самый эффективный код, который при этом всё равно отправится в эксплуатацию, потому что «Ну он же работает» и «Что сделано, то сделано». И как следствие, с течением времени будет расти потребление ресурсов, а поддержка кодовой базы — усложняться.

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

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

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

Как мы проектировали сервис оповещений

«Хороший дизайн очень похож на ясное мышление, сделанное визуальным» — Эдвард Тафти

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

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

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

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

Как убедить вашего тимлида

«Реализация стабильного проекта стоит столько же, сколько реализация нестабильного» — Майкл Нейгард

Мне часто задают вопрос: «Как вы убедили вашего менеджера в необходимости проектировать дизайн и проводить его review?». Действительно, может показаться, что проектирование и проверка дизайна — это два дополнительных этапа, увеличивающие стоимость разработки.

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

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

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

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

«С точки зрения менеджера, важно отметить, что добавление этапа проработки дизайна делает выпуск задачи более прогнозируемым: до начала разработки становятся известны сроки выпуска и требуемые ресурсы» — Дмитрий, PM

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

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

Смотреть метрики

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

Время цикла в днях, перцентиль 0.8
Время цикла в днях, перцентиль 0.8
Время цикла в днях, перцентиль 0.99
Время цикла в днях, перцентиль 0.99

Для отсеивания выбросов мы используем перцентили. Время цикла по 0.8 перцентилю составило 23 дня в 2022 году и 12,5 — в 2023.

Как спроектировать дизайн и ничего не забыть

«Лучший путь предсказать будущее — это создать его» — Питер Друкер

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

FAQ предназначен для однократного прочтения при приёме в команду и фиксирует принятый подход к проектированию технического дизайна.

Шаблон содержит структуру документа и описание содержимого каждого из пунктов. Таким образом, разработчик копирует шаблон и заполняет каждый из пунктов в соответствии с описанием. Шаблон обеспечивает единую структуру документов и целостность процесса проектирования, фокусируя внимание на аспектах, которые должны быть спроектированы. Для описания дизайна и диаграмм мы используем форматы AsciiDoc и PlantUML. Такое сочетание позволяет рендерить документы и диаграммы в IDE и в репозитории проекта.

Ссылки на скачивание шаблона в форматах: AsciiDoc, pdf. Пишите в комментариях, насколько наш чек‑лист подходит для ваших проектов и чего в нём не хватает.

FAQ проектирования

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

Введение

Документ описывает правила подготовки дизайна задачи. Цель ‑ помочь разработчику при проектировании задачи и создании артефактов для DR. Наличие чек‑листа и общей структуры документа позволяет проверяющему правильно расставлять акценты и легче погружаться в контекст дизайна.

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

Основные правила

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

  • Реализовать задачу (в том числе писать код) нужно после проектирования и DR. Нарушение этого правила может привести к необходимости изменений в только что написанном коде.

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

Чек-лист проектирования

  • DDD. Убедиться, что реализуемая функциональность соответствует домену микросервиса и его взаимосвязям в связанном контексте.

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

  • Требования. Описать функциональные и нефункциональные требования к задаче.

  • API

    • Наименования. Сформулировать точные и лаконичные названия объектов и полей API, избегая путаницы у потребителей.

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

    • Изменения. Определить, являются ли изменения обратно совместимыми; в противном случае провести анализ использования.

  • База данных

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

    • Качество. Проверить целостность и достаточность данных для реализации задачи. Оценить риск порчи данных со временем и способы его минимизации.

  • Logic

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

    • Бизнес‑логика. Подробно описать алгоритмы, последовательности вызовов и компоненты, а также зависимости между ними.

Итерации DR

Последние два пункта документа формируются итеративно: описание дизайна отправляется на review, корректируется и вновь проверяется. Итеративный процесс завершается, когда у всех участников не остаётся критичных вопросов к дизайну задачи.

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

  • Результат design review, итерация 1

  • Решение после проверки, итерация 1

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

Заключение

«Чем раньше вы начнете писать код, тем дольше займет программа» — Рой Карлсон

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

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

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

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


  1. HiDiv
    02.12.2023 10:02
    +2

    Спасибо за интересный и подробный рассказ о Вашем опыте. Хотелось бы попробовать внедрить что-то подобное и у себя в команде...
    Я пока не разбирался подробно в самой описанной технологии, но есть несколько вопросов после прочтения самой статьи. Насколько я понял метрики оценки, дизайн занял 3 дня (правда не понятно сколько человек участвовало на этом этапе и сколько фактически человеко-часов было потрачено) и потом 4 разработчика реализовали доработку за 18 дней (4 * 18 * 8 = 576 человеко-часов). По мне, так это далеко не маленьких проект...

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

    Объясню суть. В моей практике, неоднократно, были задачи с оценкой, например, 8 человеко-часов или около того. Да, это могут быть довольно "простые" и небольшие по объему задачи, но они имеют место быть! Наверняка у Вашего подхода к дизайн-проекту есть какое-то минимальное время на выполнения... По ощущениям, это никак не меньше нескольких часов и это при варианте если участвует всего один человек, иначе на кодирование времени вообще не останется...

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


    1. vladislav-shikhanov Автор
      02.12.2023 10:02
      +1

      Спасибо за Ваш вопрос!

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

      Начну с общих соображений.

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

      • В общем случае цена исправления ошибочного решения увеличивается со сложностью задачи.

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

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

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

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

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

      Надеюсь, ответил на Ваш вопрос.