Привет, друзья. В преддверии запуска курса «Backend разработчик на PHP», традиционно делимся с вами переводом полезного материала.
Программное обеспечение решает все больше и больше повседневных задач, при этом становясь все сложнее и сложнее. Как однажды сказал Марк Андрессен, оно поглощает мир.
В результате в течение последних нескольких лет подходы к разработке и поставке приложений серьезно изменились. Это были сдвиги тектонического масштаба, которые в результате привели к появлению набора принципов. Эти принципы оказались полезными в формировании команды, проектировании, разработке и доставке вашего приложения конечным пользователям.
Принципы могут быть обобщены в следующем виде: приложение должно быть маленьким, сетевым и иметь архитектуру, ориентированную на разработчика. Опираясь на эти три принципа вы можете создать надежное, комплексное приложение, которое может быть быстро и безопасно доставлено конечному пользователю, а также легко масштабируется и расширяется.
Каждый из предложенных принципов имеет ряд аспектов, которые мы обсудим, чтобы показать, как каждый принцип способствует достижению конечной цели, которая заключается в быстрой доставке надежных приложений простых в обслуживании и использовании. Мы будем рассматривать принципы в сравнении с их противоположностями, дабы прояснить, что значит, допустим, «Убедитесь, что вы используете принцип малости».
Мы надеемся, что эта статья побудит вас использовать предложенные принципы построения современных приложений, которые обеспечат единый подход к проектированию в контексте непрерывно растущего стека технологий.
Применив эти принципы, вы обнаружите, что пользуетесь последними тенденциями в разработке программного обеспечения, включая подход DevOps к разработке и доставке приложений, использование контейнеров (например, Docker) и фреймворков для оркестрации контейнеров (например, Kubernetes), использование микросервисов (включая Микросервисную Архитектуру NGINX и архитектура сетевой связи для микросервисных приложений.
Что такое современное приложение?
Современные приложения? Современный стек? Что именно означает «современный»?
Большинство разработчиков имеют лишь общее представление о том, из чего состоит современное приложение, поэтому необходимо дать четкое определение этому понятию.
Современное приложение поддерживает несколько клиентов, будь то пользовательский интерфейс на библиотеке JavaScript React, мобильное приложение под Android или iOS, или приложение, которое соединяется с другим по API. Современное приложение подразумевает наличие неопределенного количества клиентов, для которых оно предоставляет данные или сервисы.
Современное приложение предоставляет API для доступа к запрашиваемым данным и сервисам. API должно быть неизменным и постоянным, а не написанным конкретно под специфический запрос с какого-то конкретного клиента. API доступен по HTTP(S) и обеспечивает доступ ко всему функционалу, который есть в GUI или CLI.
Данные должны быть доступны в общепринятом, совместимом формате, таком как JSON. API предоставляет объекты и службы в понятной, организованной форме, к примеру, RESTful API или GraphQL обеспечивают достойный интерфейс.
Современные приложения строятся на современном стеке, а современный стек – это тот стек, который поддерживает такие приложения, соответственно. Такой стек позволяет разработчику с легкостью создавать приложение с HTTP интерфейсом и четкими конечными точками API. Выбранный подход позволит вашему приложению легко принимать и отправлять данные в формате JSON. Другими словами, современный стек соответствует элементам Двенадцати-Факторного приложения для микросервисов.
Популярные версии этого типа стека основаны на Java, Python, Node, Ruby, PHP и Go. Микросервисная Архитектура NGINX олицетворяет пример современного стека, реализованного на каждом из упомянутых языков.
Обратите внимание, что мы не пропагандируем исключительно микросервисный подход. Многие из вас работают с монолитами, которые должны развиваться, в то время как другие имеют дело с SOA приложениями, которые расширяются и развиваются, чтобы стать микросервисными приложениями. Третьи движутся в направлении применения безсерверных (serverless) приложений, а некоторые внедряют комбинации из вышеперечисленного. Принципы, изложенные в статье применимы к каждой из этих систем лишь с некоторыми незначительными изменениями.
Принципы
Теперь, когда мы добились общего понимания о том, что же такое современное приложения и современный стек, настало время погрузиться в принципы архитектуры и разработки, которые сослужат вам неплохую службу в разработке, реализации и поддержке современного приложения.
Один из принципов звучит как «создавайте маленькие приложения», давайте просто назовем его принципом малости. Существуют невероятно сложные приложения, которые состоят из большого количества подвижных компонентов. В свою очередь построение приложения из маленьких дискретных компонентов упрощает его проектирование, обслуживание и работу с ним в целом. (Заметьте, мы сказали «упрощает», а не «делает простым»).
Второй принцип заключается в том, что мы можем увеличить продуктивность разработчиков, помогая им сосредоточиться на тех функциях, которые они разрабатывают, при этом освобождая их от забот об инфраструктуре и CI/CD во время реализации. Итак, в двух словах, наш подход ориентирован на разработчиков.
Наконец, все, что касается вашего приложения, должно быть подключено к сети. За последние 20 лет мы сильно продвинулись к сетевому будущему, поскольку сети стали быстрее, а приложения сложнее. Как мы уже выяснили, современное приложение должно использоваться по сети множеством различных клиентов. Применение сетевого мышления в архитектуре имеет значительные преимущества, которые хорошо сочетаются с принципом малости и концепцией подхода, ориентированного на разработчиков.
Если при разработке и внедрении приложения вы будете помнить об указанных принципах, у вас будет неоспоримое преимущество в развитии и поставке своего продукта.
Давайте рассмотрим эти три принципа более детально.
Принцип малости
Человеческому мозгу сложно воспринимать одновременно большое количество информации. В психологии термин когнитивная нагрузка означает общее количество умственных усилий, необходимых для сохранения информации в памяти. Снижение когнитивной нагрузки на разработчиков является приоритетным, поскольку в таком случае они могут сосредоточиться на решении проблемы, вместо того, чтобы удерживать в голове текущую комплексную модель всего приложения и разрабатываемые функции.
Приложения декомпозируются по следующим причинам:
Есть несколько способов снизить когнитивную нагрузку на разработчиков, и именно здесь вступает в игру принцип малости.
Итак, три способа понизить когнитивную нагрузку:
Уменьшение временных рамок разработки
Вернемся в те времена, когда методология
Самой большой переменой в процессе разработке приложений стало внедрение методологии agile. Одна из основных особенностей методологии
Смещение фокуса с массивного приложения на конкретные маленькие функции, которые могут быть завершены за двухнедельный спринт, с заглядыванием вперед не более чем на одну функцию из следующего спринта в голове, является значительным изменением. Это позволило повысить продуктивность разработки при этом уменьшив когнитивную нагрузку, которая постоянно колебалась.
В методологии
Небольшие кодовые базы
Следующий шаг в уменьшении когнитивной нагрузки – это уменьшение кодовой базы. Как правило, современные приложения массивные – надежное, корпоративное приложение может состоять из тысяч файлов и сотен тысяч строк кода. В зависимости от организации файлов связи и зависимости кода и файлов могут быть очевидными или наоборот. Даже отладка самого выполнения кода может вызывать проблемы, в зависимости от используемых библиотек и того, насколько хорошо средства отладки разграничивают библиотеки/пакеты/модули и пользовательский код.
Построение рабочей ментальной модели кода приложения может занять внушительное количество времени, и вновь возложить на разработчика большую когнитивную нагрузку. Это особенно характерно для монолитных баз кода, где имеется большое количество кода, взаимодействие между функциональными компонентами которого четко не определено, а разделение объектов внимания часто размыто, поскольку не соблюдаются функциональные границы.
Одним из эффективных способов снижения когнитивной нагрузки на инженеров является переход к микросервисной архитектуре. В микросервисном подходе каждый сервис фокусируется на одном наборе функций; при этом смысл сервиса обычно определен и понятен. Границы сервиса также ясны – помните, о том, что коммуникация с сервисом осуществляется с помощью API, поэтому данные сгенерированные одним сервисом могут легко быть переданы в другой.
Взаимодействие с другими сервисами обычно ограничено несколькими пользовательскими сервисами и несколькими сервисами провайдера, которые используют простые и чистые API вызовы, например при помощи REST. Значит, когнитивная нагрузка на инженера серьезно снижается. Самой сложной задачей остается понимание модели взаимодействия сервисов и того, как такие вещи как транзакции происходят в нескольких сервисах. По итогу, использование микросервисов снижает когнитивную нагрузку, уменьшая количество кода, обозначая четкие границы сервиса и обеспечивая понимание отношений пользователей и провайдеров.
Маленькие инкрементальные изменения
Последний элемент принципа малости – это управление изменениями. Особым искушением для разработчиков считается посмотреть на базу кода (даже, возможно, на свой собственный, более старый код) и заявить: «Это дерьмо, нам нужно это все переписать.» Иногда это правильное решение, а иногда нет. Оно возлагает на команду разработчиков бремя глобального изменения модели, что, в свою очередь, приводит к масштабной когнитивной нагрузке. Лучше, чтобы инженеры сосредотачивались на изменениях, которые они могут внести в течение спринта, чтобы потом своевременно выкатить необходимый функционал, пусть и постепенно. Конечный продукт должен напоминать предварительно запланированный, но с некоторыми изменениями и тестированием, чтобы соответствовать потребностям клиента.
При переписывании больших частей кода иногда быстро осуществить доставку изменения оказывается невозможным, поскольку здесь вступают в игру другие зависимости системы. Для того, чтобы контролировать поток изменений, можно использовать отключение функционала (feature hiding). В принципе, это значит функционал есть на продакшне, но он недоступен при помощи настроек переменных среды (env-var) или какого-либо другого механизма конфигурации. Если код прошел все процессы проверки качества, то он может оказаться в продакшене в скрытом состоянии. Однако, эта стратегия работает только если функция в конечном итоге будет включена. В противном случае она будет лишь захламлять код и добавит когнитивной нагрузки, с которой придется справиться разработчику для продуктивной работы. Управление изменениями и инкрементальные изменения сами по себе помогают поддерживать когнитивную нагрузку разработчиков на доступном уровне.
Инженерам приходится преодолевать много сложностей даже при простом внедрении дополнительного функционала. Со стороны руководства разумным будет уменьшить лишнюю нагрузку на команду, чтобы она могла сосредоточиться на ключевых элементах функционала. Есть три вещи, которые вы можете предпринять, чтобы помочь своей команде разработчиков:
Если вы в своей работе примените принцип малости, ваша команда станет куда счастливее, лучше сфокусируется на реализации необходимых функций и с большей вероятностью будет быстрее выкатывать качественные изменения. Но это не говорит о том, что работа не может усложниться, иногда наоборот, внедрение нового функционала требует модификации нескольких сервисов и этот процесс может быть сложнее, чем аналогичный в монолитной архитектуре. В любом случае, преимущества от применения подхода малости того стоят.
Конец первой части.
В скором времени опубликуем вторую часть перевода, а сейчас ждем ваши комментарии и приглашаем на день открытых дверей, который пройдет уже сегодня в 20.00.
Программное обеспечение решает все больше и больше повседневных задач, при этом становясь все сложнее и сложнее. Как однажды сказал Марк Андрессен, оно поглощает мир.
В результате в течение последних нескольких лет подходы к разработке и поставке приложений серьезно изменились. Это были сдвиги тектонического масштаба, которые в результате привели к появлению набора принципов. Эти принципы оказались полезными в формировании команды, проектировании, разработке и доставке вашего приложения конечным пользователям.
Принципы могут быть обобщены в следующем виде: приложение должно быть маленьким, сетевым и иметь архитектуру, ориентированную на разработчика. Опираясь на эти три принципа вы можете создать надежное, комплексное приложение, которое может быть быстро и безопасно доставлено конечному пользователю, а также легко масштабируется и расширяется.
Каждый из предложенных принципов имеет ряд аспектов, которые мы обсудим, чтобы показать, как каждый принцип способствует достижению конечной цели, которая заключается в быстрой доставке надежных приложений простых в обслуживании и использовании. Мы будем рассматривать принципы в сравнении с их противоположностями, дабы прояснить, что значит, допустим, «Убедитесь, что вы используете принцип малости».
Мы надеемся, что эта статья побудит вас использовать предложенные принципы построения современных приложений, которые обеспечат единый подход к проектированию в контексте непрерывно растущего стека технологий.
Применив эти принципы, вы обнаружите, что пользуетесь последними тенденциями в разработке программного обеспечения, включая подход DevOps к разработке и доставке приложений, использование контейнеров (например, Docker) и фреймворков для оркестрации контейнеров (например, Kubernetes), использование микросервисов (включая Микросервисную Архитектуру NGINX и архитектура сетевой связи для микросервисных приложений.
Что такое современное приложение?
Современные приложения? Современный стек? Что именно означает «современный»?
Большинство разработчиков имеют лишь общее представление о том, из чего состоит современное приложение, поэтому необходимо дать четкое определение этому понятию.
Современное приложение поддерживает несколько клиентов, будь то пользовательский интерфейс на библиотеке JavaScript React, мобильное приложение под Android или iOS, или приложение, которое соединяется с другим по API. Современное приложение подразумевает наличие неопределенного количества клиентов, для которых оно предоставляет данные или сервисы.
Современное приложение предоставляет API для доступа к запрашиваемым данным и сервисам. API должно быть неизменным и постоянным, а не написанным конкретно под специфический запрос с какого-то конкретного клиента. API доступен по HTTP(S) и обеспечивает доступ ко всему функционалу, который есть в GUI или CLI.
Данные должны быть доступны в общепринятом, совместимом формате, таком как JSON. API предоставляет объекты и службы в понятной, организованной форме, к примеру, RESTful API или GraphQL обеспечивают достойный интерфейс.
Современные приложения строятся на современном стеке, а современный стек – это тот стек, который поддерживает такие приложения, соответственно. Такой стек позволяет разработчику с легкостью создавать приложение с HTTP интерфейсом и четкими конечными точками API. Выбранный подход позволит вашему приложению легко принимать и отправлять данные в формате JSON. Другими словами, современный стек соответствует элементам Двенадцати-Факторного приложения для микросервисов.
Популярные версии этого типа стека основаны на Java, Python, Node, Ruby, PHP и Go. Микросервисная Архитектура NGINX олицетворяет пример современного стека, реализованного на каждом из упомянутых языков.
Обратите внимание, что мы не пропагандируем исключительно микросервисный подход. Многие из вас работают с монолитами, которые должны развиваться, в то время как другие имеют дело с SOA приложениями, которые расширяются и развиваются, чтобы стать микросервисными приложениями. Третьи движутся в направлении применения безсерверных (serverless) приложений, а некоторые внедряют комбинации из вышеперечисленного. Принципы, изложенные в статье применимы к каждой из этих систем лишь с некоторыми незначительными изменениями.
Принципы
Теперь, когда мы добились общего понимания о том, что же такое современное приложения и современный стек, настало время погрузиться в принципы архитектуры и разработки, которые сослужат вам неплохую службу в разработке, реализации и поддержке современного приложения.
Один из принципов звучит как «создавайте маленькие приложения», давайте просто назовем его принципом малости. Существуют невероятно сложные приложения, которые состоят из большого количества подвижных компонентов. В свою очередь построение приложения из маленьких дискретных компонентов упрощает его проектирование, обслуживание и работу с ним в целом. (Заметьте, мы сказали «упрощает», а не «делает простым»).
Второй принцип заключается в том, что мы можем увеличить продуктивность разработчиков, помогая им сосредоточиться на тех функциях, которые они разрабатывают, при этом освобождая их от забот об инфраструктуре и CI/CD во время реализации. Итак, в двух словах, наш подход ориентирован на разработчиков.
Наконец, все, что касается вашего приложения, должно быть подключено к сети. За последние 20 лет мы сильно продвинулись к сетевому будущему, поскольку сети стали быстрее, а приложения сложнее. Как мы уже выяснили, современное приложение должно использоваться по сети множеством различных клиентов. Применение сетевого мышления в архитектуре имеет значительные преимущества, которые хорошо сочетаются с принципом малости и концепцией подхода, ориентированного на разработчиков.
Если при разработке и внедрении приложения вы будете помнить об указанных принципах, у вас будет неоспоримое преимущество в развитии и поставке своего продукта.
Давайте рассмотрим эти три принципа более детально.
Принцип малости
Человеческому мозгу сложно воспринимать одновременно большое количество информации. В психологии термин когнитивная нагрузка означает общее количество умственных усилий, необходимых для сохранения информации в памяти. Снижение когнитивной нагрузки на разработчиков является приоритетным, поскольку в таком случае они могут сосредоточиться на решении проблемы, вместо того, чтобы удерживать в голове текущую комплексную модель всего приложения и разрабатываемые функции.
Приложения декомпозируются по следующим причинам:
- Понижение когнитивной нагрузки на разработчиков;
- Ускорение и упрощение тестирования;
- Быстрая доставка изменений в приложении.
Есть несколько способов снизить когнитивную нагрузку на разработчиков, и именно здесь вступает в игру принцип малости.
Итак, три способа понизить когнитивную нагрузку:
- Уменьшить временные рамки, которые они должны учитывать при разработке новой функции – чем короче таймфрейм, тем ниже когнитивная нагрузка.
- Уменьшить количество кода над которым ведется единовременная работа – меньше кода – меньше нагрузка.
- Упростить процесс внесения инкрементальных изменений в приложение.
Уменьшение временных рамок разработки
Вернемся в те времена, когда методология
waterfall
был стандартом для процесса разработки, и временные рамки от шести месяцев до двух лет для разработки или обновления приложения были общей практикой. Как правило, сначала инженеры читали соответствующие документы, такие как требования к продукту (PRD), справочный документ системы (SRD), план архитектуры и начинали объединять все эти вещи вместе в одну когнитивную модель, в соответствии с которой они писали код. По мере того, как требования и, соответственно, архитектура менялись, приходилось прилагать серьезные усилия для информирования всей команды об обновлениях когнитивной модели. Такой подход в худшем случае мог просто парализовать работу.Самой большой переменой в процессе разработке приложений стало внедрение методологии agile. Одна из основных особенностей методологии
agile
– это итеративная разработка. В свою очередь, это приводит к снижению когнитивной нагрузки на инженеров. Вместо того, чтобы требовать от команды разработчиков реализации приложения за один долгий цикл, agile
подход позволяет сосредоточиться на маленьких объемах кода, которые можно быстро протестировать и развернуть, при этом получив еще и обратную связь. Когнитивная нагрузка приложения сместилась с таймфрейма от шести месяцев до двух лет с учетом огромного количества спецификаций на двухнедельное добавление или изменений функции, ориентированное на более размытое понимание о крупном приложении.Смещение фокуса с массивного приложения на конкретные маленькие функции, которые могут быть завершены за двухнедельный спринт, с заглядыванием вперед не более чем на одну функцию из следующего спринта в голове, является значительным изменением. Это позволило повысить продуктивность разработки при этом уменьшив когнитивную нагрузку, которая постоянно колебалась.
В методологии
agile
предполагается, что конечное приложение будет несколько измененной версией первоначальной концепции, поэтому окончательная точка разработки обязательно является неоднозначной. Ясными и четкими могут быть только результаты каждого конкретного спринта.Небольшие кодовые базы
Следующий шаг в уменьшении когнитивной нагрузки – это уменьшение кодовой базы. Как правило, современные приложения массивные – надежное, корпоративное приложение может состоять из тысяч файлов и сотен тысяч строк кода. В зависимости от организации файлов связи и зависимости кода и файлов могут быть очевидными или наоборот. Даже отладка самого выполнения кода может вызывать проблемы, в зависимости от используемых библиотек и того, насколько хорошо средства отладки разграничивают библиотеки/пакеты/модули и пользовательский код.
Построение рабочей ментальной модели кода приложения может занять внушительное количество времени, и вновь возложить на разработчика большую когнитивную нагрузку. Это особенно характерно для монолитных баз кода, где имеется большое количество кода, взаимодействие между функциональными компонентами которого четко не определено, а разделение объектов внимания часто размыто, поскольку не соблюдаются функциональные границы.
Одним из эффективных способов снижения когнитивной нагрузки на инженеров является переход к микросервисной архитектуре. В микросервисном подходе каждый сервис фокусируется на одном наборе функций; при этом смысл сервиса обычно определен и понятен. Границы сервиса также ясны – помните, о том, что коммуникация с сервисом осуществляется с помощью API, поэтому данные сгенерированные одним сервисом могут легко быть переданы в другой.
Взаимодействие с другими сервисами обычно ограничено несколькими пользовательскими сервисами и несколькими сервисами провайдера, которые используют простые и чистые API вызовы, например при помощи REST. Значит, когнитивная нагрузка на инженера серьезно снижается. Самой сложной задачей остается понимание модели взаимодействия сервисов и того, как такие вещи как транзакции происходят в нескольких сервисах. По итогу, использование микросервисов снижает когнитивную нагрузку, уменьшая количество кода, обозначая четкие границы сервиса и обеспечивая понимание отношений пользователей и провайдеров.
Маленькие инкрементальные изменения
Последний элемент принципа малости – это управление изменениями. Особым искушением для разработчиков считается посмотреть на базу кода (даже, возможно, на свой собственный, более старый код) и заявить: «Это дерьмо, нам нужно это все переписать.» Иногда это правильное решение, а иногда нет. Оно возлагает на команду разработчиков бремя глобального изменения модели, что, в свою очередь, приводит к масштабной когнитивной нагрузке. Лучше, чтобы инженеры сосредотачивались на изменениях, которые они могут внести в течение спринта, чтобы потом своевременно выкатить необходимый функционал, пусть и постепенно. Конечный продукт должен напоминать предварительно запланированный, но с некоторыми изменениями и тестированием, чтобы соответствовать потребностям клиента.
При переписывании больших частей кода иногда быстро осуществить доставку изменения оказывается невозможным, поскольку здесь вступают в игру другие зависимости системы. Для того, чтобы контролировать поток изменений, можно использовать отключение функционала (feature hiding). В принципе, это значит функционал есть на продакшне, но он недоступен при помощи настроек переменных среды (env-var) или какого-либо другого механизма конфигурации. Если код прошел все процессы проверки качества, то он может оказаться в продакшене в скрытом состоянии. Однако, эта стратегия работает только если функция в конечном итоге будет включена. В противном случае она будет лишь захламлять код и добавит когнитивной нагрузки, с которой придется справиться разработчику для продуктивной работы. Управление изменениями и инкрементальные изменения сами по себе помогают поддерживать когнитивную нагрузку разработчиков на доступном уровне.
Инженерам приходится преодолевать много сложностей даже при простом внедрении дополнительного функционала. Со стороны руководства разумным будет уменьшить лишнюю нагрузку на команду, чтобы она могла сосредоточиться на ключевых элементах функционала. Есть три вещи, которые вы можете предпринять, чтобы помочь своей команде разработчиков:
- Использовать методологию
agile
, чтобы ограничить временные рамки, в которые команда должна сосредоточиться на ключевых функциях. - Реализовать свое приложение в качестве нескольких микросервисов. Это ограничит количество внедряемых функций и укрепит границы, которые удерживают когнитивную нагрузку во время работы.
- Предпочтите инкрементальные изменения крупным и громоздким, изменяйте небольшие кусочки кода. Применяйте сокрытие функций, чтобы реализовывать изменения, даже если они не будут видны сразу после добавления.
Если вы в своей работе примените принцип малости, ваша команда станет куда счастливее, лучше сфокусируется на реализации необходимых функций и с большей вероятностью будет быстрее выкатывать качественные изменения. Но это не говорит о том, что работа не может усложниться, иногда наоборот, внедрение нового функционала требует модификации нескольких сервисов и этот процесс может быть сложнее, чем аналогичный в монолитной архитектуре. В любом случае, преимущества от применения подхода малости того стоят.
Конец первой части.
В скором времени опубликуем вторую часть перевода, а сейчас ждем ваши комментарии и приглашаем на день открытых дверей, который пройдет уже сегодня в 20.00.
evgenyk
Меня все мучает вопрос о границах применимости микросервисов. Ведь в некоторых случаях замена монолитного приложения микросервисами может понизить проихзводительность на порядки ИМХО.
DimNS
Всё потому что применять технологии нужно обдуманно, а не потому что это модно, стильно, современно.
А то обычно получается так: «ооо микросервисы, это же крутяк, надо срочно внедрять» а потом через n-времени «упс...»
Микросервисы не всегда полезны, всё правильно.
Опять же, а почему именно «замена монолита на микросервисы», почему не «микросервисы дополнительно к монолиту»
Допустим в вашем приложении есть расчёт конечной стоимости услуги с помощью сметы. которая находится в excel-файле и туда передаются параметры из формы, а в ответ оттуда получается конечная стоимость (реальный пример), вот это вот дело можно завернуть в микросервис, потому что он живет своей жизнью
А весь монолит переносить на микросервисы, ну такое… и тут на раз два можно в ногу выстрелить