О темной стороне legacy-кода. Как решить проблемы с монолитными приложениями

Меня часто просят рассказать о работе с legacy-монолитами. Про микросервисную архитектуру и переход на нее говорят много, но редко упоминают о том, что проекты приходят к этому после многих лет развития как монолиты. Учебники по решению проблем не пишут. Чтобы поменять архитектуру живого решения, надо пройти несколько этапов. Я работал с разными проектами — и с полноценным multitenancy service-oriented REST architecture в Oracle, и с огромным монолитом, в репозитории которого были коммиты за десять лет. Эта статья — о темной стороне, о legacy-коде, и практических решениях проблем с монолитными приложениями на PHP.

Иллюстрация Алины Самолюк

Причины появления

Есть две основные причины появления legacy-кода. Первая — выходят новые версии операционных систем, языков, браузеров, библиотек. Особенно актуальна проблема для мобильных приложений и скриптовых языков — в каждой новой версии платформы нужно исправлять проблемы совместимости со старым кодом. Этого процесса не исбежать.

Вторая — технический долг, который создается специально. Руководство сокращает срок разработки ПО за счет отказа от проектирования, автоматического тестирования или code review, одобряет сторонние библиотеки, которые не поддерживаются, а разработчики не документируют сложную логику. Это происходит повсеместно и не зависит от количества денег в компании. Не стоит ругать плохих начальников. У них есть весомые причины поступать именно так.

У продуктов есть жизненный цикл, период большого спроса на популярные товары длится три-четыре месяца. Все лучшее конкуренты скопируют и сделают еще лучше, поэтому компании вынуждены регулярно выпускать новинки. Чтобы поддерживать объем выручки, новые продукты и новые версии появляются каждые несколько месяцев, так продажи нового цикла компенсируют снижение продаж в конце цикла. По три-четыре крупных релиза в год делают и Apple, и Marvel. И в Oracle на рынке Enterprise SaaS тоже квартальный релизный цикл. При этом рецепта успеха не существует. 97% стартапов выкидывают свои наработки и пробуют делать что-то новое, прежде чем найдут такой продукт, который у них будут покупать. Поэтому затраты на разработку MVP в стартапах максимально сокращают.

Проблемы с легаси? Значит, вам повезло!

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

Проблемы?

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

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

Что делать тем, кому повезло?

Начинать надо с тестирования

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

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

Обновление версии языка

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

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

Составить список проблем совместимости с новой версией PHP помогут утилиты статического анализа.

  • Rector — решит простые случаи несовместимости с новой версией, автоматически обновив часть кода.
  • Exakat — проанализирует совместимости кода по версиям PHP, покажет список используемых расширений, проблемных участков кода и поможет составить список задач на доработку.
  • Phan — покажет в коде лексические конструкции, которые убраны из новых версий PHP.

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

Обновление версии платформы или языка в таком случае выполняется быстро. Автор был инициатором обновления PHP с 5-й версии на 7-ю для приложения с большим объемом кода. И эту задачу команда успешно выполнила за три недели.

Переход от монолита к сервисной архитектуре

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

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

К счастью, слона можно съесть по кусочкам: отделять от монолита модули, не переписывая код заново, зафиксировать API, а затем превращать их в сервисы. Сначала части кода приложения надо выделить в отдельные пакеты, затем из пакетов можно будет создавать сервисы.

Перенос кода в пакеты открывает ряд возможностей:

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

Главное — это относительно небольшая по объему работы задача. Вынести часть кода в пакет без переписывания можно за несколько дней. У автора был опыт переноса в пакеты по тысяче строк кода в день с инверсией внешних зависимостей. А после фиксации API-модулей будет проще заниматься масштабным рефакторингом.

Разделение приложения на пакеты

Допустим, есть приложение на PHP, которое предоставляет клиентский API. Начинать любые изменения надо с тестирования и релиза, который включает план отката. Это называют release, control, validation и DevOps. Однако в активно развивающихся проектах тестирование и выкладка отработаны. В этом случае надо начинать разделять приложение с определения таких ограниченных контекстов, которые логично выделить в отдельные модули и сервисы.

Как пример, из приложения можно выделить обработку фотографий, аутентификацию пользователей, обработку платежей.

