Я работаю в сфере разработки заказного программного обеспечения. Когда мы говорим о проектировании программного обеспечения, как правило перед нами всплывает такая картинка:
Такие схемы для нас готовят наши архитекторы. В принципе, изучая схему, мы можем получить информацию о системе в целом. Однако для разработчика, ответственного за конкретный модуль системы, эта схема несет лишь часть информации о соседних модулях, с которыми осуществляется взаимодействие. Разработчик не сможет получить информацию о составе и сложности каждого модуля, об объеме и трудоемкости работ в целом. Соответственно, такая схема не поможет при планировании работ.
Давайте поближе рассмотрим один из модулей такой системы, намеренно упростив задание для разработчика. Предположим, надо создать модуль для отправки уведомлений со следующими функциональными требованиями:
Отправка уведомлений должна осуществляться посредством SMS и e-mail ежеминутно.
Все уведомления должны храниться в БД с указанием времени отправки.
Простой функционал. Поручив такую работу неопытному разработчику, скорее всего, получим следующее незатейливое решение:
Согласно решению - модуль отправки уведомлений считывает сообщения из БД и отправляет их. Все просто, как и озвученные требования.
Но опытный разработчик посмотрит и скажет, что это плохое решение. В системе может быть много модулей: 10, 20, 100. Если каждый модуль будет обращаться к БД раз в минуту, это может вызвать перегрузку БД, и печальные последствия для системы. Нужно ограничить считывание из БД частотой 1 раз в час. Таким образом мы только что сформулировали первое требование, которое нигде не прозвучало явно. Это важное требование, от выполнения которого зависит стабильность нашей информационной системы:
Обращаться к БД не чаще чем 1 раз в час.
Закроем это требование добавлением кэша, который будет осуществлять выборку всех сообщений из БД на ближайший час и с которым будет работать модуль отправки уведомлений.
Однако и в истории с кэшем не все так просто. Если уведомление на отправку в ближайший час запишется прямиком в БД, то, вероятнее всего, его отправка не произойдет. Уведомление будет отсутствовать в кэше, поскольку выборка уже была произведена ранее. Соответственно, в БД сообщения должны поступать не напрямую, а через сервис отправки уведомлений. Там, где будет принято решение о добавлении новых сообщений в кэш. В итоге мы сформулировали еще одно требование, которое нигде не прозвучало явно:
Использовать REST API для работы с сообщениями.
В схему нашего решения добавляем кэш, модуль для работы с кэшем и REST API для работы с сообщениями.
После изучения схемы возникает вопрос: сколько сообщений будет отправляться? Они будут отправляться по одному? Если сообщений будет тысячи или десятки тысяч, то, отправляя сообщения по одному, может случиться так, что сервис отправки уведомлений может не успеть передать все данные о сообщениях за отведенный час. Соответственно, отправка уведомлений должна быть пакетной. Это критично для производительности системы.
К списку требований, которые нигде не были озвучены явно, добавляем еще одно:
Отправлять почтовые сообщения и SMS пакетами.
Из схемы также видно, что присутствует два временных интервала: ежеминутная выборка сообщений из кэша и ежечасная выборка из БД в кэш. Мы собираемся эти интервалы прописывать непосредственно в коде? А если пользователь в какой-то момент поменяет решение о ежеминутной отправке сообщений? Значит, интервалы отправки сообщений и актуализации кэша нужно хранить отдельно, где они могут быть изменены пользователем. Формируем новое требование:
Хранить интервалы отправки почтовых сообщений, SMS и выборки из БД в сервисе конфигураций.
У опытных разработчиков может возникнуть еще один вопрос: какое время ожидания (таймаут) если внешние сервисы или БД не работают? Следовательно, необходимо хранить информацию о таймаутах в сервисе конфигураций.
Открытым остается вопрос: куда будут отправляться сообщения об ошибках и метрики работы сервиса?
Добавляются еще требования:
Хранить в конфигурации системы таймауты доступа к БД и внешним сервисам.
Отправлять информацию об ошибках и метриках в сервисы логирования и мониторинга системы.
Далее возникает вопрос, как будут выдерживаться интервалы отправки уведомлений и загрузки данных из БД? Наш сервис будет крутить бесконечный цикл или нужно более техничное решение? Наверное, лучше всего добавить в систему какой-то таймер или использовать системный cron. Появляется еще одно неявное требование:
Использовать таймер или cron для работы сервиса.
Все изменения отражаем на нашей схеме. Добавляем сервис конфигураций и пометку о пакетной отправке уведомлений, информацию о таймаутах, внешние сервисы мониторинга, логирования и cron.
На схеме мы видим, что в системе используются общие компоненты: логирование, мониторинг, сервис конфигураций. Скорее всего, большинство модулей системы (возможно все) будут взаимодействовать с этими компонентами. Мы будем писать везде один и тот же код? Возможно, в будущем придется заменить сервисы логирования и мониторинга на другие. Что если для сервиса конфигураций придется использовать не собственный компонент, а готовый opensource? Необходимо устранить дублирование кода с помощью dependency injection (DI).
Добавляем еще одно требование к нашему списку:
Устранить дублирование кода при работе с сервисом конфигураций, логирования и мониторинга.
Внесем изменения в нашу схему.
Можно и дальше продолжать анализировать решение, добавляя новые требования.
Например:
Определить максимальный размер пакета уведомлений;
Использовать проверку сервиса на работоспособность – HealthCheck.
Уверен, что многие из вас могут добавить новые требования или поменять уже озвученные. Например, не затронуты вопросы безопасности, надежности и т.д. Но данная статья не об этом. Не о том, как проектировать решения, а о том, что оно дает.
Давайте приведем список явно озвученных и выявленных нами требований.
Функциональные требования:
Отправка уведомлений должна осуществляться посредством SMS и e-mail ежеминутно;
Информация обо всех уведомлениях должна храниться в БД с указанием времени отправки.
Неозвученные технические требования, которые были выявлены в процессе проектирования:
Обращаться к БД не чаще, чем 1 раз в час;
Использовать REST API для работы с уведомлениями;
Отправлять почтовые сообщения и SMS пакетами;
Хранить интервалы отправки почтовых сообщений, SMS и выборки из БД в сервисе конфигураций;
Хранить в конфигурации системы таймауты доступа к БД и внешним сервисам;
Отправлять информацию об ошибках и метриках в сервисы логирования и мониторинга системы;
Использовать таймер или cron для работы сервиса;
Устранить дублирование кода при работе с сервисом конфигураций, логирования и мониторинга;
Определить максимальный размер пакета уведомлений;
Использовать проверку сервиса на работоспособность – HealthCheck.
В итоге получаем следующий интересный расклад:
На 2 явно озвученных функциональных требования было сформулировано еще 10 неозвученных;
Обнаружили ранее не выявленную потребность в 4 дополнительных функциональных блоках сервиса: cron, кэш, REST API для работы с уведомлениями, HealthCheck;
Выявили потребность в 3 компонентах системы: сервис конфигураций, сервис логирования и сервис мониторинга (написанные самостоятельно или готовые решения).
Итог
В процессе даже поверхностного проектирования модуля нам удалось сформировать довольно внушительный список никем не озвученных требований, некоторые из которых довольно критичны. Мы выявили дополнительные функциональные компоненты создаваемой системы.
Приведенный пример позволил подсветить тот момент, где «живет опыт» сеньоров и экспертов по разработке. Чем меньше опыт, тем меньше технических требований будет выявлено на этапе проработки решения и тем больше вероятность сырого, недоработанного и проблемного решения.
Могло бы помочь такого рода проектирование неопытному разработчику в выявлении проблем еще до написания кода? Да. Как минимум, до старта работ схему решения и описание смогли бы оценить более опытные коллеги и внести корректировки, если потребуется. Схема вместе с описанием решения, помогла бы осуществить внешний технический аудит. Имея подобный артефакт на руках, можно попробовать нарезать задачу на микрозадачи, с которыми могла бы работать команда неопытных разработчиков.
Стоит ли тратить время на отрисовку подобных схем? Проработка текущего решения вместе с созданием схемы у автора заняла 30 мин. Для наглядности этой статьи, схемы рисовались в графическом редакторе, однако – это, могли бы быть и UML-диаграммы. А вообще, проектирование — это не про схемы. Прежде, чем начинать писать код хорошо бы подумать, что Вы будете делать. Вместо схемы Вы можете использовать текстовое описание. Главное понятно донести до команды суть вашего решения.
Описание, схема могут быть результатом коллективной работы. Создавая модуль или фичу каждый может внести свой вклад: изменения в БД, изменения в API, изменения связанных компонентов и т.д.
Думаю, вы согласитесь, что намного лучше сегодня потратить время на проработку и обсуждение с коллегами вариантов решения. Сегодня заложить требуемую гибкость и устойчивость к нагрузке, чем выявить проблемы и слабые места спустя месяц или год, когда решение уже в эксплуатации.
Проектирование с выявлением технических требований повышает прозрачность разработки, позволяет глубже понять состав работ и дать им адекватную оценку, что поможет осуществлять выверенное планирование.
И напоследок - такая схема, описание решения, займет достойное место в документации, облегчит подключение к проекту новых членов команды, обеспечит разработчикам понимание, повысит осознанность доработок и поможет избежать наслоений логики (известная проблема, когда исправления вносятся без глубокого понимания работы кода).
Эпилог
Насколько часто я вижу такое предварительное проектирование на проектах, с которыми приходится иметь дело? Практически не вижу. Но часто встречаю проблемы, связанные с непродуманными решениями. Разработчики соглашаются с важностью такого проектирования, но обычно оно остается «в голове», а на отрисовку и оформление нет времени. Беда только в том, что у каждой «головы» есть ноги и в какой-то момент компетенции могут уйти вместе с разработчиком.
К сожалению, встречаются противники такого проектирования. Бизнесу важно снижение затрат на разработку и добавление еще одного этапа проектирования не всегда находит понимание. Но на мой взгляд, это игнорирование реальности. Разработчики и аналитики, в любом случае, продумывают свои решения и лучше, чтобы это происходило открыто.
Насколько необходимо проектирование (иногда его называет design, а процесс в разработке Design Review) и когда – решать вам. Все плюсы я постарался изложить в этой статье, ну а минусы вы найдете самостоятельно.
Комментарии (17)
sshmakov
29.11.2023 13:28+7Итог - вместо того, чтобы реализовать самый первый, самый простой, но жизнеспособный вариант, разработчики первый месяц занимались каким-то проектированием...
Кстати, прочитав исходные два требования, я не увидел, что уведомления должны браться из БД. Я их понял так, что в БД нужно сохранять лишь факт отправки уведомлений.
iggr63
29.11.2023 13:28+1еще одного этапа проектирования
Хорошо проиллюстрировано почему нужен второй этап проектирования. И даже отношение технических требований первого и второго уровней довольно типично. И конечно да можно проектировать до бесконечности, но обычно трех согласлованных уровней хватает : требования заказчика, спецификации системы и ее компонент.
Yuri0128
29.11.2023 13:28+1Ну и описан вариант недоработки второго этапа при разработке архитектуры. Я, кстати, из текста тоже понял, что база только для логирования "отправок", сижу и думаю, чем может нагрузить добавление одной записи в минуту? Потом ТС разъяснил, что и сам не вьехал (ну когда писал). Ну и непонятно, что за такой кэш (ну и как он так по глупому построен), что может "потерять" запись, пускай даже на время? В общем - неоднозначно все у ТС-а...
А целом - типичная задача ну и решений полно, да у загашнике у каждого более-менее "долгого" спеца есть свое собственное.
onets
29.11.2023 13:28+3Откуда взялось 10-20-100 модулей, которые читают уведомления и отправляют их? Зачем дублировать код отправки в 100 местах? Это должен быть один модуль, с настраиваемым кол-вом одновременных потоков, от 1 до n. И уже экспериментальным путем подбирать оптимальное кол-во потоков. Зачем кэш?
Короче, ничего не понятно с самого начала.
onets
29.11.2023 13:28Перечитал еще раз. Малец хаос. Уведомления - это журнал, все туда пишут, когда им захочется, пачками или по одному. И отдельный поток (или несколько) это дело читает и рассылает. Тоже по одному или пакетом.
Можно прикрутить rest api, чтобы пополнять журнал, а можно подписаться на брокера сообщений.
На первое время журнал можно сделать в виде таблицы в реляционной БД. Когда перестанет хватать - перейти на кафку. Но это случится,
когдаесли проект станет очередным фейсбуком.Если надо как в UDP (отправил и забыл) - то никакого логирования, только указатель на последнюю прочитанную запись из журнала или флаг "обработано".
Если надо как в TCP - тогда да, придется писать логи, что было отправлено, что нет, последнюю ошибку, политики повторов и так далее. Но тут сразу проблемы с пакетной отправкой - из пакета в 100 штук могут упасть только 10.
Никаких кэшей. Можно ежедневно мониторить размер не обработанной очереди. Если она растет - значит мало серверов на отправку. Добавляем или настраиваем авто масштабирование в облаке по этому признаку в облаке.
synoptic555 Автор
29.11.2023 13:28Другие модули не отправляют сообщения. Об этом нет речи в статье.
Не придирайтесь к описанию решения описан гипотетический пример, он не был взят из жизни. Статья о том что простое: "сесть и подумать до написания кода" вскрывает много подводных камней.
RomanSeleznev
29.11.2023 13:28+1Прошу не воспринимать как критику, просто информация к размышлению.
Но опытный разработчик посмотрит и скажет, что это плохое решение. В системе может быть много модулей: 10, 20, 100. Если каждый модуль будет обращаться к БД раз в минуту, это может вызвать перегрузку БД, и печальные последствия для системы.
Вот тут я бы попросил уточнить: а опытный разработчик из этой истории сам примет решение относительно требований к системе (получил задачу и ушёл творить)? Если да, то это очень плохо, т.к. надо было бы подключать заказчика, архитектора, аналитика или кого-то, кто владеет информацией о реальных потребностях.
Иначе может так статься, что вы будете стрелять пушкой по воробьям, проектируя максимально надёжное и высконагруженное решение, хотя на деле потребность может оказаться 0.01 ops. Тут как раз архитектор (или кто-то иной из названных выше товарищей) и мог рассказать о нефункциональных требованиях.
На 2 явно озвученных функциональных требования было сформулировано еще 10 неозвученных;
Вот те 10 требований по факту можно рассматривать как проектное решение. Заказчику эти вещи, видимо, были не сильно интересны на входе в проект. При наличии обратной связи от него что-то и могло потом "просочиться" в требования. По крайней мере я не увидел по тексту, что кто-то потом подтвердил необходимость в оных.
А вообще, проектирование — это не про схемы... Вместо схемы Вы можете использовать текстовое описание. Главное понятно донести до команды суть вашего решения.
И да, и нет. Естественные языки страдают от избыточности и неоднозначности, вот тут диаграммы (особенно в понятных нотациях) и полезны. Безусловно, одних диаграмм не бывает достаточно, слова потребуются, т.к. хорошо, когда эти 2 бойца работают в паре.
Закроем это требование добавлением кэша...
Это и последующие решения я бы согласовывал с архитектором. Может оказаться, что кэш нужен вполне конкретный, что он уже есть в ИТ-ландшафте и нужно использовать именно его, не привнося ничего своего; а что-то может вообще противоречить целевой архитектуре и архитектор просто поставит крест компоненте; логирование же может быть просто ненужным, т.к. сервис не рассматривается критичным (отправка и доставка сообщений не является критерием успеха) и пр.
P.S. Допускаю, что, на самом деле, всё это было учтено и сделано, просто в тексте в целях сокращения материала эти моменты были пропущены. Мне лично их не хватило. Успехов!
synoptic555 Автор
29.11.2023 13:28Статья не про проектирование, а про то что оно дает разработчику.
О том что даже простые требования, в реальности таковыми не являются. К.Вигерс в своей книге это хорошо подмечает.
Конечно, здесь не затрагиваются вопросы сбора и согласования требований заказчика, согласование решения с архитектором и т.д. Не об этом статья.
Zimnuk
29.11.2023 13:28Проблема в том что проектирование в вакууме, без согласований с заказчиком, архитектором и т.д. приведет к потере времени и денег. Можно спроектировать отличную распределенную систему, написать идеальный код, для сайта цветочного магазина у остановки, но зачем ему все это?
Проектирование это инструмент, общение с заказчиком тоже своего рода инструмент. умение пользоваться одним инструментом без использования другого приводит к забиванию гвоздей микроскопом.
Нельзя отрывать проектирование от сбора требований от заказчика и согласованности решения с другими командами.
timintim
29.11.2023 13:28А где схемы создавали?
А если через UML описывать - сильно сложнее/дольше получается? Как будто им обычно паттерны всякие описывают (но могу ошибаться)
synoptic555 Автор
29.11.2023 13:28Для статьи рисовал в Sketch - он быстро и точно позволяет все эти кубики расставлять. Есть веб-аналог Figma. UML, BPMN и вообще любые диаграммы очень быстро и просто рисуются в web draw.io ( app.diagrams.net )
Batalmv
29.11.2023 13:28+1Первая схема - это для печати на А2 и где-то повесить, чтобы было.
--------------------------
Для разработчика конкретно что нужно:
Описание контракта сервиса, который он делает.
Понимание стека
Иные соглашения (наименования, логирования, безопасности, ...)
Нефункциональные требования к сервису
Чтобы к этому прийти, архитетор и ДЕЛАЕТ разработку архитектурного решения. Как именно, если кратко, вам сюда: The C4 model for visualising software architecture. Там указан роадмап, как рисовать схемы. Если и другие ресурсы, но идеи схожи. Можете погружаться и копать вширь и вглубь
Схемы являются результатом проектирования и позволяют проверить, что решение в целом будет работать и будет соответствовать нефункциональным требованиям, которые собираются на первом этапе.
Но не схемы критичны, как таковые - а подход, о чем вы и написали в конце статьи. Но, к моменту проектирования конкретного сервиса у вас уже должно все остальное быть "на борту". Это странно, в этом момент уже думать о том, как делать кеш, многопоточность, идемпотентность и т.д.
Ну и в вашем описание этот "сервис" все время путается под ногами архитектора. Вы либо сначала "архитетктурите", а потом уже сервисы, либо делаете одновременно, получая каждый новый сервис, который базируется на чем-то своем или улучшенном. Либо так статья написана, типа на негативном примере, что ли
HarryFox
29.11.2023 13:28Это не "неявные требования", а аналитические вопросы, требующие соответствующей проработки. Большая часть вопросов, к слову, может отсеяться после получения контекста о требуемой системе.
Более того, никто не запрещает сделать на коленке первый вариант который уже через неделю будет решать бизнесовые задачи, и развить его по мере необходимости в нужный (!), а не предполагаемо нужный вариант.Так, значит, раздуваются требования к системе в заказной разработке? А клиент "просто хотел получать уведомление когда у него купили курс".
Greesha
А потом приходит заказчик и говорит: я просто хотел получать уведомления каждый раз, когда кто-то покупает мой онлайн-курс "Keep it Simple, Stupid!"
Cels
+
С таким подходом, можно проект никогда не закончить. Хорошо, если это будут оплачивать.
nekt
Заказчику это может быть на самом деле не надо.
А может быть он не в курсе, надо это ему или нет.
И дело специалиста, профессионала в своей области, показать заказчику что тут надо сделать, а потом убрать те части, которые заказчик посчитает ненужными - когда они понадобятся заказчику, тогда и запилим.