Вашему вниманию предлагается перевод поста Гергелия Ороса, занимающего должность Engineering Manager в Uber. В нем он делится своим взглядом на проектирование крупномасштабных систем, основанном на собственном практическом опыте работы в Uber и Microsoft. В сочетании с комментариями на Hacker News, которые добавляют весомые контр-аргументы и дополняют точку зрения автора, его статья стала одним из самых интересных постов недели. В статье используется термин «дизайн кода» для сравнения с традиционной «архитектурой» — о нем подробнее можно прочитать здесь.
На мою долю выпало достаточно опыта в проектировании и создании крупномасштабных систем. Я принимал участие в переписывании распределенной системы платежей в Uber, проектировании и релизе Skype на Xbox One и выпуске в открытый доступ RIBs — мобильного архитектурного фреймворка, созданного в Uber. Все эти системы имели тщательно продуманный дизайн, прошли через несколько итераций, с ними связано множество совещаний, проведенных у маркерной доски, и других обсуждений. Затем придуманный дизайн сводился к дизайн-документу, который распространялся среди других разработчиков для сбора дополнительной обратной связи, который продолжался до тех пор, пока мы не переходили к разработке.
Все эти системы отличали большие масштабы: их создавали сотни разработчиков — или они использовали их в своих разработках — и сегодня они бьются в сердцах систем, которыми ежедневно пользуются миллионы людей. Причем, эти проекты создавались не с нуля. Система платежей должна была заменить две другие существующие платежные системы, используемые десятками других систем и дюжинами команд, и все это — без какого-либо ущерба для бизнеса. Переписывание приложения Uber было проектом, над которым одновременно работало несколько сотен инженеров — он включал в себя портирование всей существующей функциональности на новую архитектуру.
Позвольте мне начать с того, что может прозвучать неожиданно. Во-первых, мы никогда не пользовались стандартными средствами планирования архитектуры ПО для работы над дизайнами этих систем. Мы не использовали ни UML, ни модель 4+1, ни ADR, ни C4, ни диаграммы зависимостей. Мы рисовали множество диаграмм, но ни одна из них не следовала никаким строгим правилам. Нужны были лишь старые добрые прямоугольники и стрелочки — и мы получали диаграммы, похожие на эту, описывающую информационные потоки, или другую, изображающую структуру класса и отношения между компонентами. Две диаграммы в одном дизайн-документе часто имели разный стиль, поскольку зачастую они добавлялись или редактировались разными людьми.
Во-вторых, у нас не было архитекторов, которые владели бы дизайном. Не было ни IT Architect, ни Enterprise Architect. Это правда — ни в Uber, ни в Skype/Mircrosoft нет штатных позиций для архитекторов ПО. Ожидается, что инженеры высших уровней — например, в должности Staff Engineer — по-прежнему регулярно пишут код. На всех наших проектах есть опытные инженеры — однако, ни один человек не владеет архитектурой или дизайном единолично. В то время, как опытные инженеры являлись движущей силой процесса дизайна, в обсуждение были вовлечены и остальные разработчики, включая даже зеленых «джунов» — причем они часто оспаривали решения других и выносили на обсуждение альтернативные варианты.
В-третьих, мы практически никогда не ссылались на распространенные архитектурные паттерны и другой общепринятый жаргон, который используется в популярной литературе по архитектуре ПО вроде руководства Мартина Фаулера. Не было упоминаний микросервисов, serverless-архитектуры, границ приложений, событийно-ориентированной архитектуры (event-driven architecture) и всего остального. Некоторые из этих терминов всплывали во время сессий мозгового штурма; однако, у нас не было никакой необходимости ссылаться на них самих в проектной документации.
Проектирование ПО в технологические компаниях и стартапах
Так как же мы со всем справились? И почему мы не стали следовать распространенным подходам к архитектуре ПО?
Я обсуждал этот вопрос с коллегами-инженерами, работающими в других технологические компаниях — как размеров FANG (Facebook, Amazon, Netflix, Google), так и мелких стартапов. Большинство команд и проектов — неважно, были ли они большими или маленькими — объединял схожий подход к дизайну и реализации:
- Начните с бизнес-задачи. Какую проблему мы пытаемся решить? Какой продукт мы пытаемся создать и почему? Как мы cможем измерить его успешность?
- Устройте мозговой штурм для выбранного «подхода». Соберитесь с командой и за несколько сессий найдите рабочее решение. Не стоит слишком затягивать с этой фазой. Начните с верхнего уровня и постепенно опускайтесь вниз на уровни ниже.
- Изобразите ваш подход на доске. Соберите команду вместе, и пусть один из вас изобразит подход, к которому склоняется команда. Вы должны быть способны понятно объяснить архитектуру вашей системы/приложения при помощи доски — начиная с самого верхнего уровня и по необходимости спускаясь все ниже и ниже. Если у вас возникают трудности с каким-либо объяснением или оно недостаточно понятно для коллег, то вам нужно лучше проработать детали своего решения.
- Зафиксируется его в виде простой документации с простыми диаграммами, основанными на том, что вы ранее объясняли при помощи вайтбординга. Сведите жаргон на минимум: вы хотите, чтобы даже джуниоры могли понять, о чем идёт речь. Используйте в своём описании понятный и лёгкий язык. В Uber мы используем документ, напоминающий RFC, который основывается на простом шаблоне.
- Обсудите компромиссы и альтернативы. Хороший дизайн ПО и хорошая архитектура заключаются в правильном выборе компромиссов. Сам по себе ни один из выборов дизайна не является плохим или хорошим: все зависит от контекста и целей. Разделена ли ваша архитектура на различные сервисы? Упомяните, почему вы приняли решение не делать один большой сервис, к которого могли бы быть свои другие преимущества вроде более простого и быстрого деплоя. Вы решили расширить модуль или сервис с новой функциональностью? Вместо этого взвесьте возможность разработки отдельного сервиса или модуля, и какие плюсы и минусы будет нести данный подход.
- Распространите дизайн-документ внутри команды/организации и соберите обратную связь. Раньше в Uber мы рассылали все дизайн-документы ПО всем нашим инженерам — до того момента, пока наш штат не вырос до 2000 человек. Теперь, когда мы выросли до таких масштабов, мы по-прежнему стараемся охватить как можно большую аудиторию — но в то же время стали следить, чтобы уровень информационного шума не превышал допустимого предела. Стимулируйте других задавать вопросы и предлагать альтернативы. Будьте прагматичны насчет временных лимитов на обсуждение фидбека и его включение в документ там, где это нужно. Конкретные пожелания можно применить сразу и быстро, в то время как детализированную обратную связь лучше разбирать с человеком лично на месте.
Почему наш подход отличается от того, что обычно упоминается в литературе по архитектуре ПО? На самом деле, наш подход принципиально не так уж сильно отличается от того, что описано в руководствах по архитектуре. Почти все руководства предлагают начинать с бизнес-задачи, а также с описания решений и компромиссов — мы тоже делаем это. Чего мы не делаем — так это не используем более сложные средства, за которые выступают многие архитекторы и книги по архитектуре. Мы документируем дизайн максимально просто, используя для этого самые простые и очевидные инструменты, вроде Google Docs или Office 365.
Я полагаю, что основное различие в нашем подходе сводится к инженерной культуре различных компаний. Высокая степень автономии и сведенная к минимуму иерархия — это черта, которая является общей у современных технологических компаний и стартапов; в случае с более традиционными компаниями, это далеко не всегда справедливо. Также это причина, почему в этих местах чаще прибегают к «дизайну, основанному на здравом смысле» вместо дизайна на основе процесса со строгими правилами.
Мне известны банки и автомобильные компании, где разработчиков активно отговаривают от того, чтобы принимать какие-либо архитектурные решения без того, чтобы подняться вверх по цепочке, получить одобрения от архитекторов на несколько уровней выше, которые наблюдают за несколькими командами. Это делает процесс медленнее, и при большом количестве запросов архитекторы становятся перегруженными. Поэтому эти архитекторы создают еще больше формальных документов, в надежде на то, что система станет более понятной — используя все те инструменты, которые описывает знакомая вам литература. Эти документы подкрепляются подходом «сверху вниз», поскольку он действует на инженеров запугивающим образом — ведь они не архитекторы, и не должны сомневаться или оспаривать решения, ведь они уже были задокументированы при помощи формальных методов, в которых они слабо разбираются. Поэтому они обычно так и не поступают. Честно говоря, эти компании поступают подобным образом для того, чтобы сделать разработчиков взаимозаменяемым ресурсом — ведь это позволяет им перекидывать людей с одного проекта на другой в сжатые сроки. Для вас наверняка не будет сюрпризом то, что для разных окружений лучше подходят различные инструменты.
Простой дизайн ПО без жаргонизмов вместо архитектурных паттернов
Целью проектирования системы должна быть простота. Чем проще система, тем проще она для понимания — и тем проще находить в ней проблемы, а также реализовывать ее. Чем яснее язык, которым она описана, тем доступнее ее дизайн. Избегайте пользоваться жаргоном, который не будет понятен каждому члену команды — даже самый неопытный из ваших коллег должен иметь возможность на уровне остальных понимать, о чем идет речь.
Чистый дизайн похож на чистый код: он легко читается и его просто понять. Существует много отличный способов написать чистый код. Однако, вы часто услышите что кто-либо предлагает начать с применения паттернов проектирования «банды четырех» к вашему коду. Чистый код начинается с принципа единственной ответственности, понятных наименований и простых для понимания соглашений. Эти принципы в равной степени применимы к чистой архитектуре.
Так в чем заключается роль архитектурных паттернов? Я нахожу их полезными — настолько же, насколько полезны и паттерны проектирования кода. Они могут подать вам идеи насчет того, как вы можете улучшить свой код или архитектуру. Взять паттерны проектирования кода: я отмечаю для себя, когда замечаю в коде синглтон — и поднимаю брови и копаю глубже, когда я вижу класс, который ведет себя как фасад, а на деле всего лишь пробрасывает вызовы. Но еще не наступил тот день, когда я бы подумал «здесь прямо-таки напрашивается абстрактная фабрика». По правде говоря, у меня ушло много времени на то, чтобы понять, что делает этот паттерн и прежде чем меня осенило; понимание пришло в результате плотной работы с инъекцией зависимостей (dependency injection) — одной из немногих областей, где этот паттерн широко распространен и крайне полезен. Я также признаюсь, что пусть я и потратил много времени на чтение и осознание паттернов проектирования «банды четырех», они имели гораздо меньшее влияние на мой профессиональный рост как программиста, чем фидбек, который я получил от других разработчиков относительно своего кода.
Аналогичным образом, знание распространенных архитектурных паттернов — это полезная штука: оно помогает сократить время обсуждений с теми людьми, кто понимает их тем же образом, что и вы. Но архитектурные паттерны не являются целью, и они не станут заменой для более простых вариантов дизайна систем. Когда вы проектируете систему, то можете вдруг осознать, что случайно применили некий распространенный паттерн — и это хорошо, ведь позже вам будет проще сослаться на использованный подход. Но никогда не стоит брать один или несколько паттернов и использовать их в качестве молотка, для которого вам будут везде мерещиться гвозди.
Архитектурные паттерны появились на свет, когда инженеры обратили внимание на то, что в определенных ситуациях имеет смысл принимать схожие решения относительно дизайна и его реализации. Со временем эти решения получили названия, были записаны и обсуждены широкой общественностью. Архитектурные паттерны — это инструменты, которые появились уже после того, как решения были найдены, в надежде на то, что это поможет сделать жизнь остальных проще. Как инженер, вы должны ставить своей целью самостоятельный поиск решений для своих задач и обучаться в ходе этого процесса — вместо того, чтобы брать «хайповый» архитектурный паттерн в надежде на то, что это решит вашу задачу.
Как научиться лучше проектировать системы
У меня часто спрашивают совета: как «прокачаться» в архитектуре и дизайне систем? Опытные инженеры обычно рекомендуют почитать про архитектурные паттерны, а также книги по архитектуре ПО. Я на все сто поддерживаю рекомендацию читать как можно больше — особенно книги, поскольку они позволяют погрузиться в тематику гораздо глубже, чем короткие посты — однако, у меня есть несколько более практических рекомендаций.
- Привлеките одного из членов команды и вместе займитесь вайтбордингом. Нарисуйте на доске то, над чем вы работаете, и объясните смысл изображенного. Убедитесь, что ваши коллеги понимают, о чем идет речь. И, когда вас наконец поймут, попросите обратную связь.
- Опишите ваш дизайн в виде простого документа и поделитесь им со своей командой, попросив у них обратную связь. Не имеет значения, насколько проста или сложна задача, над которой вы работаете — это может быть как небольшой рефакторинг, так и крупномасштабный проект — вы должны сжато резюмировать ее. Сделайте это таким образом, чтобы для вас это документ имел смысл, а для остальных — был понятен; для вдохновения — вот вам пример, как это делается в Uber. Поделитесь этим документом со своей командой в формате, который позволяет комментирование — например, при помощи Google Docs, Office 365 или чего-то подобного. Попросите других дополнить его своими соображениями и вопросами.
- Создайте два различных дизайна и противопоставьте их. Когда большинство из нас проектируют архитектуру, обычно они идут одним путем: тем, который всплывает в их голове. Однако, архитектура — это не что-то черно-белое. Придумайте второй вариант дизайна архитектуру, который тоже может сработать в вашей ситуации. Противопоставьте оба дизайна и объясните, почему один лучше другого. Укажите, что вы рассматривали второй вариант некоторое время в качестве альтернативы, и объясните, почему вы приняли решение не в его пользу.
- Определитесь насчет компромиссов, которые вы готовы сделать — выясните, зачем вы их принимаете, и чего хотите добиться с их помощью. Четко установите существующие ограничения, которые вы были вынуждены принять во внимание.
- Проводите ревью чужих дизайнов. Причем делайте это с умом. Если у вас в компании есть культура обмена дизайнами, которыми люди делятся при помощи вайтбординга, совещаний или документов — берите от них больше. Обычно во время ревью люди воспринимают чужой дизайн кода как наблюдатели; вместо этого, задавайте уточняющие вопросы для тех частей, которые вам не совсем понятны. Спросите, какие альтернативы они рассматривали. Спросите, на какие компромиссы они решились и какие ограничения они рассматривали. Побудьте адвокатом дьявола и предложите другую, возможно более простую альтернативу — даже если она не лучше их решения — и спросите, что они думают насчет вашего предложения. Пусть вы не настолько хорошо продумали свой дизайн по сравнению с тем, кто его презентует — ваше обсуждение по-прежнему может привнести что-то новое и поможет вам лучше понять задачу, которой вы занимаетесь.
Лучший дизайн ПО прост и легок для понимания. В следующий раз, когда вы будете начинать новый проект, вместо того, чтобы размышлять над вопросом "Как я буду проектировать эту систему, какие опробованные в бою паттерны я должен использовать и при помощи какой формальной методологии я должен ее задокументировать?", подумайте — "Как я могу прийти к наипростейшему возможному дизайну — такому, который будет легко понятен любому?".
Лучшие практики архитектуры ПО, архитектурные паттерны корпоративных приложений, формальные способы описания систем — все это инструменты, которыми полезно владеть, поскольку однажды они могут вам очень сильно пригодиться. Но при проектировании систем стоит начинать с простого и продолжать придерживаться максимальной простоты. Старайтесь избегать сложности, которую неизбежно привносят в ваши системы более сложные архитектуры и формальные инструменты.
Данный пост вызвал бурное обсуждение среди работников индустрии, которые поделились своим опытом и отношением к архитектуре ПО. Вы можете ознакомиться с этими дискуссиями на Hacker News, Lobste.rs и Reddit.
В то время, как большинство комментаторов согласно с основным посылом статьи, другие замечают, что в реальности подобный подход может быть слабо применим к миру «кровавого энтерпрайза», где архитекторы недаром «едят свой хлеб»; они задаются вопросом — как будет выглядеть спроектированная подобным образом система 20 лет спустя и вспоминают «теорию разбитых окон», а также рассказывают про интеграцию 300 IT-систем, которая по определнию не может быть простой — ведь каждая из них имеет уникальное API, часть систем работает на Коболе и каждую из них обслуживает от 5000 до 7000 операторов.