Создание отдельного модуля — это цикл из пяти подзадач:

  1. Выбрать небольшой функционал для переноса в модуль. Например, изменение размера изображений.
  2. Определить API модуля — написать интерфейс, доступный приложению.
  3. Написать или проверить приемочные тесты, например на загрузку и валидацию изображения.
  4. Скопировать в модуль старый код и инвертировать в коде модуля зависимости через границу модуля, без рефакторинга или переписывания всего кода.
  5. Заменить в коде приложения прямые обращения к старому коду на вызовы сервиса из нового модуля. Для решения этой задачи используют две технологии: IoC-контейнер и менеджер зависимостей.

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

Начать создавать пакеты можно в локальном каталоге, а для полноценной сборки и развертывания стоит создать собственный репозиторий пакетов, такой как Packeton, и перенести код модулей в собственные Git-репозитории. Также использовать платный репозиторий Private Packagist.

Как создать composer-пакет в приложении и зарегистрировать его как сервис в IoC-контейнере, смотрите здесь: до изменений, после изменений, diff.

В примерах используется composer для управления зависимостями пакетов и Symfony Dependency Injection как IoC-контейнер для сервисов. У вас может быть другой контейнер. Если в приложении нет IoC-контейнера, придется делать рефакторинг и реализовать внедрение зависимостей. Простейший пример добавления IoC-контейнера в приложение.

Решение проблем со связанностью кода

Есть два типа связанности:

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

Рассмотрим случаи связанности кода и варианты выделения модулей без трудоемкого рефакторинга всего кода.

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

Основные алгоритмы расцепления связанности:

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

2. Статические вызовы. Синтаксис PHP допускает вызов статических методов у объектов как методов класса (пример). Если выносите в пакет обычную функцию или класс, у которого есть статический метод, эти функции/методы нужно добавить в публичное API пакета (пример, diff).

Аналогично статические вызовы из пакета к методам классов приложения можно заменить статическими вызовами сервисов. Это будет реализация паттерна «мост».

Ссылки: пример прямого статического вызова, пример инверсии зависимости статического вызова через внедрение сервиса, diff коммита.

Если несколько методов из разных классов используются вместе, для них можно создать сервис-фасад.

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

Если есть несколько независимых классов-«хелперов» (пример) или обычных пользовательских функций, которые используются одновременно и в приложении, и в новом модуле, из них стоит создать отдельный composer-пакет и указать его в зависимостях приложения и других пакетов.

5. Применение глобальных констант и констант классов. Возьмем пример: в приложении есть класс, который нарушает Single Responsibility Principle и содержит обращения к константе другого класса. Наша задача — вынести первый класс в пакет без рефакторинга второго класса, потому что рефакторинг потребует изменения всего кода, в котором используется константа. Надо избавиться от прямого обращения к константе.

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

Ссылки: до изменений, после изменений, diff, декларация инъекции константы в контейнере.

6. Динамическое разрешение имен через строковые операции. Пример:

$model = new ($modelName . ’Class’);

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

Эту конструкцию можно попробовать переписать в switch-структуру со статическим списком классов. К счастью, в приложениях подобный код бывает редко.

Оптимизация

В больших приложениях количество сервисов в IoC-контейнере бывает очень большим. Если в пакет выносится большой объем кода, у него могут быть десятки зависимостей. При обработке клиентских вызовов обычно создается только небольшая часть сервисов. Но при передаче зависимостей в конструктор класса контейнер будет создавать все перечисленные сервисы.

Есть несколько способов решения этой задачи:

  1. Сервисы, которые передаются в пакет, можно объявить как lazy.
  2. Объект API пакета можно объявить как Service Subscriber.
  3. Разделить API пакета на несколько сервисов.

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

Service-Oriented Architecture

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

У каждого пакета зафиксирован публичный API. На основе этого API можно создать сервис с RESTful-протоколом. Код нового сервиса — это код пакета, вокруг которого написан стандартный роутинг, записываются логи и прочий инфраструктурный код. А в старом коде вместо кода пакета появляется адаптер для HTTP-вызовов через curl.

При создании отдельных внутренних приложений-сервисов надо решить две задачи:

  1. Детальное протоколирование вызовов всех сервисов. Каждому клиентскому запросу надо присваивать уникальный ID вызова, который передается во все сервисы при вызовах внутренних API. И каждый вызов сервиса следует протоколировать. Необходимо иметь возможность отследить вызовы сервисов по цепочке.
  2. Гарантировать единственный результат выполнения запроса при сбое одного из сервисов, когда запрос к сервису передан заново. Пример: клиентский запрос на платеж с его счета на другой счет. При сбое внутреннего выделенного сервиса, который выполняет запись результатов транзакции и пересчитывает баланс на счетах пользователей, повторный запрос к нему не должен привести к двум денежным переводам с одного счета на другой.

