У Вас проблемы с legacy — значит, Вам повезло! Распил монолита на PHP

    Вступление

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

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

    Есть две основные причины появления 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 коммита.

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

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

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

    • Наследование от внешних классов с зависимостями надо превратить в композицию с помощью адаптеров, которые внедряются как сервисы

    • Для защищенных свойств, которые используются в дочернем классе, надо сделать getter-методы, а для защищенных методов надо создать прокси-методы.

    • Наследование классов приложения от классов модуля стоит инвертировать в композицию с сервисом, который предоставляется новым пакетом.

    Рефакторинг наследования - довольно трудоемкая задача, а добавление адаптеров может негативно повлиять на производительность. Поэтому для небольших родительских классов и трейтов без зависимостей, имена которых не используются в типах параметров, можно нарушить принцип подстановки Лисков, и для сокращения объема работы просто скопировать в пакет, выставив им пространство имен пакета. Пример: до изменений, после изменений, 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. Гарантировать единственный результат выполнения запроса при сбое одного из сервисов, когда запрос к сервису передан заново. Пример: клиентский запрос на платеж с его счета на другой счет. При сбое внутреннего выделенного сервиса, который выполняет запись результатов транзакции и пересчитывает баланс на счетах пользователей, повторный запрос к нему не должен привести к двум денежным переводам с одного счета на другой.

    Заключение

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

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 69

      0

      Можете раскрыть данный пункт в примерах про снижение связанности?


      Для защищенных свойств, которые используются в дочернем классе, надо сделать getter-методы, а для защищенных методов надо создать прокси-методы.
        0
        Уточните вопрос, пожалуйста. В тексте есть ссылки на github, можно посмотреть код и diff. Как иначе можно раскрыть этот пункт?
          +1
          Я думал о том, как привести пример рефакторинга. Просто кода недостаточно, надо показать как код меняется. Сначала надо прочесть пример кода со связанностью по ссылке «до изменений», потом результат рефакторинга по ссылке «после изменений», и для удобства ссылка на diff коммита.
        –4
        В 2020 пошла начался прямо шторм заказов на апгрейд с php 5. Причина до неортодоксальна — хостеры апдейтят mysql. А старый php со свеженьким mysql или не работает или работает с костылями или требует настройки не на уровне пользователя из-за кодировок, т.к. адаптер другой.
        Апдейтим на 7.4.х в основном, много ручной работы, т.к. автоматические (в том числе самописные) конверторы так себе, несмотря на token_get_all, без которого вообще смерть была бы.
        Откровенно говоря php переборщил с несовместимостями со старыми версиями. Очень много поломано, что совершенно не обязательно было ломать, при этом во многих случаях поведение меняется неявно, т.е. код просто начинает работать по другому, даже нотисов не бросает, это жесткая подстава.
        Тестами даже в хорошем легаси обычно и половины не покрыто, из-за чего пришлось взять на вооружение «двойную запись». Сначала пишем аналог функции, потом сайт работает с недельку-две на обоих функциях проверяя одинаковость результата при каждом вызове. Получается покрытие кода тестами без написания собственно самих тестов и более полное. Но это жесть конечно.
        Самое дурацкое, что 8-ка пхп тоже имеет много несовместимостей с 7-кой, при этом местами таких, что если писать хороший код на 7-ке, то на 8-ку его придется именно переписывать. Печаль.
          +2
          Откровенно говоря php переборщил с несовместимостями со старыми версиями.

          О, да. Обновляться с php 5 в момент, когда выпущен php 8 и утверждать о проблемах с обратной совместимостью?


          Самое дурацкое, что 8-ка пхп тоже имеет много несовместимостей с 7-кой, при этом местами таких, что если писать хороший код на 7-ке, то на 8-ку его придется именно переписывать. Печаль.

          Такие утверждения неплохо бы подтверждать примерами.


          На мой личный взгляд php очень правильно и верно избавляется от старых "неконсистентностей". Более того, обычно всё проходит стадию notice, прежде чем стать фатальной ошибкой. Просто нельзя вечно откладывать апгрейд — либо нужно платить за обновления постепенно (а-ля амортизация), либо в аврале и панике (а-ля плохой менеджмент).

            –1
            О, да. Обновляться с php 5 в момент, когда выпущен php 8 и утверждать о проблемах с обратной совместимостью?
            Вы как-то всё в черно-белом свете воспринимаете.
            Совершенно очевидно и банально, нам кажется, что проблемы с обратной совместимостью неизбежны.
            Проблема не в том что они есть, проблема в том, что с ними переборщили.

            Такие утверждения неплохо бы подтверждать примерами.
            www.php.net/manual/en/migration80.incompatible.php

            На мой личный взгляд php очень правильно и верно избавляется от старых «неконсистентностей».
            Абсолютно согласны.
            Вот только было бы неплохо, что бы
            а) Он делал это грамотно. Что бы хотя бы нотисы были на изменившееся поведение там, где оно изменилось, без необходимости вручную перепроверять килобайты кода. Потому что втихаря такие вещи не делаются по уму.
            б) Не избавлялся бы от старого только потому что оно старое, в погоне за модой и ориентируясь на другие языки. Потому что когда приходится рефакторить абсолютно корректно и однозначно работавший кусок кода только потому, что в новой версии пхп запретили применять подход который показался устаревшим (хотя он применяется в том же питоне успешно или в котлине и даже речи не идет об избавлении от него), то это уже перебор.

            Вы, возможно, просто не сталкивались с задачами по переписыванию старого легаси? Ни в одном из языков не было столько гимороя, сколько в пхп с момента появления 7-ки. Даже питон 2 на 3 проще переписать или друпал с 4 на 8, чем проект на пхп обновить.
              +1
              www.php.net/manual/en/migration80.incompatible.php

              Это так себе аргумент. Хотелось бы реальных примеров из жизни.


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

              От чего избавились в погоне за модой? Только не надо мне снова ту ссылку скидывать.


              Вы, возможно, просто не сталкивались с задачами по переписыванию старого легаси?

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

                +2

                Судя по некоторым признакам (ужжжжасная сложность апгрейда и упорное нежелание приводить конкретные примеры), товарищ имеет в виду старое доброе mysql ext.


                Других вариантов настолько сильно страдать при апгрейде я не представляю.

                  –1
                  Судя по некоторым признакам (ужжжжасная сложность апгрейда и упорное нежелание приводить конкретные примеры),
                  Конкретные примеры есть по ссылке выше и плюс несколько текстом изложили чуть ниже, ужжжжасно сложно было прочитать перед ответом или просто нежжжжжелали делать это?:)

                  товарищ имеет в виду старое доброе mysql ext.
                  Со старым добрым mysql ext никаких проблем при апгрейде нет, свежая версию php со свежим mysql дружит из коробки лучше даже чем старые. Самая проблема была с переходом когда все на latin1 сидели, но это было уже черт знает когда, еще во времена 3 или 4 пхп.
                  Если Вы имеете ввиду deprecated mysql, то это тоже не проблема там все тупо решается заменой его на mysqli — все названия функций даже сохранились. Да и опять же, к моменту 5.6 как правило все движки уже это пофиксили.

                  Других вариантов настолько сильно страдать при апгрейде я не представляю.

                  Если это какая-нибудь известная цмс с парой кастюмных плагинов, то никаких сильных страданий, но такие заказы на апгрейд редкость, там как правило хватает штатного прогера накатить свежую версию движка, да поискать аналогичный плагин если старый не обновлялся.
                  Проблемы возникают когда проект хотя бы на 50% кастюмный, т.к. пересмотреть его весь нереально, а смена поведения кода не всегда в логи ошибок попадает.
            0

            Что-то не заметил шторма

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

              В php не так много проблем обратной совместимости между версиями. У меня был случай, когда огромную enterprise-систему с десятком команд общей численностью в несколько сотен человек обновили с PHP 5.3 на 7 менее, чем за месяц.

              Кое-что переписывать приходится. Однако, эти строки не надо искать, их показывают IDE и статические анализаторы. Почему обычная штатная поддердка считается проблемой — это уже вопрос не технический.
                –1
                Сроки жизненного цикла версий PHP известны на много лет вперед, запланировать обновление в следующем году не так сложно.
                Для планирования нужно знать объемы изменений, при этом даже зная их — для планирования нужен еще предварительный анализ легаси кода.
                А объемы изменений оценивать тоже забавно, вот из первого попавшегося. И такого до фига, вроде даже в 7-ке еще не убрали, а сейчас 2021 год уже.
                According to an internals discussion from June 2008 (see references below), the curly brace syntax was deprecated in PHP 5.1 RC5, but the deprecation warning was removed before the final release. In August 2006, the documentation for $str{42} read “deprecated as of PHP 6”, but again the deprecation never made it into a production release.


                Эти строки не надо искать, их показывают IDE и статические анализаторы.
                Если что-то депрекейтед, варнинг или нотис — таки да.
                А вот если что-то типа 0 == "" которое из тру внезапно стало фалсе, несмотря на 42 == " 42" так и оставшийся тру, при том что 42 == «42foo» стал фалсе — то тут беда, т.к. код как был абсолютно рабочим, так и остался.
                А когда в class Name { function Name вдруг функция перестала быть конструктором? Да, это понятное изменение которое понятно как править, но во-первых на фига (в яве например предыдущее поведение и никто не говорит что это плохо, а где ява и где пхп), а во-вторых это не выдает ошибки, просто класс вдруг остается без конструктора со всеми вытекающими.
                А изменения в порядке аргументов и типах аргументов? Учитывая автоконверт типов? А если умножить на изменение в конверте типов?
                И так далее и тому подобное… вымораживают именно вот такие мелочи — когда поведение абсолютно рабочего кода работающего строго по однозначной документации вдруг становится другим.

                Более того, самый треш даже не в том, когда есть рабочий легаси который надо апгрейдить. Самый треш в том, что мы берем чужой код и не зная на какую версию пхп он был расчитан, мы не знаем что он выдаст на выходе, не можем сразу понять какое поведение ожидалось.
                То есть логично если бы какой-то код не запустился на какой-то версии или выдавал бы ошибки, но ведь он запустится на обоих и без всяких ошибок выдаст разные результаты. Это что за б-ство вообще?
                Представьте себе что в пятницу 2*2=11, а в субботу 2*2=10, просто потому что «теперь другая версия»? И без всяких признаков ошибок? И хорошо еще если не 9… ведь может быть и неверный ответ.

                  +2
                  Апдейтим на 7.4.х в основном
                  А когда в class Name { function Name вдруг функция перестала быть конструктором?
                  это не выдает ошибки

                  https://3v4l.org/kWSbk


                  Но виноват, конечно же, РНР

                    +2
                    когда в class Name { function Name вдруг функция перестала быть конструктором

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

                    Может быть, Rector делает автоматическую замену. PHPStorm заменяет объявление конструктора по клику мышки.
                    +1
                    Давайте определим контексты.

                    Объемы изменений синтаксиса показывают статические анализаторы, в том числе PHPStorm.

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

                      Дело в том, что такая ситуация везде и с любыми программами. В python аналогично, в JS режим strict решает не все проблемы.
                      Компилированные приложения, написанные на С/RUST/golang, при замене libc успешно запустятся, но ломаются на несовместимости ABI. О проблемах с ПО, которое написано под Windows 7, под 10-кой, думаю, знают все.
                      Любое ПО требует сопроводительной документации и указания версий.
                        0
                        А вот если что-то типа 0 == "" которое из тру внезапно стало фалсе

                        А вы считаете код, который так делает, качественным? Зачем вообще использовать ==, когда есть ===? Это ж и есть причина хождения по граблям.


                        А когда в class Name { function Name вдруг функция перестала быть конструктором

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

                          –1
                          Ну вообще-то class Name { function Name — в php4 не было.
                          так что это всё же php5
                            +1

                            А как работали конструкторы в PHP4?

                            0
                            А вы считаете код, который так делает, качественным? Зачем вообще использовать ==, когда есть ===? Это ж и есть причина хождения по граблям.
                            Отличное замечание и ситуация с этим как раз ярко показывает переставление кроватей, вместо смены персонала.

                            Было привычное всем и однозначное поведение, 0=="" тру. Много где, кстати, приведение строки к числу даст 0 и это будет тру. Много прогеров полагались на это автоматическое приведение типа при сравнении.

                            Окей, пхп решили улучшить и решили поправить в нем ситуации с типами. Логично было бы сделать строгое сравнение, правильно? На старый тип сравнения кидать нотис, варнинг или депрекейтед в конце концов.
                            А что сделали? Взяли и поменяли тру на фалсе в обычно, привычной, никому не мешавшей, часто используемой конструкции.

                            Если это было в ваших проектах, написанных на PHP5, то это лишний раз доказывает, что с качеством кода у них было не очень.
                            Да речь не о наших проектах, а о том с чем приходится сталкиваться.
                            И кстати, в проектах написанных еще на пхп 4, но перешедших на пхп 5 такое было повсеместно. Даже в магентом том же.
                              0
                              Было привычное всем и однозначное поведение, 0=="" тру.

                              Оно не было ни привычным, ни однозначным. Как минимум в мире PHP5. Зато это был пример дурного тона и плохого кода.

                                0
                                Оно не было ни привычным, ни однозначным. Как минимум в мире PHP5.
                                Оно было прямо прописано в мануале и всегда работало. Вы о чем?
                                Зато это был пример дурного тона и плохого кода.
                                Нет, это не так. Автоматический каст типов это одно из основополагающих свойств php, формирующее сам язык. Если кто-то не способен пользоваться этим инструментом безопасно, то это не проблема языка.
                                  0
                                  Если кто-то не способен пользоваться этим инструментом безопасно, то это не проблема языка
                                  Не, с инструментами такая логика не работает. Ни в виртуальном, ни тем более в реальном мире. Любой инструмент обвешивается защитами настолько, насколько создатели инструмента смогли придумать. Идеальный инструмент конструктивно невозможно использовать неправильно.
                                    0
                                    Оно было прямо прописано в мануале и всегда работало. Вы о чем?

                                    Оно может и было, но кроме мануала есть — внезапно — комьюнити, PSR-стандарты, best practices. PHP в свои ранние годы не был образцом лучших практик, но комьюнити их таки формировало. И именно в немалой степени под влиянием комьюнити в языке произошли многие изменения. Кивать на плохой пример из мануала, прикидываясь, что вокруг ничего больше нет, так себе подход. Близорукий и недальновидный.

                                      0
                                      Это какой-то инфернальный аргумент.
                                      Мы Вам говорим — «мануал же, что непонятно-то».
                                      А Вы нам «да при чем тут мануал, если мне в комьюнити по другому сказали».
                                      Кгм.
                                        0

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


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

                                          –1
                                          Скорее всего там было что-то в духе «если сделать так, то результат будет такой-то»..
                                          Это и называется мануалом. Потому что из этого любому адекватному члену сообщества понятно какой будет результат, если сделать так. Это и называется предсказуемым и однозначным поведением языка.
                                          Потому что что бы там не «говорили в комьюнити», если в мануале написано 0=="" тру, то оно тру и будет. А нравится оно Вам или нет — это уже субъективное мнение, а не язык.
                                            0
                                            Потому что из этого любому адекватному члену сообщества понятно какой будет результат, если сделать так

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


                                            Язык, знаете ли, и вот такое позволяет:


                                            $b = 1;
                                            $a = 'b';
                                            $$a = 2;
                                            print_r($b); // 2

                                            Но это не значит, что так нужно делать.


                                            Да и линтеры не вчера были придуманы. Не пользуетесь ими? Ну так ССЗБ.

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

                                              Язык, знаете ли, и вот такое позволяет:
                                              Но это не значит, что так нужно делать.
                                              Если к инструкции к микроскопу написано — «смотреть в микроскоп и изучать микробов», то практиковать его использование в качестве телескопа несколько странно, но это конечно Ваше право:)

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

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

                                                  0
                                                  Если бы вы пользовались,
                                                  Вы не считаете нужным читать о чем дискуссия, перед тем как влезть в нее?

                                                  У нас не свой проект, к нам обращаются самые разные заказчики с просьбами проапгрейдить их сайты с php самых старых версий, которые в большинстве случаев пишутся по мануалам. За этот год проапгрейдили больше 20 штук, поэтому знаем о чем говорим.

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

                                                    Вы — это не вы конкретно, а вы и ваши клиенты. Цепляясь к словам, вы пытаетесь подменить тезис, но вам это не удалось. Суть в том, что в некачественном коде виноват не язык, а его авторы. А язык сейчас пытается немного ограничить рамки некачественности. И именно это вас не устраивает, судя по вашему недовольству. Но если вас что-то не устраивает, вы можете просто этим не заниматься. Ну, т.е. я надеюсь, что вы не в рабстве там.


                                                    Оно есть во многих комьюнити и крупных проектах, чему пример код зенд.фреймворка и многих других распространенных, код магенто и многих других распространенных магазинов, код друпела и многих других распространенных цмс.

                                                    Это еще хорошо, что вы Wordpress в пример явным образом не привели :D Но что с того, что где-то есть кривой код? Это повод его копировать? Так себе аргумент.

                                                      0
                                                      Вы — это не вы конкретно, а вы и
                                                      И опять Вы пытаетесь нам что-то приписать.

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

                                                      что с того, что где-то есть кривой код? Это повод его копировать?
                                                      Это из серии «одна я белая красивая в белом стою».
                                                      Крупнейшие разработчики (включая разработчиков самого языка) использовали подход описанный в мануале, но тут приходите Вы и говорите что они все дебилы не использующие линтеры. Вам в голову не приходило, что существуют разные подходы, а не только Ваш единственно правильный?

                                                      p.s.: Вот только если Ваш подход хотя бы наполовину такой серьезный как Вы преподносите, то встает вопрос почему Вас не приняли в команды разрабатывающие на «взрослых языках», где этот подход прямо прописан в гайдлайнах по языку… и вам приходится довольствоваться php, которым Вы так недовольны:)
                                                        0
                                                        Ваш тезис мы процитировали

                                                        Вы — царь? Или вас там сколько?


                                                        встает вопрос почему Вас не приняли в команды разрабатывающие на «взрослых языках», где этот подход прямо прописан в гайдлайнах по языку

                                                        А что вы обо мне знаете, чтобы это утверждать?

                              0
                              Если что-то депрекейтед, варнинг или нотис — таки да.
                              А вот если что-то типа 0 == "" которое из тру внезапно стало фалсе, несмотря на 42 == " 42" так и оставшийся тру, при том что 42 == «42foo» стал фалсе — то тут беда, т.к. код как был абсолютно рабочим, так и остался.

                              Да, проблема такая есть!
                              У всех есть 2 года, чтобы адаптировать приложения, с 2023 PHP7 превращается в тыкву. Это много, много строк кода, и не всегда можно заменить if (foo()==0) на if (foo()===0 || foo()==='0').
                              Интересная тема для исследования и для статьи, может быть, даже для стартапа. Сейчас подумаю на эту тему.
                            +1

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


                            Откровенно говоря php переборщил с несовместимостями со старыми версиями

                            которое ничего общего с реальностью не имеет.


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


                            А если уж апгрейдить с какой-нибудь допотопной 5.2 сразу на 8-ку, то надо это делать с умом. Сначала проапгрейдить на 5.6, потом на 7.4, и только потом уже пытаться это натянуть на 8.
                            Плюс параллельно рефакторить код под новые стандарты.

                              0
                              Знаешь, я вижу два мира, которые обычно не встречаются, а тут в комментариях не могут понять друг друга. Один мир — это enterprise, который хочет дженериков и радуется аннотациям. Маленькие винтики больших систем, готовые все отрефакторить за месяц-другой. Боец рефакторит — зарплата идет.

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

                              Вопрос в том, как помочь вторым?
                                0

                                Не знаю.
                                Могу только сказать, что им будет очень непросто.
                                Ты в курсе, наверное, как Зеев отчаянно бился против отмены коротких тегов, причем практически в одиночку? Учитывая, что его гешефт собственно и как раз построен на поддержке легаси, такая отмена означала бы ровно то самое переписывание огромных объемов кода на пустом месте, о котором ты говоришь.
                                И если теги ему удалось отстоять, то с undefined variable уже не получилось. Теперь, чтобы продолжать их игнорировать, нужно ужимать уровень отображения ошибок, а это очень рискованное занятие.


                                Никита в одном интервью как-то сказал, что веб за 20 лет кардинально изменился. И тот — молодой, зеленый РНР, который появился вместе с вебом, уже не отвечает требованиям времени. И чтобы выжить, должен меняться. Причем в первую сторону.

                                  0
                                  С php сейчас происходит примерно то же, что с внедорожниками за последние 20 лет. Из жестких полноприводных с блокировками и механикой которые позволяли делать что угодно они превратились в нечто моноприводное тепличное с автоматизацией и рамками.
                                  Это, конечно, отвечает веяниям времени, но это уже не php который выбирали за внедорожность. Загоняют в рамки дорог, потому что на дорогах нельзя так увязнут как на бездорожье.
                                  И преподносят это как бонус, вот мол — теперь сложнее совершать ошибки, а машина намного чище. Ну офигеть, чистая машина, ведь за этим внедорожник и покупают, ага.

                                  Мы в свое время слезли с явы и си на php именно потому, что это был язык который много что позволял и в нем не было явовской монструозности, где для hello world надо наколбасить проект из 3 файлов и в каждом по 50 строк (утрируем немного).
                                  Сейчас чем больше смотрим на последние изменения в php и общие тенденции, тем больше видим его уход в сторону того, от чего мы в свое время ушли на php.
                                  Само по себе это может и неплохо, но! приближаясь в эту сторону php начинает конкурировать со взрослыми языками и в конце концов есть неслабый риск размыться в них и умереть.
                                  Допилят завтра visual studio, будет компилить проект на котлине в jit код. Дальше что? А дальше всё. Все те, кто пришел в пхп вынужденно из явы, дот.нет и прочего — положат большой болт на php и уйдут писать на котлине. Потому что там этих вот «правильных написаний» значительно больше.
                                  Да в конце концов даже мы так поступим при таком раскладе, т.к. если php потеряет свою «неправильность», то проще будет действительно работать с устоявшимися языками с немерянным количеством либ на языке кровавого энтерпрайза, а не городить нечто на php лишая себя кучи возможностей.
                                    0
                                    Да в конце концов даже мы так поступим при таком раскладе, т.к. если php потеряет свою «неправильность», то проще будет действительно работать с устоявшимися языками с немерянным количеством либ на языке кровавого энтерпрайза, а не городить нечто на php лишая себя кучи возможностей.

                                    Нет, остановитесь! Мир PHP этого не переживет!

                                  0
                                  Вопрос в том, как помочь вторым?

                                  А им надо помогать?

                                    0
                                    почему нет, это большой рынок
                                      0

                                      Ну, если в этом есть коммерческая выгода, то, уверен, помощники найдутся. А если выгоды нет, то значит это не рынок, а так...

                                    +1
                                    Другой мир — это веб-студии, в которые идут владельцы тысяч сайтов, а некоторые сайты написаны были еще на PHP 4. Этих сайтов были миллионы, и десятки тысяч все еще живы. Платить за рефакторинг у владельцев желания нет, но немного заплатить за продление жизни готовы. Они решают проблемы по мере их появления, и чем дешевле решение — тем лучше.
                                    Напомнило:
                                    Взять, к примеру, башмаки. Он получал тридцать восемь долларов в месяц плюс довольствие. Пара действительно хороших башмаков стоила пятьдесят долларов. А пара доступных по средствам башмаков, которых хватало на сезон или два, пока не изнашивался подметочный картон, после чего они начинали течь как сито, стоила десять долларов. Именно такие башмаки Ваймс покупал и носил до тех пор, пока их подошвы не становились настолько тонкими, что даже в самую туманную ночь он легко мог определить, на какой улице Анк-Морпорка находится, лишь по ощущению булыжников под ногами.
                                    Не уверен, что владельцам маленьких сайтов получится бесконечно помогать. Рано или поздно им придется «купить новые башмаки».
                                0
                                У каждого пакета зафиксирован публичный API. На основе этого API можно создать сервис с restful-протоколом. Код нового сервиса — это код пакета, вокруг которого написан достаточно стандартный роутинг, запись логов, и прочий инфраструктурный код. А в старом коде вместо кода пакета появляется адаптер для http-вызовов через curl.
                                My First Law of Distributed Object Design: Don't distribute your objects (From P of EAA).
                                Ну так себе совет делать веб-сервисы. Интуитивно даже это кажется нехорошим. Говорят, лучше определять business/boundary contexts и так делить. Что сложнее и менее очевидно, но имеет больше смысла, в отличие от раскидывания целостного приложения по серверам.
                                  0
                                  Спасибо за комментарий, в следующем абзаце первым шагом указано именно определение контекстов.
                                    0
                                    Но в приведённом абзаце предлагается соединять целостное приложение синхронными сетевыми вызовами (то есть выделять часть приложения в веб-сервис), а не независимые BC по асинхронным способом. Что есть обычно следствие неправильного определения BC.
                                      0
                                      Про синхронность речь не идет. Если писать точнее, PHP с помощью curl позволяет выполнять http-запросы в неблокирующем режиме.

                                      Есть городская легенда среди разработчиков на других платформах, что в PHP доступен только блокирующий режим. Однако, это не так. Для PHP даже принят стандарт PSR 7, чтобы абстрагироваться от http-протокола и запросов.
                                      Чтобы не работать с HTTP-запросами, есть к примеру, Guzzle. Аналогичный функционал есть в популярных фреймвоках.
                                        0
                                        Речь не о технических деталях, а о том, что логика потока одной подсистемы зависит от результата или времени выполнения другой подсистемы, то есть они тесно coupled. Взаимодействие по сихронному протоколу, как HTTP — это явный маркер такой сплетённости, но не только. Если не зависит, то всё хорошо и можно выкинуть синхронность.
                                        Если говорить о технической имплементации, то для асинхронного взаимодействия стоит всегда выбирать например очереди, так как это повышает общую надёжность всей системы.
                                        В PHP это даже стандартизировано как PSR 7.
                                        PSR-7 вообще не о режиме выполнения, это стандартизированная обёртка над HTTP и не более того.
                                          0
                                          Не уловил Вашу мысль. Подсистемы могут зависеть, или могут не зависеть — согласен.
                                          HTTP — это маркер не только сплетенности. Если не зависит, то всё хорошо

                                          простите, не понимаю о чем Вы
                                            0
                                            К примеру, сохранение заказа:
                                            // Веб-сервис, очень плохо
                                            // код в Customer
                                            $order = $orders->getLatest($customer); // Под капотом вызов отдельного сервиса-приложения Order через CURL
                                            $transaction = $order->pay($customer); // Под капотом вызов сервиса Order через HTTP
                                            
                                            // Независимый сервис, приемлемо
                                            $order->notifyCreation(); // Под капотом выброс события $order->record(new OrderCreatedEvent())
                                            // при завершении этого потока приложения в какую-нибудь очередь вроде SQS ложится сериализованный OrderCreatedEvent
                                            // Подписчики на это событие делают свою логику вроде инвойсов или оплаты
                                            

                                            Понятно, что код неэквивалентен и переписать одно в другое невозможно, поэтому если что-то переделывать, то не так:
                                            На основе этого API можно создать сервис с restful-протоколом. Код нового сервиса — это код пакета, вокруг которого написан достаточно стандартный роутинг, запись логов, и прочий инфраструктурный код. А в старом коде вместо кода пакета появляется адаптер для http-вызовов через curl.
                                            а переделывать архитектуру и связи между логикой, дабы избежать «распределённых объектов».
                                              0
                                              спасибо, согласен, что так делать может быть неразумно, однако, проектирование SOA выходит за рамки темы статьи, и так 7 печатных страниц с примерами вышло :)
                                          0

                                          Как раз недавно писал решение для асинхронного запроса в curl, но выглядит это достаточно костыльно и чужеродно для PHP.

                                            0
                                            Да, промисы, генераторы, корутины писать непривычно.
                                    –1
                                    Как придумать себе проблемы на ровном месте.

                                    >>Автор был инициатором обновления PHP с 5-ой версии на 7-ую для приложения с очень большим объемом кода, и эта задача была успешно выполнена командой за три недели.
                                    Это ненормально. У нормального enterprise языка должна быть обратная совместимость, а не отключения части функций по желанию левой пятки индуских мантейнеров. Как бы не хаяли Microsoft, но у них обратная совместимость во главе всего.
                                      0

                                      А php давно стал enterprise языком?


                                      а не отключения части функций по желанию левой пятки индуских мантейнеров

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

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

                                          Вы это из каких моих слов сделали такой вывод? Нет, мне не нравится, но я за избавление от груза легаси.

                                            0
                                            А я не за избавление от груза легалиси, если он лежит себе и не мешает. Можно ведь старый функционало просто не использовать, если не нравится. Можно не удалять синонимы функций просто так, если они больше не нравятся майнтейнерам.
                                              0

                                              Создайте свой язык, со своими правилами. Ну или не используйте тот, что не нравится. Используйте тот, что нравится.

                                                0
                                                Вынести legacy код в изолированный сервис с ограниченным доступом, и забить на обновления — вполне рабочее решение. Встречайте в каждом банкомате мира :)
                                                  0
                                                  memcache, redis, postgresql и т.д. — как собиратесь подключать к этому сервису?:)
                                                    0
                                                    Начинаем с bounded contexts :) Этот вопрос выходит за рамки статьи, я не готов писать продолжение в комментарии.
                                                      0

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

                                              0
                                              Да, на php работает 80% middleware крупных интернет-проектов всего мира. Ссылка. 30% — wordpress, а 50% — системы enterprise-уровня.

                                              PHP активно используется в корпоративной среде, наряду с Java, почему и наблюдаем развитие только тех фич, которые востребованы в крупных сложных проекта. Никита с Димой не бесплатно работают. Аннотации, JIT, типизация в CMS использоваться не будут. Это сложный функционал, развитие которого финансируется enterprise-проектами.
                                                0
                                                Да, на php работает 80% middleware крупных интернет-проектов всего мира.

                                                Это не значит, что язык php — это язык энтерпрайз-уровня. Язык энтерпрайз-уровня — это Java и C#.

                                                  0
                                                  К сожалению, нет стандартов NIST, Open Group или каки-либо других для оценки языков программирования с точки зрения применимости в enterprise.
                                                  Я бы назвал Java основным языком для мобильных устройств, но в промышленности он тоже широко применяется.
                                                    0

                                                    Для меня показатель банки и прочие финансовые институты. И там php, если и есть где-то (и то, сомневаюсь, что есть), то исключительно в какой-то части а-ля backend for frontend, не более того.

                                              0
                                              У нормального enterprise языка должна быть обратная совместимость, а не отключения части функций

                                              Спасибо за комментарий. Java, C#, MySQL и Postgres так же ломают обратную совместимость в новых мажорных версиях.

                                              Добавлю уточнение: обновление приложения в моем случае было полным, соблюдали SLA 5 девяток, то есть, бешовно.

                                            Only users with full accounts can post comments. Log in, please.