12 факторов: хранить состояние вне процесса
Запущенное приложение не должно рассчитывать на внутреннее состояние процесса. Когда-то было принято хранить на диске буквально всё. Кроме сессий в /var/tmp, нормальной практикой было сжимать загруженные в админку изображения в момент их запроса. Типа запросил пользователь фоточку товара 400x400, а у нас в кеше есть только 200x200 — идём в оригинал, сжимаем его в 400x400 и сохраняем на диск. Другим пользователям уже отдаём кешированную версию. Думаю, не нужно рассказывать, чем это плохо, если вы читали мои предыдущие посты про 12 факторов.
Такой подход до сих пор проскакивает в маргинальных инструментах вроде django-webpack-loader: эта штука собирает JS в момент запроса и сохраняет где-то на диске. Конечно же, если вы деплоите JS, который почему-то не собирается, вы об этом узнаете не в CI, а от пользователей, которым прилетит неработающий бандл.
Не делайте так. Все преобразования выполняйте в процессе CI, а промежуточные данные храните в отдельных сервисах — данные в базе, сессии в кеше, а картинки где-нибудь в S3.
Запущенное приложение не должно рассчитывать на внутреннее состояние процесса. Когда-то было принято хранить на диске буквально всё. Кроме сессий в /var/tmp, нормальной практикой было сжимать загруженные в админку изображения в момент их запроса. Типа запросил пользователь фоточку товара 400x400, а у нас в кеше есть только 200x200 — идём в оригинал, сжимаем его в 400x400 и сохраняем на диск. Другим пользователям уже отдаём кешированную версию. Думаю, не нужно рассказывать, чем это плохо, если вы читали мои предыдущие посты про 12 факторов.
Такой подход до сих пор проскакивает в маргинальных инструментах вроде django-webpack-loader: эта штука собирает JS в момент запроса и сохраняет где-то на диске. Конечно же, если вы деплоите JS, который почему-то не собирается, вы об этом узнаете не в CI, а от пользователей, которым прилетит неработающий бандл.
Не делайте так. Все преобразования выполняйте в процессе CI, а промежуточные данные храните в отдельных сервисах — данные в базе, сессии в кеше, а картинки где-нибудь в S3.
Журнал Glamour включил трансгендера, PHP разработчика, активного контрибьютора Symfony, Манро Бергдорф в свою премию «Женщина Года».
Этот трап-активист ещё в 2017 году стал первой трансгендерной моделью L'Oreal, но был уволен за расистские высказывания:
«Честно, у меня больше нет сил говорить о расистском насилии со стороны белых людей. Да, ВСЕХ белых людей. Поскольку большинство из вас даже не осознает, что все ваше существование, привилегии и успех как расы построен на спинах, крови и гибели цветных людей. Все ваше существование пропитано расизмом».
Однако уже в 2020 году, после смерти Джорджа Флойда, L'Oreal решили извиниться перед трапом и возобновили с ним сотрудничество.
Этот трап-активист ещё в 2017 году стал первой трансгендерной моделью L'Oreal, но был уволен за расистские высказывания:
«Честно, у меня больше нет сил говорить о расистском насилии со стороны белых людей. Да, ВСЕХ белых людей. Поскольку большинство из вас даже не осознает, что все ваше существование, привилегии и успех как расы построен на спинах, крови и гибели цветных людей. Все ваше существование пропитано расизмом».
Однако уже в 2020 году, после смерти Джорджа Флойда, L'Oreal решили извиниться перед трапом и возобновили с ним сотрудничество.
Эффективное проектирование агрегатов
Эссе Вона Вернона в трёх коротких частях, которое поможет с пониманием паттерна агрегат.
Прочитав его, вы получите ответы на следующие вопросы:
• как и зачем проектировать маленькие агрегаты;
• что такое истинные инварианты и как их искать;
• почему Value Object предпочтительнее, чем Entity;
• когда нужна мгновенная (immediate), а когда конечная (eventual) согласованность;
• когда позволительно изменять несколько агрегатов в одной транзакции.
По сути, это квинтэссенция нескольких важных глав из синей и красной книги по DDD.
https://dddcommunity.org/library/vernon_2011/
Эссе Вона Вернона в трёх коротких частях, которое поможет с пониманием паттерна агрегат.
Прочитав его, вы получите ответы на следующие вопросы:
• как и зачем проектировать маленькие агрегаты;
• что такое истинные инварианты и как их искать;
• почему Value Object предпочтительнее, чем Entity;
• когда нужна мгновенная (immediate), а когда конечная (eventual) согласованность;
• когда позволительно изменять несколько агрегатов в одной транзакции.
По сути, это квинтэссенция нескольких важных глав из синей и красной книги по DDD.
https://dddcommunity.org/library/vernon_2011/
12 факторов: явные зависимости
Все зависимости вашего приложения должны быть выражены явно. И я говорю не только о списке зависимостей из package.json, Pipfile, Cargo.toml или что там у вас — нужны именно все зависимости.
Если вашему приложению, чтобы запуститься, нужна установленная русская локаль, древняя версия openssl или elasticsearch, слушающий на 9000 порту, и вы не нашли способ понятно выразить это в репозитории, вам будет очень грустно всякий раз, когда придётся сталкиваться с девопсом. Вы будете чертыхаться всякий раз, поднимая окружение для CI, разворачивая проект на локальной машине или добавляя новую тачку в парк серверов.
В README такие требования описывать глупо — никто не будет их там искать: люди обычно не читают документацию. А даже если и читают, то вряд ли вы найдёте в себе силы актуализировать там версии зависимостей. Описывать их имеет смысл на языках, которые позволят автоматически воспроизводить нужный вашему приложению контекст. В худшем случае это какая-нибудь виртуалка на Vagrant (надеюсь никогда больше его не увидеть), чуть получше — плейбуки Ansible. Норм — это Dockerfile и docker-compose.
Все зависимости вашего приложения должны быть выражены явно. И я говорю не только о списке зависимостей из package.json, Pipfile, Cargo.toml или что там у вас — нужны именно все зависимости.
Если вашему приложению, чтобы запуститься, нужна установленная русская локаль, древняя версия openssl или elasticsearch, слушающий на 9000 порту, и вы не нашли способ понятно выразить это в репозитории, вам будет очень грустно всякий раз, когда придётся сталкиваться с девопсом. Вы будете чертыхаться всякий раз, поднимая окружение для CI, разворачивая проект на локальной машине или добавляя новую тачку в парк серверов.
В README такие требования описывать глупо — никто не будет их там искать: люди обычно не читают документацию. А даже если и читают, то вряд ли вы найдёте в себе силы актуализировать там версии зависимостей. Описывать их имеет смысл на языках, которые позволят автоматически воспроизводить нужный вашему приложению контекст. В худшем случае это какая-нибудь виртуалка на Vagrant (надеюсь никогда больше его не увидеть), чуть получше — плейбуки Ansible. Норм — это Dockerfile и docker-compose.
12 факторов: одна кодовая база — много деплоев
Хорошее приложение полностью находится под системой контроля версий и лежит в одном репозитории (мы здесь говорим о приложениях, а не о распределённых системах). Содержимое репозитория раскатывается в любую среду — на прод, на машину разработчика, на стейджинг, если надо.
Если у вас прод использует одну кодовую базу, а дев-машина или стейджинг — немного другую, то рано или поздно у вас на прод проскочит ошибка, которую нельзя воспроизвести нигде, кроме прода. Вот и придётся затягивать упоротые решения вроде rookout или просто писать console.log.
Код на инстансах при этом может отличаться — вполне нормально выкатывать фиче-бранчи с какими-нибудь коммитами, которые ещё не попали на прод, но уже хочется потестировать. Главное, чтобы это были разные версии одного репозитория.
Хорошее приложение полностью находится под системой контроля версий и лежит в одном репозитории (мы здесь говорим о приложениях, а не о распределённых системах). Содержимое репозитория раскатывается в любую среду — на прод, на машину разработчика, на стейджинг, если надо.
Если у вас прод использует одну кодовую базу, а дев-машина или стейджинг — немного другую, то рано или поздно у вас на прод проскочит ошибка, которую нельзя воспроизвести нигде, кроме прода. Вот и придётся затягивать упоротые решения вроде rookout или просто писать console.log.
Код на инстансах при этом может отличаться — вполне нормально выкатывать фиче-бранчи с какими-нибудь коммитами, которые ещё не попали на прод, но уже хочется потестировать. Главное, чтобы это были разные версии одного репозитория.
Если вам вдруг когда-нибудь потребуется найти/заменить символы перевода строки (\r, \n, \r\n), например для нормализации, можно воспользоваться малоизвестным классом \R.
preg_replace('/\R/', PHP_EOL, "text\rwith\nvarious\r\nline endings\n\n")
https://3v4l.org/5cBcW
preg_replace('/\R/', PHP_EOL, "text\rwith\nvarious\r\nline endings\n\n")
https://3v4l.org/5cBcW
3v4l.org
Online PHP editor | output for 5cBcW
Run your php code online; get statistics, vld output and compare output from all versions.
Теперь у Symfony тоже есть библиотека для работы с UUID: composer req symfony/uid. Сравним.
ramsey/uuid
- процесс генерации реализован прямо в библиотеке и разложен по ООП полочкам, легко расширяется;
- поддержка GUID;
- поддержка экспериментального UUID v6;
- интеграции: ramsey/uuid-doctrine , ramsey/uuid-console .
symfony/uid
- элементарная обёртка над полифилом symfony/polyfill-uuid, который имитирует функционал PHP-расширения UUID и при этом работает быстрее;
- поддержка UUID v6;
- поддержка ULID;
- обсуждаются планы по интеграции с другими компонентами и библиотеками.
Как только допилят интеграцию, можно будет в новых проектах ставить symfony/uid просто из-за скорости работы.
____
Есть недовольные, мол, Symfony продолжает добавлять компоненты, которые заменяют существующие неплохие проекты. Возможно, имеется в виду symfony/messenger, который оставил в тени simple-bus/simple-bus.
Мое мнение, что если пакет лицензирован MIT, то по определению ни у кого не должно быть никаких претензий к тем, кто создает похожие библиотеки.
Во-вторых, Symfony не всегда делает хорошо, например, к тому же Messenger и другим компонентам немало вопросов по архитектуре. Разработки Symfony никак не блокируют стремление к прекрасному, скорее стимулируют. Да и в целом конкуренция — это почти всегда хорошо, она препятствует застою, предоставляет выбор. Может быть Ben Ramsey не релизнул бы 4.0 еще полгода, если бы Symfony не анонсировала свой UID 😉
ramsey/uuid
- процесс генерации реализован прямо в библиотеке и разложен по ООП полочкам, легко расширяется;
- поддержка GUID;
- поддержка экспериментального UUID v6;
- интеграции: ramsey/uuid-doctrine , ramsey/uuid-console .
symfony/uid
- элементарная обёртка над полифилом symfony/polyfill-uuid, который имитирует функционал PHP-расширения UUID и при этом работает быстрее;
- поддержка UUID v6;
- поддержка ULID;
- обсуждаются планы по интеграции с другими компонентами и библиотеками.
Как только допилят интеграцию, можно будет в новых проектах ставить symfony/uid просто из-за скорости работы.
____
Есть недовольные, мол, Symfony продолжает добавлять компоненты, которые заменяют существующие неплохие проекты. Возможно, имеется в виду symfony/messenger, который оставил в тени simple-bus/simple-bus.
Мое мнение, что если пакет лицензирован MIT, то по определению ни у кого не должно быть никаких претензий к тем, кто создает похожие библиотеки.
Во-вторых, Symfony не всегда делает хорошо, например, к тому же Messenger и другим компонентам немало вопросов по архитектуре. Разработки Symfony никак не блокируют стремление к прекрасному, скорее стимулируют. Да и в целом конкуренция — это почти всегда хорошо, она препятствует застою, предоставляет выбор. Может быть Ben Ramsey не релизнул бы 4.0 еще полгода, если бы Symfony не анонсировала свой UID 😉
Чтобы узнать, какие пакеты пора обновить, используем команду composer outdated или просто composer out.
Желтым будут выделены зависимости, которые обновились в мажорной версии. В соответствии с правилами семантического версионирования мажорный релиз допускает нарушение обратной совместимости. Это означает, что обновление такого пакета потребует времени — нужно будет почитать заметки к релизу, проверить все сценарии и места использования и скорее всего покодить. Не стоит сразу накидываться на новые мажорные версии — пусть настоятся, получат свои хотфиксы.
Красным будут выделены зависимости, которые обновились в минорной или патч версиях. Такие релизы не должны нарушать обратной совместимости, при условии, конечно, что авторы библиотек знают про принципы версионирования. Патчи рекомендую накатывать сразу — скорее всего они содержат багфиксы или закрывают уязвимости. Минорные апдейты, как правило, содержат новые фичи и депрекации старых. Тут обновляемся по вкусу, но тоже лучше не затягивать, иначе потом все за один присест придется осваивать.
Чтобы скрыть мажорные апдейты (наличие которых форсирует желтый цвет даже если пакет в текущем мажоре получил патч), используем флаг --minor-only (-m).
Про остальные флаги и другие полезные команды читаем в официальной документации Composer.
Желтым будут выделены зависимости, которые обновились в мажорной версии. В соответствии с правилами семантического версионирования мажорный релиз допускает нарушение обратной совместимости. Это означает, что обновление такого пакета потребует времени — нужно будет почитать заметки к релизу, проверить все сценарии и места использования и скорее всего покодить. Не стоит сразу накидываться на новые мажорные версии — пусть настоятся, получат свои хотфиксы.
Красным будут выделены зависимости, которые обновились в минорной или патч версиях. Такие релизы не должны нарушать обратной совместимости, при условии, конечно, что авторы библиотек знают про принципы версионирования. Патчи рекомендую накатывать сразу — скорее всего они содержат багфиксы или закрывают уязвимости. Минорные апдейты, как правило, содержат новые фичи и депрекации старых. Тут обновляемся по вкусу, но тоже лучше не затягивать, иначе потом все за один присест придется осваивать.
Чтобы скрыть мажорные апдейты (наличие которых форсирует желтый цвет даже если пакет в текущем мажоре получил патч), используем флаг --minor-only (-m).
Про остальные флаги и другие полезные команды читаем в официальной документации Composer.
Чем себя занять в моменты, когда на работе нужно написать много рутинного кода, ну или задачи по-просту не интересные? Кто-то ходит по собесам просто ради интереса, кто-то пишет пет проекты, а кто-то фрилансит. В перерывах между пет-проектами лично я люблю решать небольшие конЕчные (не конЧЕнные=)) задачки.
Как это работает? Вам дается задание, область написания кода и кнопка "test", по нажатию на которую запускаются автотесты, проверяющие ваш код. В некоторых заданиях дополнительно еще ограничено время. Кстати, обычно очень широкий выбор языков программирования.
Вот совсем коротенький список, буду рад, если в комментариях дополните:
1. www.hackerrank.com Платформа запущена в 2012 году. Помимо просто решения задач предлагает еще получить сертификаты в той или иной области, а также есть площадка для поиска работы. По сути, хакерранк это топовая платформа, которая также используется крупными компаниями для найма на работу через решение задач. К примеру - ежегодный Badoo конкурс о найме на работу в Лондон - размещался именно там.
2. www.codewars.com - очень похожая на первую, однако тут упор сделан на писькомерство. При регистрации вам выдают, кажется, 8 кю (8 уровень, как в карате-до), и по решению задач присваивают новый кю/дан. После решения задачи вы попадаете на экран, на котором есть полный список всех решений всех пользователей с лайками по категориям "бест практис" и "самое умное решение". Самое топовое решение наверху. Можно его форкнуть и улучшить. Ну и можно контрибьютить - добавлять свои задания
Как это работает? Вам дается задание, область написания кода и кнопка "test", по нажатию на которую запускаются автотесты, проверяющие ваш код. В некоторых заданиях дополнительно еще ограничено время. Кстати, обычно очень широкий выбор языков программирования.
Вот совсем коротенький список, буду рад, если в комментариях дополните:
1. www.hackerrank.com Платформа запущена в 2012 году. Помимо просто решения задач предлагает еще получить сертификаты в той или иной области, а также есть площадка для поиска работы. По сути, хакерранк это топовая платформа, которая также используется крупными компаниями для найма на работу через решение задач. К примеру - ежегодный Badoo конкурс о найме на работу в Лондон - размещался именно там.
2. www.codewars.com - очень похожая на первую, однако тут упор сделан на писькомерство. При регистрации вам выдают, кажется, 8 кю (8 уровень, как в карате-до), и по решению задач присваивают новый кю/дан. После решения задачи вы попадаете на экран, на котором есть полный список всех решений всех пользователей с лайками по категориям "бест практис" и "самое умное решение". Самое топовое решение наверху. Можно его форкнуть и улучшить. Ну и можно контрибьютить - добавлять свои задания
Параметры vs Аргументы
Параметр — это переменная в сигнатуре функции. В PHP параметры характеризуются именем, типом, позицией, значением по умолчанию, вариативностью (...) и возможностью передачи по ссылке (см. ReflectionParameter).
Аргумент — это выражение/значение, используемое при вызове функции.
Именно поэтому $reflectionMethod->getParameters(), но func_get_args и InvalidArgumentException.
На мой взгляд простой, но важный нюанс 👌
Параметр — это переменная в сигнатуре функции. В PHP параметры характеризуются именем, типом, позицией, значением по умолчанию, вариативностью (...) и возможностью передачи по ссылке (см. ReflectionParameter).
Аргумент — это выражение/значение, используемое при вызове функции.
Именно поэтому $reflectionMethod->getParameters(), но func_get_args и InvalidArgumentException.
На мой взгляд простой, но важный нюанс 👌
Как часто вы сталкиваетесь с проблемой, когда для тестирования задачи приходится менять код? Тесты отложенной отправки письма, генерации чего-то по расписанию раз в неделю и т.д.
Badoo имеет свое собственное решение, которое упрощает жизнь тестировщикам.
Все тут:
https://telegra.ph/API-dlya-QA-testiruem-fichi-bez-dostupa-k-kodu-12-01
Badoo имеет свое собственное решение, которое упрощает жизнь тестировщикам.
Все тут:
https://telegra.ph/API-dlya-QA-testiruem-fichi-bez-dostupa-k-kodu-12-01
Telegraph
API для QA: тестируем фичи без доступа к коду
Многие фичи приложения невозможно быстро протестировать, не меняя исходный код. Представьте типичную задачу, с которой может столкнуться каждый разработчик: через три дня после регистрации пользователю нужно предложить купить премиум-доступ к продукту со…
Раньше, чтобы создать nullable ValueObject из nullable примитива, приходилось писать колбасу вроде
null === $stringClientId ? null : ClientId::fromString($stringClientId).
Сегодня условные типы Psalm позволяют перенести if в статический конструктор:
/**
* @template T of ?string
* @psalm-param T $id
* @psalm-return (T is null ? null : self)
*/
public static function fromString(?string $id): ?self
{
if (null === $id) {
return null;
}
Assert::uuid($id);
return new self($id);
}
ClientId::fromString($stringClientId)
https://psalm.dev/r/ab0090be4c
null === $stringClientId ? null : ClientId::fromString($stringClientId).
Сегодня условные типы Psalm позволяют перенести if в статический конструктор:
/**
* @template T of ?string
* @psalm-param T $id
* @psalm-return (T is null ? null : self)
*/
public static function fromString(?string $id): ?self
{
if (null === $id) {
return null;
}
Assert::uuid($id);
return new self($id);
}
ClientId::fromString($stringClientId)
https://psalm.dev/r/ab0090be4c