Заключение

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

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

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті.

👍НравитсяПонравилось4
В избранноеВ избранном4
Подписаться на тему «legacy»
LinkedIn



Підписуйтесь: Soundcloud | Google Podcast | YouTube


50 комментариев

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

Как раз пишут) Я свою третью книгу как раз и посвятил постепенному безопасному переходу от монолита к микросервисам it-simulator.com/#/article/459

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

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

Конечно! Предлагаю Вам тоже почитать ;) Сможем обсудить по делу, буду рад!

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

В Java мире переписать очень крупный монолит практически невозможно, прежде всего из-за финансовых затрат. Но с другой стороны, SOA(микросервисы) это не всегда панацея, как говорит Роберт Фаулер, монолит, грамотно разбитый на модули, лучше, чем криво сделанное микросервисное приложение.

В мире бизнеса нет понятия «дорого», есть понятие «выгодно» :) В Oracle переписывают, но статья не про него.

как говорит Роберт Фаулер

возможно Мартин?)

Нет, он говорит, что микросервисные от такого просто загибаются вместе с бизнесом
martinfowler.com/bliki/MonolithFirst.html

Роберт Мартин тоже говорит похожие вещи :) Пока автор сам не исправится — хз на кого ссылался

Детальное протоколирование вызовов всех сервисов. Каждому клиентскому запросу надо присваивать уникальный ID вызова, который передается во все сервисы при вызовах внутренних API. И каждый вызов сервиса следует протоколировать. Необходимо иметь возможность отследить вызовы сервисов по цепочке.

Да, это обязательное условие для микросервисов, и distributed tracing как раз это и помогает сделать, например, Zipkin.

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

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

Я специально избегаю темы SOA/microservices. Статья вышла на 7 печатных страниц + примеры кода — это и так слишком много. Service mesh, saga — темы для отдельных работ. Тема обеспечения единственности исполнения достойна целой книги.

Статья вышла на 7 печатных страниц
Service mesh, saga — темы для отдельных работ. Тема обеспечения единственности исполнения достойна целой книги.

а прикол в том, что на форумах люди почему-то ожидают что ты им пояснишь что-то вообще одним постом.
и когда не можешь вжать в один пост, гордо и уверенно делают вывод что имели дело с нубом :D

а прикол в том, что на форумах люди почему-то ожидают что ты им пояснишь что-то вообще одним постом.

Держи саморекламу)
dou.ua/forums/topic/32636
Правда, его очень немногие дочитали — вероятно как раз из-за длины и смеси тем

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

А вот что искренне рекомендую
Перевести на английский и опубликовать на медиуме и/или dev.to

Если б я такую написал — то точно бы так сделал.

Я вообще зацепился за идею с последней картинкой — кажется, там есть закономерность. Чтобы проверить, накодил себе на корутинах, которых раньше не трогал. В результате в одной статье и С++ и теория, и они не слепились.
Думаю поспрашивать у заграничного народа — куда приткнуть. Больших штук вроде Медиума боюсь — потеряется в потоке. Надо какую-то тематическую конфу или журнал.

Надо какую-то тематическую конфу или журнал.

как видел — народ просто потом постит линк в редите
а там глядишь и в хакер ньюс кто протолкнет

то есть — должна быть статья, а уж где и как ее пиарить, вопрос полегче :)

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

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

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

К счастью, слона можно съесть по кусочкам: отделять от монолита модули, не переписывая код заново, зафиксировать API, а затем превращать их в сервисы. Сначала части кода приложения надо выделить в отдельные пакеты, затем из пакетов можно будет создавать сервисы.

Не уверен, что такое пакеты в PHP, но обычно такая задача решается с помощью паттерна Facade, когда старые и новые куски проекта скрываются за неким API, и клиенты и не подозревают, какой же код они в итоге вызывают. Это позволяет например переключаться между старым и новым кодом просто установив галочку где-то в админке

В статье как-раз много примеров использования фасада и композиции. Пакет в PHP — примерно то же, что в js, python и home brew :)

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

Неужели в PHP все так плохо? В современных Java проектах разработчик должен обеспечить 100% покрытие НОВОГО года, иначе его код никому не нужен.
Одобрение сторонних библиотек, которые не поддерживаются — вообще жесть, наоборот, тяжело уговорить добавить новую библиотеку.
Документирование сложной логики — а зачем это делать. Тесты это и есть документация вашего кода и его логики

Темы «php умирает», управление SBOM, покрытие тестами и документирование — это разные вопросы, и каждый достоин большого обсуждения не в одном комментарии.
Чувствую, что Вы не видели проектов на ноде, не собирали фронт Gulp-ом :) В мире веб-разработки обратная совместимость обеспечивается далеко не всеми популярными платформами.

В современных Java проектах разработчик должен обеспечить 100% покрытие НОВОГО года, иначе его код никому не нужен.

неправда.
в тех проектах на Джаве в которых участвовал — не ставили себе такую цель.
Уже потому что на покрытие свыше 60-70% — резко растут трудозатраты.
Или надо принимать технические решения которые будут неэффективными с инженерной точки зрения.

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

Этой байке много лет.
Она живуча — потому что красива. Красивые байки поэтому живут веками...

Простой вопрос — как вы проверите корректность написанных тестов — без документации?

А как вы тогда гарантировали, что ваш код будет работать, если он не покрыт тестами?

покрытый код — тоже не дает таких гарантий.

никогда в жизни не давал гарантий что мой код будет работать без багов.

если вы даете, то какую сумму убытка готовы покрыть от своего бага?
с своего кармана. вот это тогда да, вы гарантируете.
а зеленые тесты — это не гарантия.

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

Покрытый год дает гарантию, что этот код ЗАПУСКАЕТСЯ и работает корректно с теми аргументами, который ему подали на вход. Конечно, ошибки могут быть, потому что нельзя все предусмотреть, но zero-level bugs не должно быть.

что этот код ЗАПУСКАЕТСЯ и работает корректно с теми аргументами, который ему подали на вход.

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

Покрытый год дает гарантию

то есть багов на проде не будет?
сколько готовы выставить на спор например?
ну хоть «ящик коньяку» — готовы?

Конечно, ошибки могут быть, потому что нельзя все предусмотреть,

а-а-а, то есть вы не даете гарантий, что в тестах все предусмотрено....

все значимое оказалось в сносках к договору, мелким шрифтом :)

Покрытие кода не дает уверенности, к сожалению. Я как-то исправлял ошибку, и вместе с ней тест, который проверял, что поведение ошибочно. Ошибка заключалась в том, что метод возвращал константу 1 вместо пустой строки. Прямо так и было написано: return !(int)(bool)$result; а в тесте assert is_int(), assert >0;
Вообще, бизнесу не нужна уверенность, бизнесу нужны продажи.

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

согласен, байкшеддинга я стараюсь избегать

Придумали. Программы можно формально доказывать en.m.wikipedia.org/...​ightweight formal methods. Очень дорого, но если вы ракеты запускаете — оправдано. Можно ещё писать на строго функциональных языках, типа Haskell — там если скомпилировать то шансов что есть баги уже намного меньше. Но, снова, дорого, сложно найти людей... в общем php для бизнеса снова оказывается по итогу обычно выгоднее. Не хочу раздувать пост и писать почему в покрытом коде тоже есть баги, просто зайдите в репу к любому популярному оупенсорс проекту с не тривиальной базой кода, который вы сами считаете хорошим и у которого есть тесты, и посмотрите сколько к них багов зарепорчено

Вы точно Lead, как написано в профиле? Это же прописные истины.

Это же прописные истины.

прописные истины учат «в первом классе»

если это все что вы пока выучили, то мой ответ на

Вы точно Lead, как написано в профиле?

сливки и сахар добавляйте сами. по собственному вкусу.

а я годами соглашаюсь на доу — тупее и невежественней меня на доу еще поискать нужно :)
даже вот афоризм накрапал
«Если один считает другого кретином, то один из них точно кретин»

Простой вопрос — как вы проверите корректность написанных тестов — без документации?

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

сначала code-review, а потом уже считается количество ошибок в том или ином модуле и на основании этого оценивается качество тестов

вы даже вопроса не поняли.
поэтому вопросов к вам больше нет :)

О темной стороне legacy-кода. Как решить проблемы с монолитными приложениями

Хорошо бы вынести в название статьи слово PHP, чтобы сразу понять о чем речь.

Есть две основные причины появления legacy-кода. Первая — выходят новые версии операционных систем, языков, браузеров, библиотек. Особенно актуальна проблема для мобильных приложений и скриптовых языков — в каждой новой версии платформы нужно исправлять проблемы совместимости со старым кодом. Этого процесса не исбежать

Вот тут не совсем согласен. Если это только про PHP — может быть. Но для Java как платформы всегда гарантируется обратная совместимость. Если говорить про библиотеки, то тут очень высокая степень совместимости в новых версиях — от 90 до 99% (за редким исключением). Поэтому для Java проектов причина появление legacy кода — это скорее не новые версии библиотек, а новые тренды и подходы к разработке

Да, редакция убрала PHP из заголовка и оформила иллюстрацией. Java на серверах — да, но правила и guideline для мобильных приложений меняются часто, и без обновлений аппы убирают из маркета. Python тоже касается, особенно касательно перехода с 2 на 3. JS в меньшей степени, однако, непонятно что лучше — считать null объектом, или поломать BC.

Я могу лишь догадываться о причинах. Скорее всего, чтобы увеличить количество просмотров. Правда в самой ссылке есть слово php.

Детальное протоколирование вызовов всех сервисов. Каждому клиентскому запросу надо присваивать уникальный ID вызова, который передается во все сервисы при вызовах внутренних API. И каждый вызов сервиса следует протоколировать. Необходимо иметь возможность отследить вызовы сервисов по цепочке.

Підкажіть, будь ласка, а яким чином ви збираєте потім ці всі логи в одне місце, щоб потім відслідкувати, які сервіси проходив запит, та отримати якийсь trace? Як я розумію файлові логи посервісно тут не підійдуть (бо дуже не зручно буде відслідковувати ланцюжок викликів), чи можно їх потім загнати в якийсь агрегатор як от elasticsearch?

Здесь сразу три вопроса.
1. Файловые логи, конечно, не рассматриваются. Сервисы зачастую работают на «бездисковых» блейдах, в которых стоят небольшие диски только для запуска OS.
Вариантов решений несколько. Мне понравилось решение с on-premise sentry, когда логи отправляются в sentry, который запущен на собственном сервере, однако sentry дорогой.
Популярный вариант — ELK, но простым решение назвать нельзя, elastic — капризный монстр. На днях ребята жаловалсь, что попробовали просто переключить на него монолог — эластик стал ругаться на неверный формат данных, надо разбираться.
Иногда делают по классике — из приложения в syslog, а дальше аггрегация на центральном севере, но надо понимать, что syslog медленный, и в стандартных реализациях есть ограничение на размер записи, всего несколько килобайт, так что записать в лог пользовательский ввод не получится.
2. Надо проектировать поддержку глобального сквозного идентификатора событий в сервисах, по этому идентификатору можно будет вытягивать логи по всем сервисам. Событие — это http-запрос от пользователя, запуск по cron или задача, которая выполняется в заданное время.
3. А дальше уходим в тему event sourcing и, возможно, saga.

однако sentry дорогой

Бесплатный.

Может быть и бесплатным, при соблюдения ограничений их нестандартной лицензии. Запрет для «third parties other than your employees and contractors to access (Sentry), so that such third parties directly benefit from the error-reporting or application monitoring» для использования в enterprise-среде, в группе компаний не подходит. Для маленьких проектов, где все оформлено в рамках одного субьекта — нормально. Но доступ в sentry сотрудников и контрактников, у которых договора с разными юридическими лицами, из разных стран — нарушение. Впрочем, аутсорсинг на такие вещи часто забивает.

Толкая статья, спасибо !

Дякую за статтю, якраз теж маю моноліт і розмірковую над тим, щоб почати виокремлювати від нього сервіси. Цей приклад буде корисним. Окрема подяка за діфи.

починайте з контекстiв

Автор работал с разными проектами — и с полноценным multitenancy service-oriented REST architecture в Oracle

 — просьба поделиться опытом.

Автор был инициатором обновления PHP с 5-й версии на 7-ю для приложения с большим объемом кода. И эту задачу команда успешно выполнила за три недели.

Можете чуть подробней про объем кода и с какой на какую версию переходили, а так же размер команды ? Был похожий опыт и интересно сравнить сколько усилий надо если приложение как я понял на симфони.

Объем кода не имеет значения. Влияют другие факторы. Важен так называемый «Software Bill of Materials» — компоненты ПО, библиотеки, зависимости. Проблем с кодом приложений обычно намного меньше, чем с зависимостями. Когда любой кодер в команде имеет право выполнить composer require или npm install без анализа поддержки библиотеки, отправить обновленный список зависимостей в работу, а затем уволиться, сроки обновления версии php можно называть от полугода до года. Когда SFBM управляем, а QA имеют отработанную схему тестирования, срок обновления даже очень большого объема кода на следующую мажорную версию — до месяца.

Подписаться на комментарии