Русский
Русский
English
Статистика
Реклама

Блог компании skyeng

PHP 8 и развитие языка в 30 вопросах и ответах

24.02.2021 12:05:30 | Автор: admin
В конце ноября мы провели стрим с Никитой Поповым и Дмитрием Стоговым, ключевыми контрибьюторами ядра PHP. За полчаса мы получили 100+ вопросов и ребята не успели ответить на все. Поэтому я сгруппировал оставшиеся сообщения по темам, отсеял совсем специфические и собрал ответы в текстовом виде. Все острые и холиварные вопросы оставил.



Готовя ответы, по многим пунктам я консультировался с Никитой и другими активными участниками сообщества. Кстати, в эту субботу, 27 февраля, мы проводим новый стрим! Будет пара докладов, несколько дискуссий, интересные гости и возможность задать новые вопросы. Читайте те, что под катом и подключайтесь, чтобы задать новые.



Планируется ли дальнейшее развитие type hinting? Например, для сигнатур функций.


Простор для улучшений, безусловно, есть. Со времени стрима появились Enum RFC и соответственно возможность их использовать в декларациях типов.

Из того что обсуждалось:
Intersection Types RFC обсуждался несколько раз, вот тут есть немного контекста.
Дженерики см. ниже.
Тайпхинты для Callable тут было несколько RFC: Typesafe callable, Callable prototypes, Functional interfaces. То есть запрос есть, но пока не было удачного RFC.
Алиасы типов обсуждалось в контексте юнион типов, но пока без отдельного RFC.

Конкретных планов по этим идеям пока нет.

Частый вопрос будет ли асинхронный PHP?


Мы получили 13 вопросов за время ноябрьского стрима, вот что именно спрашивали люди
Появится ли когда-нибудь неблокирующий ввод/вывод?

Будет ли добавлена какая-то асинхронность в php имеется в виду встроенная в ядро.

Как оцениваете github.com/amphp/ext-fiber, какая вероятность пройти rfc?

Будет ли РНР асинхронным?

Планируется ли добавление функционала Promise`ов?

Будет ли как то развиваться многопоточное/асинхронное программирование в дальнейших версиях? Есть конкретные планы?

LibUV. В каком состоянии интеграция LibUV в ZendEngine? И будет ли она?

Будет ли event loop-ы на РНР конкурентные с Нодой?

Планируется ли реализация такого же event loop как в javascrypt?

Theoretically, is it possible, to implement light threads, like coroutines in Kotlin<3, via JIT and FFI? Or is it possible to create an async mechanism using JIT?

Есть ли будущее у асинхронного пхп (reactphp, swoole, asyncphp)?

Будет ли движение php в сторону асинхронщины?

Планируется ли внедрение асинхронности и/или многопоточности в следующих версиях языка?

Смотря что понимать под асинхронным PHP.

Писать неблокирующий код на PHP можно уже сейчас с помощью Amp, ReactPHP, Workerman.

Активно обсуждается предложение по файберам RFC и готова реализация. Если оно будет принято, то это упростит работу с пакетами типа ReactPHP и Amp. Подробнее было на канале PHP Digest.

Вот примеры, как может выглядеть аналог async/await в PHP 8.1 + Amp v3 и на ReactPHP.

Кроме того, еще есть Swoole. Это расширение для PHP, в котором реализовано уже все для создания полностью асинхронных приложений, в том числе драйверы БД, а также корутины и каналы. И даже использование стандартных функций для работы с IO (например file_get_contents()). В 2017 я его не рекомендовал, но сейчас он оброс отличной экосистемой и готов для использования в продакшне.

Есть более минималистичное подмножество Swoole: swow. Теоретически, у него есть даже шансы быть включенным в ядро. Но пока об этом рано говорить. Дождемся результатов голосования по файберам.

Что по дженерикам в PHP?


Мы получили 5 вопросов за время ноябрьского стрима, вот что именно спрашивали люди
Будут ли в php generic`и?

Планируется ли реализация дженериков для PHP? Когда она может увидеть свет?

Дженерики. Будут ли, и когда. Полноценные, или фейковые, без рантайм проверки.

Ну когда же будут дженерики?

Есть ли в планах дженерики?

Короткий ответ: большинство контрибьюторов хотят, но пока нет нормального способа их реализовать в PHP. Не стоит надеяться, что в ближайшее время их добавят в ядре.




Дальше перевод ответа Никиты на Reddit.

Для тех, кто не слишком знаком, есть три широких способа реализации дженериков:
  • Type-erasure (стираемые): Дженерики просто удаляются и Foo<T> становится Foo. Во время выполнения дженерики ни на что не влияют, и предполагается, что проверки типов осуществляются на каком-то предварительном этапе компиляции/анализа (прим. Python, TypeScript).
  • Reification (реификация): Дженерики остаются в рантайме и могут быть на этом этапе использованы (и в случае PHP, могут быть проверены в рантайме).
  • Monomorphization (мономорфизация): С точки зрения пользователя, это очень похоже на реификацию, но подразумевает, что для каждой комбинации аргументов дженериков генерируется новый класс. То есть, Foo<T> не будет хранить информацию что, класс Foo инстанциирован с параметром T, а вместо этого будут созданы классы Foo_T1, Foo_T2, , Foo_Tn специализированный для данного типа параметра.

> Вы сказали, что мономорфизированные дженерики повысят производительность, а reified дженерики потребуют много изменений во всей кодовой базе.

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

Мономорфизация как главная стратегия реализации не имеет смысла в PHP. Она важна для таких языков, как C++ или Rust, где возможность специализировать код для определенных типов имеет большое значение для производительности (и даже в этом случае размер кода остается большой проблемой). В PHP мы не получим от него достаточной выгоды в плане производительности, чтобы оправдать накладные расходы по памяти (опять же, когда речь идет об общей мономорфизации). Тем более что непонятно, как можно кэшировать мономорфизированные методы в opcache (из-за требований иммутабельности).

Единственная причина, по которой мономорфизация была предложена в качестве стратегии реализации, заключается в том, что она упростит реализацию наивной модели дженериков. Предполагается, что нам просто нужно сгенерировать новые классы для всех комбинаций, а остальным частям ядра вообще ничего не нужно знать о дженериках. Однако эта идея ломается, если учесть вариативность дженерик параметров (Traversable<int> это Traversable<int|string>), поскольку такие отношения не могут быть смоделированы без непосредственного знания дженерик параметров.

> Не было заметно особо отзывов по исследованию дженериков, которое вы опубликовали на GitHub. Были ли закулисные разговоры об этом, или это все?

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

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

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

function test(): List<int> {    // We don't want to write this:    return new List<int>(1, 2, 3);    // We want to write this:    return new List(1, 2, 3);}

Мы точно не хотели бы, чтобы людям на PHP пришлось писать больше типов, чем на современном статически типизированном языке, таком как Rust. Однако, я не совсем понимаю, как вывод типов для дженерик параметров в настоящее время может быть интегрирован в PHP. В первую очередь из-за очень ограниченного представления о кодовой базе, которая есть у компилятора PHP (он видит только один файл за раз). Приведенный выше пример очевиден, но почти все, что выходит за его рамки, кажется быстро переходящим в "невозможное".

Это оставляет меня в конфликте с поддержкой дженериков в PHP, и это также является причиной, по которой я не настаивал на обсуждениях этой темы. Я сам не уверен, что это хорошая идея.

> Вы рассматривали стираемые дженерики, как это делает Python?

And that leaves us with the cowards way out

Во-первых, я думаю, что неправильно говорить, что у Python есть стираемые дженерики. У Python все типы стираемые и это все меняет. Если вся ваша модель типов заключается в том, что аннотации типов игнорируются во время выполнения и проверяются отдельным статическим анализатором, то это отдельный самодостаточный подход. Это как phpdoc в PHP.

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

Хуже того, в PHP даже не будет встроенного валидатора типов, а проблема будет делегирована стороннему инструменту статического анализа, такому как psalm, phpstan или phan (или, по крайней мере, как я понимаю). Это означает, что тип может быть нарушен по умолчанию, и вы должны пойти еще добавить что-то, чтобы предотвратить это.

Еще хуже (черт возьми, насколько хуже может быть?), у нас в PHP есть разделение типов на слабое и строгое. Типы в PHP это не просто декларации типов, они также могут действовать как приведение типов!

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

class StringList {    public function add(string $value) { $this->data[] = $value; }}$list = new StringList;$list->add(42);var_dump($list); // ["42"]


class List<T> {    public function add(T $value) { $this->data[] = $value; }}$list = new List<string>;$list->add(42);var_dump($list); // [42]

Даже strict_types=1 не полностью спасает нас от этого, потому что преобразования int->float по-прежнему разрешены.

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

Sorry, I just don't have a good answer for you :(


Есть попытка стандартизировать синтаксис и семантику дженериков в PHP: github.com/DaveLiddament/php-generics-standard. Пока она довольно сырая и на ранней стадии обсуждения.

Что можно сделать, чтобы разработка PHP стала доступной более широкому кругу разработчиков?


Под этим заголовком мы объединили 3 оригинальных вопроса с ноябрьского стрима
Расскажите, пожалуйста, о текущем состоянии в области публикаций о внутреннем устройстве PHP (яызка и виртуальной машины). Есть ли что-то, что может помочь, кроме непосредственно чтения исходников?

Какой нужен минимум знаний для разработки ядра и вирт машины?
Как попасть к вам в команду? Спасибо за ответ!

Когда можно будет писать php на php?

Ядро PHP в обозримом будущем останется на C. Поэтому если есть желание контрибьютить, то придется с ним разобраться. Никита обновляет PHP Internals book, в котором описаны внутренности ядра.

И есть статья Никиты PHP 7 Virtual Machine и даже перевод она актуальна и из нее можно почерпнуть все самое важное для старта.

Что касается расширений, то тут есть потенциал для развития. Никита и Дмитрий занимаются исследованием возможности писать расширения на PHP. Ждем результатов этого исследования.

Планируется ли открыть внутреннее API PHP наружу через FFI, чтобы писать экстеншены на самом языке? С учетом отказа от PECL в PHP 8 вопрос становится актуальным.


Такого плана нет.

Задепрекейчен сам инструмент pecl, который использовался для установки расширений, потому что он использовал PEAR. А расширения, конечно же, не задепрекейчены.

Можно ли будет подключать к PHP модули на других языках, кроме C? Я видел примеры подключения Rust через FFI и это очень криво выглядит.


Теоретически это возможно, но технически никто не занимался написанием нужных интерфейсов для FFI.

В качестве альтернативы FFI есть возможность подключения wasm модулей: wasmerio/wasmer-php.

Какое будущее у FFI?


Кажется, что в FFI уже есть все, чтобы его использовать. Пока использование не особо активно.

Убили короткий тэг. Чем руководствовались, какой в этом смысл?


Если речь про <?, то его не убили. Было горячее обсуждение RFC, но в итоге решили, что в ближайшие 5 лет трогать его не будут.

Зачем добавляют mixed type?


Есть два основных аргумента в его пользу:

mixed сигнализирует о том, что тип не забыли указать, просто он не может быть уточнён,
mixed часто фигурирует в документации PHP.

Подробнее можно прочитать в предложении RFC.

Подписывайтесь на канал PHP Digest, чтоб узнавать про такие вещи первыми. Про mixed была заметка еще в мае 2020.

Будет ли развитие рефлексии? Изменение проперти типов?


Изменения типов свойств, то есть ReflectionProperty::setType, и вообще любых изменений классов не будет никогда. Классы неизменяемы это часть философии языка.

Почему Reflection медленный?


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

Почему php разработчики получают меньше, чем Java разработчик (примерно одного уровня). Или это не так?


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

Во-первых, от сложности задач и уровня ответственности, который предполагают эти задачи.

Поскольку на PHP делается очень много простого веба (небольшие сайты, CMS и т. п.), то и средний уровень зарплат возможно ниже.

А во-вторых, от особенностей рынка.

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

Кроме того, что такое Java и PHP разработчики примерно одного уровня? Как вы сравниваете?

Почему стоит выбрать PHP, а не Go или Node.js?


Выбирайте, что больше нравится.

PHP это в основном веб-разработка. Сейчас более популярны "универсальные" языки типа Python и JS. Планируется ли расширять зону применения PHP?


PHP, как язык, такой же универсальный как JS или Python. Вопрос в рантаймах, то есть где его можно запускать. Станет ли возможным запускать PHP в браузере, как JS? Не думаю, потому что зачем?

Одним из мотивов для FFI и JIT как раз был потенциал применения PHP в других сферах. Что из этого выйдет поживем-увидим.

Планируете ли вы интегрировать final свойства в PHP, как, например, immutable data classes в Java?


Идея иммутабельности в том или ином виде постоянно витает в PHP-сообществе. Были разные RFC на эту тему. Скорее всего, что-то будет.

Есть подробное исследование темы от Larry Garfield. И есть черновик RFC по аксессорам свойств, который позволит делать иммутабельные объекты.

Планируется ли уменьшение бюрократии для небольших патчей? Упираюсь в то, что ради мелкого патча просят создать RFC с полным фаршем.


Давайте конкретные примеры. RFC требуются для изменений в синтаксисе языка или при поломке обратной совместимости. Очень много изменений проходит обычными пул-реквестами без RFC.

Если нужна помощь с оформлением RFC пишите либо мне pronskiy, либо @Danack. Кстати, можно ознакомиться с его репозиторием c непринятыми RFC github.com/Danack/RfcCodex и документом по этикету RFC.

Почему фичи PHP 8 такие сырые?


Привет, Кирилл serafimarts :-) Жизнь слишком коротка, чтобы закончить хоть что

  1. Именованные аргументы не гарантируют совместимость на уровне интерфейсов и значительно увеличивают проблемы BC для OS библиотек.

    Есть предложение по атрибуту для задания алиасов #[NamedParameterAlias].
  2. Атрибуты не имеют возможности иерархичной/вложенной декларации и текущий API не позволяет их ввести нормально в будущем.

    См. ниже.
  3. Property Promotion не доделан: Зачем нужны фигурные скобки у конструктора с такими аргументами? Как декларативно их прокинуть в родительский конструктор?

    Не помню, чтоб кто-то поднимал этот вопрос. Почему ты не задал его во время обсуждения RFC?
  4. Почему добавили match, нарушающий дизайн языка? А если добавляются выражения, то где ""$some = foreach (...) => ..."" или ""$some = if(...) => ...""?"

    Видимо, потому, что другие выражения мало кому нужны. Всегда можно создать RFC.
  5. Зачем добавлять union types без возможности их внешней декларации: "type iterable = array | \Traversable""? Почему только дизъюнкция и нет конъюнкции типов?

    Алисы типов годная идея, обсуждается, пока без конкретных планов. По поводу конъюнкции см. ответ в первом вопросе про развитие тайпхинтов.
  6. Зачем костыль со Stringable, но не добавлять такие же неявные имплементации Countable, Serializable, etc...? Почему утиная типизация только для Stringable?

    Stringable нужен, чтоб можно было использовать объект с __toString() там, где стоит тайпхинт string. C Countable и Serializable такой проблемы нет.


Иногда это выбор между сделать так или вообще никак. В разных RFC голосования склоняется в ту или иную сторону.

Какие перспективы по введению nested attributes?


Ответ есть в самом RFC: Why are nested attributes not allowed?

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

То есть, теоретически поддержку сделать можно, но пока не планируется.

А почему с вводом аннотаций не ввели сразу какую то привязку к функциям или методам из коробки, как в том же питоне?


Это другой концепт. Атрибуты в PHP это метаданные, а в Python это декораторы.

Если хочется именно декораторы, возможно, наилучшим решением будет goaop/framework. Пока он, правда, не совместим с PHP 8.

Планируется ли улучшение SPL или поддержки разных структур данных в PHP?


Вот пара вопросов на этот счет от зрителей стрима
Есть какие-то планы по развитию/улучшению SPL в сторону https://github.com/php-ds
Планируется ли добавить в ядро PHP хорошо спроектированные популярные структуры данных: деревья, графы, списки, очереди, множества, кортежи и т.д.
Планируется ли добавление специализированных структур данных (кортежи, множества, деревья т.д.) в ядро?

Есть интерес улучшить эту часть PHP. Из недавнего: обсуждается возможность добавить неймспейс SPL.

Что касается php-ds, то его автор не хотел бы, чтоб расширение мерджили в ядро, потому что в этом случае он был бы привязан к релизному циклу PHP.

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


Да, такие планы есть, более того в каждом релизе что-то делается для этого.

Вот пример из недавнего: Restrict $GLOBALS usage ограничено использование $GLOBALS, что позволило избавиться от кучи внутренних проверок и упростить кодовую базу. Именно об этом говорил Дмитрий Стогов на стриме отвечая на вопрос: что стоило бы убрать в следующих версиях PHP.

Планируется ли в php добавлять библиотеки для машинного обучения?


Нет. А в каком языке они есть в ядре?

Можно использовать RubixML/ML или просто специализированные инструменты.

Как вы относитесь к гегемонии Laravel? AR/статик-методы/трейты


Популярность Laravel точно помогает PHP, а значит помогает и всем, кто пользуется языком.

AR/статик-методы дело вкуса. Про трейты Никиты уже высказался, а на стриме обсудили их вдоль и поперек.

Целесообразно ли поддерживать активно-растущий проект на php 5.6 или начинать понемногу переписывать на php 7 или 8?


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

Есть приведение типов, например int или array, можно сделать приведение типов к объекту?


Можно.

В php есть довольно старое расширение php SOAP. Будет ли оно как-то развиваться/дополняться?


Расширение очень старое и над ним никто активно не работает. Более того, в нем самое большое число открытых багов из всех расширений PHP.

Если вам нужен SOAP, то лучше использовать юзерленд реализации на PHP, а не расширение.

А с какой версии можно использовать или в typehint? CurlHandle|false


Объединенные типы появились в PHP 8.0.

А для чего добавили $object::class? Теперь будут споры что же использовать get_class() или ::class


Добавили для консистентности. Зачем спорить, если можно использовать только новый вариант? roll_safe.gif

Какое будущее у поддержки альтернатив сред выполнения? GraalVM как пример.


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

Будут ли когда-нибудь в PHP возможность запретить доступ к внутренним классам извне библиотеки? Чтобы библиотека предоставляла доступ только к интерфейсным классам, а внутренние использовать запрещено.


Такая идея возникала RFC Namespace visibility и даже чуть раньше была реализация подобного. Проблема в том, что хотелось бы private/internal на уровне пакетов. А у нас только неймспейсы и поэтому непонятно как это должно работать.

Пока остается использовать PHPDoc тэг @internal.

Будет ли возможность определить, какие трейты использует класс? По аналогии с class_implements


class_uses()

Применение каких новинок PHP 8 будет влиять на снижение производительности?


Почти всех :-) Хоть оно и незначительное. Кроме, разве что, проверки типов когда много используется наследования. Но в PHP 8.1 станет намного лучше благодаря inheritance cache.

Сколько можно выиграть в производительности, если убрать все проверки типов рантайме?


Зависит от приложения. Вот inheritance cache для Symfony дает прирост 8% это приблизительно и есть оверхед проверки типов.

Ребята, по памяти будут улучшения? как пример двигать sbrk при застоях и больших дырках


Такая проблема действительно может быть. Например, при использовании расширений, которые выделяют память, используя аллокатор операционной системы, а не PHP. Например, так ведет себя libxml. Но как бороться с этим непонятно. Пока решения нет.

Какие компании финансируют развитие php? Неужели новые версии php выпускают энтузиасты безвозмездно?


Прямо: Zend, JetBrains, Microsoft. Косвенно много других, например, MongoDB, Oracle,

Сколько человек работает над ядром пхп?


Фултайм работают трое: Никита Попов (JetBrains) и Дмитрий Стогов (Zend) над ядром, и Christoph M. Becker (Microsoft) над расширениями.

Активное участие принимают многие. Можно посмотреть по статистике контрибьюторов.

Вот облако тегов тех, кто приложил руку в PHP 8.0:

Картинка от php.watch.

Когда будет PHP 8.1.0?


Новые версии языка выходят каждый год приблизительно в ноябре-декабре. Соответственно, PHP 8.1 ожидается в конце 2021.

p.s. Подключайтесь к стриму в субботу, чтобы узнать больше о состоянии PHP в 2021 году



Подробнее..

Как я на коленке делал бесплатный курс программирования, о котором мечтал 3 года (и что пошло не так)

08.04.2021 10:11:09 | Автор: admin
Дело было в период локдауна. Каждое воскресенье в 8:30 утра кухня превращалась в превращалась в в импровизированную студию для вебинаров с лайвкодингом. Дело было в период локдауна. Каждое воскресенье в 8:30 утра кухня превращалась в превращалась в в импровизированную студию для вебинаров с лайвкодингом.

В этой статье я расскажу о MVP курса по обучению программированию, который возник благодаря удаленке.

Удаленщиком я стал по зову сердца, еще в 2017-м. Работа из дома высвободила мне около 10 часов в неделю и я впервые задумался о том, чтобы делиться опытом. Так я стал наставником в одной известной онлайн-школе, и остаюсь им по сей день: ревьюю код студентов, помогаю им с вопросами по учебным проектам.

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

Идеальный шторм

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

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

Шри-Ланка. Месяц до локдауна. И полтора месяца до старта всей затеи.Шри-Ланка. Месяц до локдауна. И полтора месяца до старта всей затеи.

Где-то в тот момент я прочитал о технике достижения целей Майкла Роуча. Алгоритм простой, всего 4 шага. Если кратко: поставить себе любую цель, найти человека, который тоже хочет к ней прийти, помогать ему бесплатно раз в неделю и искренне радоваться его достижениям.

Мне захотелось проверить этот метод на практике. Так что я подумал: вернусь и напишу в соцсетях анонс о наборе группы для обучения программированию. А там будь, что будет.

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

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

Я разместил пост в соцсетях... и записалось 3 человека

Я не стал заморачиваться с лендингами и прочим: короткое описание и форма регистрации, больше ничего не было.Я не стал заморачиваться с лендингами и прочим: короткое описание и форма регистрации, больше ничего не было.

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

  • JavaScript, потому что они же новички по сути, имея браузер, ребята с первого занятия смогут что-то сделать, не заморачиваясь с настройкой окружения. Мне вообще-то ближе PHP, на работе пишем на нем.

  • Лекции с лайвкодингом, как у Пола Хегарти из Стэнфорда одно время я руководил командой мобильных разработчиков и смотрел его курсы по iOS-разработке, чтобы лучше въехать в тему. И скажу, что живое программирование это очень круто. Даже опечатки и прочие сбои играют на руку: для лектора это возможность что-то дообъяснить. А для студентов, кто только думает о работе программистом, это даст понимание, как оно бывает.

  • Занятия по воскресеньям, в 9 утра требований к опыту я не предъявлял, готов был обучать с нуля. Но вот требования по мотивации, особенно при учете бесплатности, оставил. Когда-то я ходил в воскресную школу программирования при ННГУ. Да, иногда было тяжело, но программирование это не легкая прогулка, привыкайте)

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

Время на практику и самоподготовку - это важно. Те, кто не делал первую домашку, со второй лекции стали отстающими. Время на практику и самоподготовку - это важно. Те, кто не делал первую домашку, со второй лекции стали отстающими.

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

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

Через день: у меня 49 желающих. Как это автоматизировать?

Исходно я рассчитывал на 5-10 учеников, но соцсети сходу принесли лишь пару заявок. Поэтому у меня был запасной план. У нас в Skyeng работают несколько десятков тестировщиков, и вечером того же дня я закинул анонс в канал QA. Ребята из тестирования оказались очень благодарной публикой. Кто-то задумывался о переходе в разработку и хотел попробовать занятия с наставником, большинство увидели в изучении JS возможность автоматизировать рабочую рутину.

По истории скриншотов можно заметить, что я хотел попробовать для занятий Skype, затем Hangouts - но за пару дней до старта поменял решение. А потом поменял его еще раз. По истории скриншотов можно заметить, что я хотел попробовать для занятий Skype, затем Hangouts - но за пару дней до старта поменял решение. А потом поменял его еще раз.

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

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

Помню, как над тестами пришлось сидеть до 2 ночи. Зато потом это сэкономило мне кучу времени и нервов.Помню, как над тестами пришлось сидеть до 2 ночи. Зато потом это сэкономило мне кучу времени и нервов.

Я завел репозиторий на гитхабе, придумал 12 задач для основного курса, плюс выпускные проекты на выбор. Весь остаток недели оформлял задачки и писал к ним тесты. Зато потом мне надо было просто отправлять студентам ссылку на задачку гитхабе: они решали ее, запускали мои тесты, и если они были зелеными домашка зачтена.

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

Единственное, что не удалось автоматизировать - это написание текстов) А тк телеграм тогда формально блокировали, сделал зеркало. Единственное, что не удалось автоматизировать - это написание текстов) А тк телеграм тогда формально блокировали, сделал зеркало.

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

Автоматизируем сбор обратной связи от студентов. Курс рождался на ходу, мне важно было собирать фидбек после занятий. Я создал гугл-форму для анонимных отзывов: просил поставить оценку занятию и в свободной форме написать, что понравилось, а что стоит улучшить. Заодно прикрутил к каналу-каналу ControllerBot он помогал автоматизировать постинг, добавлять реакции и ссылки в виде кнопок к постам, особенно это выручало, когда линков было много.

Так выглядел пост по итогам первого занятия.Так выглядел пост по итогам первого занятия.

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

Утром 29 марта 2020 года я был готов начать курс. Единственное, что меня беспокоило это трансляция. Так как времени на поиск инструмента было немного, я попросил доступ к корпоративному Zoom: в нем проходят внутренние митапы нашей разработки.

Первый стрим комом

Само занятие прошло хорошо. Я начал с демонстрации тезиса, что каждый может немного программировать, если у него есть браузер: показал, как локально менять код, предложил ребятам повторить. Затем добавил интерактива: открывал порты через ngrok на своем компьютере и ребята получали реальные ответы от моего сервера. А в конце занятия мы уже писали простенький сайт и настраивали рабочее окружение: VSCode, git, cmdr + либа parcel для быстрого запуска кода.

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

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

Черная область на записи - еще одна моя боль. Черная область на записи - еще одна моя боль.

А главное, я не смог быстро научиться управлять сменой планов в Zoom:

  • Мой экран в режиме демонстрации.

  • Я перед ноутбуком.

  • Я возле физической доски, на которой иногда нужно что-то рисовать.

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

В общем, я решил искать инструмент для стримов дальше. И когда заливал видео на ютуб-канал, решил посмотреть, а что сам хостинг советует для организации трансляций. Оказалось, у них есть хорошая документация: в частности, там были ссылки на несколько инструментов. Я быстро прочитал отзывы и по ним мне понравился OBS.

Я нашел на ютубе 10-минутный рассказ, как запустить стрим через эту софтину. Понял принцип ее работы. И решил попробовать.

Как я тюнил трансляцию и запись

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

За следующую неделю я поставил OBS и разобрался, как стримить на ютуб, имея несколько источников захвата: камеру макбука (чтобы было видно меня в окошке), экран с IDE и браузером, внешнюю камеру на триподе для съемки объяснений у доски. Также для стабильности сигнала я отказался от вайфая и прокинул на кухню 20 метров патч-корда.

Стало сильно лучше.Стало сильно лучше.

Чуть позже я открыл, что при связке macOS и iPhone можно также выводить в трансляцию экран смартфона и иногда пользовался для работы с комментариями.

Заодно придумал, как автоматизировать создание таймкодов. Оказалось, в настройках ютуба можно включить создание автосубтитров: если скачать их, то откроется список таймкодов остается найти там ключевое слово и скопировать таймстамп в описание ролика.

Как была устроена подготовка к стриму

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

Заодно учил ребят код-стайлу - и почему это важно.Заодно учил ребят код-стайлу - и почему это важно.

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

Разложиться - почти как к концерту подготовиться. Процесс занимал минут 20-30. Я доставал большую сумку (там хранились монитор, провода, переходники, гарнитура), заваривал чай и начинал все расставлять и настраивать для эфира.

  • переставлял стол;

  • приносил детское кресло на него ставился трипод для камеры;

  • доставал удлинитель на 5 розеток, запитывал монитор, принтер, ноут и телефон;

  • включал авиарежим на телефоне;

  • выключал Docker на втором стриме ноут гудел очень сильно, а когда я полез разбираться, оказалось, рабочие проекты Skyeng крутились в фоне;

  • заходил в VSCode, создавал папку и пустые проекты чтобы на стриме переключил экраны и все готово;

  • и, конечно, отключал все нотификации на телефоне.

Бэкенд. Коробка из-под обуви очень выручала.Бэкенд. Коробка из-под обуви очень выручала.

Чтобы ничего не забыть, я завел чек-лист подготовки и шел по нему. Минут за 15 до занятия все было готово оставалось запустить клиент OBS, проверить настройки (включен ли чат, не сбились ли битрейты) и быть готовым выйти в эфир.

Фронтенд. Это мы разбираем проекты ребят на финальном занятии.Фронтенд. Это мы разбираем проекты ребят на финальном занятии.

Казалось, к третьему занятию все наладилось. Но затем возникла новая проблема.

Они не делают домашки... Что делать?

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

В качестве финальных проектов я выбрал Арканоид и Тир. В качестве финальных проектов я выбрал Арканоид и Тир.

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

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

Желтым отмечены как раз такие необычности.Желтым отмечены как раз такие необычности.

Домашки участников конкурса я перепроверял вручную 15 человек, 12 решений. 180 раз я запускал программу. Сидел в субботу до двух ночи, так как обещал, что в воскресенье на предпоследнем занятии объявлю результаты.

Я рассказал ребятам про финальные проекты, а также подсказал классный хостинг Surge, там можно бесплатно и быстро (просто из командной строки, без регистрации) размещать фронтовые проекты. Так мы разошлись до после майских и финального стрима, где я обещал разобрать их готовые проекты.

Студенты делятся на два типа

Несколько ответов из формы заявки на курс с какими ожиданиями приходили люди:

Хочу

Чтобы программировать разные штуки)

Не хватает знаний JS, чтоб писать автоесты. Ну и просто это очень интересно! :)

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

За месяц группа поредела на две трети. Я сел анализировать, почему так. И быстро заметил, что студенты поделились на две категории: те, у кого все получалось почти самостоятельно. И те, у кого скобки не ставились.

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

Люди думали, что интерпретатор их поймет, но... нет. Нам, разработчикам, многие вещи кажутся очевидными. Например, что у языка жесткий синтаксис фигурные, круглые скобки, точка с запятой. Или то, что переменные вне и внутри функции, даже если обе названы А, они разные это же две разные области видимости. И даже когда ты это показал и рассказал, не факт, что человек все уяснил.

Бывали и обратные случаи: ученик сразу пробовал отправлять запросы на сервера, изучать фреймворки еще не разобравшись с базовыми вещами. Это выглядело как попытка сразу перескочить из 1-го класса в 5-й. И примерно также оканчивалось...

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

Что по итогу

  • 49 человек зарегистрировалось, примерно столько же смотрело прямые эфиры.

  • 15 человек решили все задачи, финальный проект сделали 4. Один дослал проект через полгода.

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

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

Кстати, про деньги. Хотя курс был бесплатным, еще в анонсе я заявлял, что не против донатов. И после первого занятия (того самого, с глючным зумом) завел форму для сбора денег.

От первого к последнему занятию динамика выглядела так.От первого к последнему занятию динамика выглядела так.

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

Вот как-то так прошла моя самоизоляция.

А еще, от курса остались артефакты:

Если вы думали делать свои уроки или искали простые задачки по JS, надеюсь, вам пригодится.

Подробнее..

Грабли WebRTC на что похоже допиливание чужого велосипеда

18.02.2021 12:05:10 | Автор: admin

В пике на нашей образовательной платформе проходит до 4 тысяч уроков в час. Основной инструмент общения преподавателя и студента видеосвязь, потому что для обучения важно видеть и слышать друг друга. В самом начале мы использовали Skype, но его нельзя было интегрировать в платформу и логировать уроки. Потом мы перешли на SaaS-решения, но это оказалось очень дорого. Мы начали искать альтернативы и 2016 году отказались от покупных решений в пользу WebRTC и Janus. Теперь дорабатываем видеоконференции под образовательную платформу силами собственной команды. Да, пришлось копнуть глубже и потоптаться по граблям чужой технологии.

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

Протокол имеет значение

Когда мы впервые интегрировали видеозвонки в учебную платформу, то пошли по самому простому и очевидному пути обратились к людям, которые предлагают готовые решения. Это быстро, удобно и не надо вкладываться в развитие инфраструктуры. Связь работала по TCP и UDP, звук и картинка нас устраивали, но был фатальный недостаток дорого. Поискали еще, нашли решение почти в 5 раз дешевле все то же самое, но работает только по UDP. Подключились к нему.

Но тут приходит новый крупный корпоративный клиент, система безопасности которого блокирует UDP.

Возвращаться к старому дорого, а новый в TCP не умеет. Чтобы не терять клиентов, мы пару лет работали с двумя подрядчиками, но каждая оплата счетов причиняла боль.

Долго так продолжаться не могло. Мы прикинули, что дешевле будет собрать свою команду и вложиться в инфраструктуру. Стали смотреть в сторону связки WebRTC и опенсорсного Janus: он выполнял бы роль сигнального сервера, обязательного элемента для организации связи по WebRTC. У него была фронтовая либа janus-lib.js, с которой можно просто встроить видеоконференции в нашу платформу. Была возможность записывать аудио и видеопотоки. Казалось, нам оставалось только дописывать фичи для своих нужд, потому что в самом WebRTC мы ничего не можем поменять. И хотя запустить и настроить его с помощью Janus получилось без особых проблем, тут были и свои подводные камни.

Ты умный, но WebRTC умнее (нет)

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

А когда WebRTC снижал трафик на пробном уроке, конверсия снижалась на 10%.А когда WebRTC снижал трафик на пробном уроке, конверсия снижалась на 10%.

С другой стороны, когда связь у клиента хорошая, WebRTC использует канал по максимуму, увеличивая битрейт. А это уже грозит перегрузкой сети на сервере, т.к. если на него пустить больше трафика, чем может переварить Janus, то у многих клиентов произойдет обрыв соединения. Был и второй минус: если битрейт очень высокий, то исходные записи занимают больше места на диске и могут его переполнить. Мы подумали и установили ограничение для битрейта 256 кбит/с, чтобы точнее рассчитывать нагрузку на сервера и качество видео было приемлемым.

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

Вот такие подсказки стали появляться. Чтобы их понять, надо учить английский :)Вот такие подсказки стали появляться. Чтобы их понять, надо учить английский :)

Мощнее значит лучше

Запуская WebRTC, мы наивно полагали, что если у нас будет мощное железо с хорошим каналом, то все будет работать легко и быстро. Рассчитали плановую нагрузку, прописали нужные конфигурации серверов, но что-то пошло не так. Жалобы на связь от учителей стали прилетать гораздо раньше, чем мы уперлись в потолок. Мы не изучали, но видимо, какие-то ограничения были внутри самого Janus gateway. Тогда мы попробовали снизить лимит пропускной способности с 300 до 200 мбит/с проблемы ушли. Копать дальше не стали, но закупили сервера попроще и с меньшими лимитами.

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

Как только мы сократили путь, пакеты стали меньше теряться.Как только мы сократили путь, пакеты стали меньше теряться.

WebRTC одинаково хорошо работает с разными браузерами

А если нет, то проблема решается с помощью волшебной таблетки библиотеки webrtc-adapter. Да, но не совсем.

Например, Китай нас радует не только разнообразием товаров с Али, но и мобильных браузеров типа QQ и UC. Когда мы пробовали войти на китайский рынок, то обнаружилось, что WebRTC они поддерживают только в одну сторону: запрещают доступ к микрофону и камере устройства, при этом воспроизводят видео от второго клиента. Та же проблема возникла в Европе с браузером DuckDuckGo. А однажды к нам прилетела жалоба, что не получается заниматься через браузер Tesla! И так как повлиять на это все мы не можем, то просим клиентов пользоваться последней версией Google Chrome.

Ничего не работает? Используй Google Chrome!Ничего не работает? Используй Google Chrome!

Хотя и с ним до недавнего времени было не все гладко на iOS-устройствах: когда пользователи запускают в Chrome видеосвязь с мобильного устройства, iOS ругается и требует открывать Safari. А Safari только недавно получил поддержку WebRTC, и не все в нем работает так как надо. Например, его не устраивает маленькое разрешение для камеры (для экономии трафика мы передаем картинку 640 на 480), и он требует установить более качественную видеосвязь.

И еще один сюрприз: на iOS-устройствах ученикам нельзя одновременно быть на связи и воспроизводить видеофайл с заданием. Поэтому сейчас им приходится делать выбор и что-то отключать. И с этим нам тоже пришлось смириться.

Нельзя просто взять и разрешить доступ к видеокамере

Для видеоурока нужно включить камеру и микрофон. Казалось бы, это очевидно. Но мы заметили, что часто связь не устанавливается из-за того, что пользователь блокирует и то, и другое. И делает он это машинально, потому что окно с запросом похоже на другие запросы, от других сайтов, еще и появляется в том же месте. Тогда мы стали показывать это уведомление непосредственно в момент входа на первый урок и для надёжности дополнительный попап в другом месте экрана. Стало гораздо понятней и проблема решилась.

Если вдруг доступа к камере и микрофону нет, перед началом урока обязательно еще раз его запросим.Если вдруг доступа к камере и микрофону нет, перед началом урока обязательно еще раз его запросим.

Что в итоге

Свое железо и команда позволили нам сократить расходы на видеосвязь больше, чем в 10 раз. Мы уменьшили риск потери пакетов, выбирая оптимальный сервер между двумя клиентами. Получили возможность быстрее реагировать на пользовательское поведение и улучшать пользовательский опыт. А в прошлом году команда начала работать над своей видеолибой: планируют настроить автовосстановление соединения и добавить разные подсказки.

Кстати, Илья Левин, старший разработчик команды видео, подготовил доклад о том, как запустить видеоконференцию на базе WebRTC и Janus и жить с этим дальше приходите послушать в прямом эфире 27 февраля. Начало в 11-00 по Москве.

Что еще посмотреть и почитать по теме:

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

  • Кирилл Роговой рассказывает, что у WebRTC под капотом и почему от вас почти ничего не зависит.

  • Доклад с советами, как использовать WebRTC в вашем продукте.

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

Подробнее..

Люди не меняют свою жизнь, потому что в глубине души знают они фиговые специалисты

05.05.2021 10:13:29 | Автор: admin

Сколько людей в стране не любят свою работу и вообще ненавидят свою жизнь? Три года назад мне в руки попало одно очень занятное исследование Google: Barriers, motives & triggers of entrepreneurship. Три тысячи респондентов, очищенная выборка, больше ста российских городов, интервью дольше 20 минут. Там были несколько чисел, которые меня, мягко говоря, ошеломили.

30% респондентов были бы готовы поменять свою жизнь и начать в любом смысле работать на себя. Треть взрослых людей вроде бы готовы были поменять свою жизнь.

37% назвали главной мотивацией увеличение дохода. Ок, понятно. 20% назвали главной мотивацией возможность заниматься любимым делом.

Что же их сдержало от реализации желания работать на себя, а не на дядю и тётю? На первом месте стоял страх потерять деньги. А на втором, с небольшим отрывом отсутствие смелости.

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

Они уже никогда не будут теми, кем хотели стать в детстве. И что же они делают, чтобы изменить жизнь? Ничего. Точнее, глушат эти эмоции. Кто чем может алкоголем, сериалами, экстремальными видами спорта, играми.

Как связаны некомпетентность и выученная беспомощность

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

Вторая причина глубже. Эта штука пронизывает Россию. И нет, это даже не коррупция, а Тотальная Повсеместная Некомпетентность. Надо признать, что мы страна с подавляющим числом плохих специалистов.

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

Попробуйте найти хорошего врача без знакомства. Когда после долгих поисков найдете, посмотрите, кто стоит над ним? В каком проценте случаев его начальник такой же крутой? А чиновник над ними?

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

Сижу я несколько лет назад на заседании российской академии образования. Умудренные и уважаемые люди обсуждают ФГОС (Федеральные государственные образовательные стандарты, то, что ребенок должен изучать в школе) по информатике. И всерьез обсуждают, что надо там оставить Паскаль как язык программирования.

Он умер еще тогда, когда я 30 лет назад школу заканчивал. На мой изумленный вопрос: А зачем?!!, мне ответили: А у нас учителя ничего другого не знают. Это один из тысяч примеров некомпетентности в верхах, который я видел в своей жизни. Они не знают, как менять. Проще оставить как есть.

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

Что же делать

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

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

  • из бедности в процветание

  • из болезни в бессмертие

  • из войны и хаоса в мир и порядок.

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

Бизнес-результат. Всем нужен результат. Поэтому, если я взялся бы вас учить, то должен был убедиться, что знания в вашей голове есть и вы их применяете. И тогда, как образовательный сервис, я могу претендовать на часть результата. То есть я бы говорил явсему этому тебя научу, атыотплатишь тем, что будешь год отдавать мне часть своей дополнительной прибавки. Грубо: если будешь получать на 100 тысяч в месяц больше, отдавай мне 20 тысяч в течение года. И все согласятся, потому что если я хорошо поработал и есть прибавка, то вы с удовольствием поделитесь ее частью.

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

  1. Учитесь брать на себя ответственность. Сначала будет больно. Но в конце концов лапы и хвост станут мощнее. Да и денег прибавится.

  2. Учитесь брать деньги за результат, а не за часы работы. Даже Харв Экер, автор книги Думай, как миллионер, пишет: Rich people choose to get paid based on results. Poor people choose to get paid based on time

  3. И третий вывод: я не случайно два первых вывода начал с глагола учитесь, а не делайте. Если бы это было так просто, у нас было бы больше состоятельных людей. Этому именно надо идти учиться.

Образование это то, что мы оставляем потомкам после того, как освоим мир. С этими мыслями мы в Skyeng запустили новый проект онлайн-университет навыков и профессий Skypro. Это наша попытка переосмыслить все взрослое образование. Он про то, что вкладывать в себя это рентабельно сегодня, а не послезавтра. Про то, что может сделать меня и тебя счастливым. Пожелайте нам всем удачи на этом пути.

P.S. Что делать-2

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

Есть такая поговорка: дурень думкой богатеет. Она про нас. В голове у нас рождаются грандиозные планы. Там мы успешны, круты, здоровы и неотразимы. И только там. Получается, что мы хотим себя изменить и ничего для этого не делаем.

Героиня знаменитый Алых парусов Александра Грина сидела и ждала своего принца. Изменения случатся сами по себе эта модель ожидания у многих прочно засела в голове. Чертова Ассоль порушила судьбы миллионов людей. Но виновата, конечно, не она, а те, кто принял такую модель жизни. Мы.

Подробнее..

Сейчас я буду убеждать вас использовать статический анализ в PHP

27.11.2020 10:16:55 | Автор: admin


Я помню выход PHP7: появились strict types, скалярные type hint-ы.

Мы начали двигаться в сторону языка со статической типизацией, но типизация не ушла в статику. Концептуально все осталась прежним мы запускаем программу и только в runtime узнаем, что где-то есть неправильный тип. Даже если мы везде явно проставим типы, все ошибки мы не поймаем и можем больно упасть в продакшене.

Всем привет! Это частичная расшифровка моего подкаста Между скобок разговоров обо всем, что связано с разработкой на PHP. В гостях у меня Валентин Удальцов, автор телеграм-канала Пых, который регулярно пишет о плюсах статического анализа.

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

Если вы решите углубиться в тему, советую эти видео:



Поехали!


Сергей Жук, Skyeng: Что вообще анализируют статические анализаторы? Они же не только про типы, да?

Валентин Удальцов, Пых: Статический анализ выходит далеко за пределы декларации типов в PHP. Типизация может быть очень многогранна: скажем, это может быть работа с иммутабельность и чистотой. То есть, мы можем гарантировать, что объект определенного класса не меняется в процессе его использования, на нем нет setter-ов.

Мы также можем следить за чистотой функции: смотреть, что она не обращается к IO.

В Psalm еще недавно добавили taint analysis: мы можем проследить данные от источника из внешней среды до какого-то уровня вглубь и понять, что все данные были так или иначе провалидированы. Тогда, с некоторой долей вероятности, у нас нет проблем с безопасностью. Вот еще одна задача, которую может решать статический анализ.

Сергей Жук, Skyeng: А ты вообще разбирался, как это работает? Если мой код не выполняется, получается, он парсится в абстрактное синтаксическое дерево и...

Валентин Удальцов, Пых: Есть статические анализаторы, которые используют autoloading композеровский те же PHPStan и Psalm. Phan по-моему, частично использует runtime, но дальше сам анализ происходит без выполнения.

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

Есть два этапа. На первом Psalm проходит весь код, который ты указал ему явно полностью. Традиционно это папка src и любые другие, которые ты перечислил. Он также заглядывает в vendor, если твой код использует вендорные классы. Это просто сбор данных: он все кэширует, трансформируя данные парсера Попова в некоторые свои сущности.

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

Второй этап это анализ. По умолчанию он проходит только в измененных файлах. Но можно форсировать полный анализ: тогда Psalm поднимет из кеша подготовленные на первом этапе данные и проведет рекурсивный анализ всего и вся.

Сергей Жук, Skyeng: Ты постоянно упоминаешь Psalm а почему не другие?

Валентин Удальцов, Пых: Да все просто. Когда я вообще что-то выбирал, то договорился на работе в Happy Inc., что начинаю новый проект и мы сразу включаем всё на максимум. Посмотрел, что чаще упоминают, сделал базовое сравнение фич. Phan не использовал composer autoloading, насколько помню. В PHPStan было больше плагинов. В Psalm была иммутабельность, еще какие-то вещи, частые релизы, сообщество, а человек, который его развивает, на оплачиваемой должности. Все это подкупило.

Выбор я сделал и не слежу, как развиваются альтернативы. Просто времени нет. Но Psalm до сих пор часто удивляет: скажем, с новым релизом может появить фича, про которую я знать не знал, что это можно проверять статически.

Бывает и так, что выйдет новый релиз, а там какая-то регрессия или на что-то не написан тест.

Естественно, приходится такое suppress-ить, писать issue. Мой лайфхак: если вы нашли проблему в любом пакете, который ставите, напишите todo, создайте issue, а в todo впишите ссылку на issue. Когда issue закроют, по его url вы найдете все места в коде, где нужно убрать suppress.

С Psalm я так и поступаю: не откатываю версию, я просто пишу suppress-ы и обязательно делаю тикет.

Сергей Жук, Skyeng: А как подсадить на статанализ команду? Люди ждут, что им сейчас всякие хитрые баги найдут, а в реальности все немного иначе.

Валентин Удальцов, Пых: У меня нет опыта внедрения Psalm на большом проекте с кучей людей. Этот момент я признаю. Мне помогло то, что я начинал писать проект один, хотел по полной обмазываться статическим анализом, и сделал это. А когда начали подключаться люди с других проектов, они были поставлены в существующие рамки. Конечно, поначалу я объяснял и показывал, но у нас небольшой техотдел. Так что не сказал бы, что адаптация других ребят заняла много времени.

Для тех, у кого много людей и старая кодовая база, в инструментах статического анализа есть такая история, как baseline.

Когда ты добавляешь инструмент в старый проект, там найдется миллион ошибок. Но ты говоришь анализатору: мы признаем, что это есть, но пока не обращаем на это внимание а вот когда мы пишем новое, рассказывай, где проблемы. Это baseline и постепенно, относительно него, разработчики учатся писать код с учетом статического анализа. При этом в Psalm есть несколько уровней, то есть инструмент максимально заточен, чтобы внедрять его постепенно.

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

Сергей Жук, Skyeng: Бывает, анализаторы ругаются на неверные PHP-доки, хотя сам код работает правильно. Да и ты говорил про баги, которые помечаешь todo-шками Есть ли тогда смысл добавлять статанализ наравне с тестами в CI?

Валентин Удальцов, Пых: Добавлять надо, иначе теряется смысл. Идея типизации в том, чтобы снизить энтропию, повысить детерминированность того, что мы пишем. А богатая типизация позволяет писать меньше тестов. Если мы знаем, что где-то есть тип, который возвращает в этом случае null, а в этом string, если мы точно знаем, в каких случаях что возвращается, то не понадобится писать тест, который это проверяет.

Статический анализ это полноценный этап CI, который пробегает до тестов.

Если у тебя типы неверные, что там тестировать? Ты в любом случае накосячил.

Что касается багов в Psalm, обычно это минорные вещи, не самая большая проблема. Если есть реальная проблема, мы формируем issue и костыльком в виде suppress или подсказки с todo-шкой это фиксим. При этом бывают нюансы, которые ты сразу не понимаешь, делаешь issue, а тебе отвечают: Да ты просто здесь вот это не так указал. И скидывают как правильно.

Сергей Жук, Skyeng: Ок, но для таких инструментов свой синтаксис надо учить. И твой код им обрастет. Как ты думаешь, в будущем это всё мигрирует в сам PHP?

Валентин Удальцов, Пых: Я смотрю на свой текущий проект. Он написан как бы не на PHP, а на PHP плюс Psalm. Но это всё-таки далеко от того, чтобы быть прям совсем другим языком. Это не как Haskell и PHP, например.

Что касается будущего, трудно сказать. Если код не типизирован, вероятность ошибки выше. Пока PHP как бы на двух стульях сидит: с одной стороны, позволяет mixed (и это иногда удобно), а с другой, мы так или иначе стремимся к типизации.

Сергей Жук, Skyeng: Ок, а что бы ты сказал человеку, который сейчас открыл IDE, создал новый проект и думает, добавлять статанализ или ну его?

Валентин Удальцов, Пых: Бизнес-логику, естественно, никакой статический анализ не проверит. А вот типы и все эти совершенные тупые ошибки снижаются процентов на 80. Да, в начале будет сложнее. Но в долгосрочной перспективе это профит. Статический анализ передвигает исправление багов ближе к началу разработки, а это снижает издержки на поддержку проекта.

Ведь всех бесят ошибки с продакшена, когда вместо expected string пришел int, null где-то появился или undefined index. Поэтому разработчик может взвесить все плюсы и минусы, понять, что благодаря статическому анализу вот такие совершенно глупые ошибки исключаются. А еще есть приятный бонус в виде дженериков.

Сергей Жук, Skyeng: А всё же есть какие-то минусы, каких-то кейсы, где вообще не надо использовать стат анализ?

Валентин Удальцов, Пых: Если проект реально большой, а команда привыкла полагаться на runtime издержки на интеграцию могут быть запредельными. Но все равно я бы советовал попробовать baseline внедрить, посмотреть, что как будет. Многие вещи надо в несколько этапов внедрять.

А если вы распиливаете большой монолитный сервис, и по какой-то причине продолжаете на PHP все делать, стоит попробовать.

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

p.s. Найти другие выпуски подкаста вы можете здесь.
Подробнее..

Как дойти до CQRS, если у тебя PHP

17.03.2021 10:08:48 | Автор: admin

Привет, я Сережа из Брянска, и в моем городе и до пандемии почти не было конференций и митапов. Поэтому я привык смотреть записи докладов из разных сообществ. Но как задать их авторам вопрос? Так что я завел подкаст и зову в него интересных докладчиков на поговорить.

Недавно я посмотрел доклад Как перестать бояться CQRS. Вроде бы простая идея, но есть нюансы. Так и появился этот выпуск.

CQRS vs CQS (не перепутай)

Аудио

Вы можете включить аудиоверсию в ней больше технических деталей.

Сергей Жук, Skyeng: А может быть CQRS с одной моделью в коде? Или обязательно две?

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

Сергей Жук, Skyeng: А какую проблему мы вообще решаем этим? Зачем вообще все усложнять и разделять?

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

Часто, когда ты строишь графический интерфейс, нужно собирать данные из большого количества сущностей. Скажем, тебе нужен заказ, нужен пользователь, количество средств у него на счету, количество новых сообщений у этого пользователя, что-то ещё. Набор данных, который нужно отобразить в интерфейсе, сильно избыточен для команд. Команды меняют состояние системы это формализованный скрипт того, как бизнес видит взаимодействия сущностей. Запросы, наоборот, ничего не меняют, они только возвращают данные и помогают абстрагировать логику отображения и выборки данных.

Так у тебя органически появляются две модели: одна для чтения, другая для записи.

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

Сергей Жук, Skyeng: А какую роль во всём этом играет ORM? Если мы для чтения можем вообще кастомные штуки делать, получается, мы можем ее выкинуть? Или стоит оставить ее для записи?

Дмитрий Симушев, Райффайзенбанк: Если мы говорим о модели записи, ORM все еще нужна, когда у тебя богатая доменная модель. Если всё построено на объектах и эти объекты нужно каким-то образом на реляционную базу отобразить, ORM помогает не думать о твоём коде в терминах БД. Ты работаешь с сущностями, а ORM за тебя всё отображает на базу.

Когда мы переходим к модели чтения, для отображения нужны не сущности, а плоские объекты. PDO может отлично брать SQL-запросы и результаты засовывать в DTO-шки. Это получится сильно быстрее. Получается, мы можем оптимизировать модель чтения, просто выкинув всё ненужное. И тут ORM как раз та штука, от которой можно отказаться. С ней мы упремся в производительность. Но этот путь стоит проходить эволюционно: если тебе хватает на чтение средств ORM, если тебя в принципе все устраивает почему бы и нет?

Сергей Жук, Skyeng: Так может, тогда и одну модель оставить?

Мы возвращаемся к исходному вопросу. СQRS это точно про две модели?

То есть, видишь, можно оставить одну ORM, а снаружи поведение, как всё у нас разделено. Мы разделим записи и чтение. Записи будут быстрые. Можно даже асинхронно сделать. Снаружи-то не видно...

Дмитрий Симушев, Райффайзенбанк: Ты знаешь, это тоже работает, но это не CQRS.

Сергей Жук, Skyeng: А почему?

Дмитрий Симушев, Райффайзенбанк: Принцип Command-query separation (CQS) появился еще раньше: один эндпоинт занимается чтением, другой записью, и они между не взаимодействуют. CQRS следующий шаг на этом пути: за чтение и запись отвечают разные подсистемы в коде, и ты используешь вообще разные модели. Если тебе достаточно CQS, если тебе хватает производительности окей. Если нет, ты уже идёшь в CQRS и в разделение этих моделей.

Сергей Жук, Skyeng: Давай тогда поговорим про архитектуру CQRS. Я верно понимаю, что чтение синхронное, а записи асинхронные? Или необязательно? Например, в рамках http-запроса мы получим данные, поставим команду на выполнение и сразу отдадим ответ: например, там 202? Сама же команда попадает в какую-то очередь, шину, и выполнится за пределами request-response?

Дмитрий Симушев, Райффайзенбанк: Необязательно. У тебя же обычно есть слой контроллеров. Скажем, у тебя может быть контроллер, который отправляет сначала команду, дожидается, пока она будет выполнена, делает запрос и возвращает что-то наружу. Само разделение идёт на более низких слоях.

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

Сергей Жук, Skyeng: Но в ООП есть принцип, что метод у объекта должен либо менять состояние, но ничего не возвращать, либо должен что-то возвращать но не менять состояние. Я воспринимал CQRS в этом ключе: грубо говоря, эндпоинт либо возвращает какие-то данные, не меняя состояние стораджа, либо он меняет состояние, но при этом всегда void.

Дмитрий Симушев, Райффайзенбанк: Знаешь, тут большой вопрос: а нужно ли CQRS проецировать на уровень эндпоинтов?

Ты говоришь про метод, но это метод кода, более низких слоев. Эндпоинты не должны этой истории подчиняться. Точнее, так: необязательно это делать прямо до конца, до эндпоинта. Ты можешь провести границу иначе: сказать, что в ядре, например, CQRS, а дальше я могут быть варианты работать с REST, GraphQL, навигацию делать, что-то еще.

Сергей Жук, Skyeng: Окей, а как понять, что мой кейс созрел для CQRS?

Дмитрий Симушев, Райффайзенбанк: Первое условие: хорошо выраженная предметная область. У тебя должен быть зрелый бизнес-домен. Второй момент: должны быть либо проблемы с производительностью, либо неравномерная нагрузка.

Как понять, что уперся?

Сергей Жук, Skyeng: Расскажи, как ты пришёл к CQRS? Наверняка же сначала возникли какие-то проблемы?

Дмитрий Симушев, Райффайзенбанк: Вот как получилось. Мы начали делать приложение, продумали сущности, стали их реализовывать. И вроде всё хорошо, но потом приходит бизнес: Ребят, вот здесь нам нужно вывести еще немножко информации. Встает выбор: делать сущности еще одну связь, чтобы при чтении взять её целиком, серилизовать и отдать на клиент. Либо придумывать что-то ещё.

Добавление связей работает только до какого-то определенного количества этих связей. А когда модель распухает, ORM на чтении начинает очень здорово тормозить. И тебе в любом случае приходится как-то это дело оптимизировать. В момент, когда мы поняли, что не можем ничего сделать с производительностью и уперлись на чтение в ресурсах, мы посмотрели по сторонам и подумали: Окей, а почему бы нам не делать обычные скрипт-запросы в базу и отдавать данные на клиента?

Поняли, что эта история очень здорово вписывается в CQRS и разбили приложение на два кусочка: один модели, плюс-минус чистые, без чтения; и второй модель чтения, которая просто подает данные.

Сергей Жук, Skyeng: Как вы поняли, что уперлись?

Дмитрий Симушев, Райфайзенбанк: Стали тормозили эндпоинты. Смотришь, у тебя ручка отрабатывает за секунды. Залезаешь внутрь, смотришь - вроде всё гладко. А потом понимаешь, что большую часть времени работает Doctrine.

Начинаешь оптимизировать Doctrine: делаешь всякие радостные хаки с ленивой загрузкой, включением-выключением, кэшем второго уровня. И в какой-то момент понимаешь, что выжал из нее все. Но тебе не хватает. Тогда ты пробуешь переписать запрос на чистый SQL и получаешь прирост х10 просто за счет того, что ты выкинул ORM и в чем-то упростил свой код.

Сергей Жук, Skyeng: Проблема была даже не в медленным запросе, а в том что PHP-код этот медленно обрабатывал?

Дмитрий Симушев, Райфайзенбанк: И то, и то. В случае с PHP-кодом, самая большая нагрузка в гидрации у Doctrine: все, что ты притащил, она сначала разворачивает в сущности, а ты сущность все равно серилизуешь в JSON, чтобы отдать на фронт. Но есть и SQL-ная часть нам нужно было, скажем, половину полей сущности, а из-за специфики ORM выбирали ее всю.

Пока мы остановились на разделении моделей. Нам хватило. PostgreSQL держит нагрузку на этом уровне. Но в целом, никто не мешает пойти дальше и выделить, скажем, отдельный сторадж.

Сергей Жук, Skyeng: Тяжело было затаскивать в уже написанный проект CQRS, разделять модели?

Дмитрий Симушев, Райффайзенбанк: Ты знаешь, очень гладко все проходит. У тебя есть сущность, есть бизнес-модель. Ты понимаешь, что у тебя на чтении система не справляется, и просто сбоку делаешь сервис, который ходит в базу данных. В контроллере используешь его и всё запрос ты инкапсулировал, постепенно можешь часть нагрузки на чтение снимать с бизнес-модели. И вот так, кусочками, вплоть до единичного запроса и эндпоинтов.

Мы перевели только то, что тормозило: нужно было оптимизировать запросы мы их оптимизировали. Как только стало нормально тут же остановились. Часть моделей у нас работает и на чтение, и на запись.

Сергей Жук, Skyeng: У вас были проблемы? Что было тяжело, может быть, пошло не так?

Дмитрий Симушев, Райффайзенбанк: Наверное, самое сложное это решиться. Потому что есть ORM, есть логика, которая собрана в одном месте, есть маппинги, которые в одном месте описаны. И ты вроде как понимаешь, что если ты сейчас начнешь где-то сбоку делать что-то ещё то рано или поздно это стрельнет в ногу. Самое сложное принять, что ты уперся и делать надо.

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

Сергей Жук, Skyeng: А к тестированию как-то подход меняется при этом?

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

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

Сергей Жук, Skyeng: Часто CQRS рассматривается как некий шаг на пути к Event Sourcing. Как тебе удалось спокойно остановиться?)

Дмитрий Симушев, Райффайзенбанк: Когда мы переходим к CQRS, то начинаем сначала с одного стораджа. Если не хватает производительности делаем слейвы на чтение. А вот если дальше всё равно не хватает можно задействовать Event Sourcing.

Есть бизнес-домены, где Event Sourcing сам по себе очень полезен. Не как технология, не как следствие CQRS, а сам по себе. В целом, это любая область, где изменение приводит к каким-то последствиям. Классический пример финансовые истории с бизнес-транзакциями, когда мы рассматриваем, например, какие-то клиентские счета. Тут не так интересна цифра на счете, как история того, что происходило с этим счётом. Ты смотришь в первую очередь на сам процесс, нежели на то, где сейчас находишься.

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

Сергей Жук, Skyeng: А если я знаю, что будет нагрузка и что домен подходит под Event Sourcing, но мне надо запилить MVP?

Дмитрий Симушев, Райффайзенбанк: Ты можешь начать с самого простого: ORM, одной базы данных, одной модели на запись и на чтение, сериализатора. Потому что никто не мешает тебе в какой-то момент остановиться и выделить отдельные запросы на чтение. Как только ты понимаешь, что надо ускоряться ты постепенно начинаешь выделять модель чтения. Модель записи можно вообще пока не трогать. Когда выделишь модель чтения, тогда начинай резать и модель записи. В CQRS очень здорово то, что ко всему можно приходить эволюционно.

Полезное по теме:

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

Другие выпуски подкаста Между скобок.

Подробнее..

Опенсорс-бот для трекинга SLA в хелпдеске

10.03.2021 12:22:46 | Автор: admin

Лебовски создавался для биллинга (отсюда и название), но быстро прижился везде, где была нужна простая статистика по хелпдескам.

Все началось в декабре 2019-го. Ко мне пришли проджекты и тестировщики из биллинга: Есть канал в Slack, куда мы кидаем обращения по проблемным платежам. Хотим вести по ним статистику: смотреть, какие типы обращений встречаются чаще, понимать, сколько времени заняло решение.

Slack не дает циферок, но многим их хочется. Поэтому есть вариант интегрироваться с Jira, затем строить графики в Redash или Grafana. Тут кажется, что нужна помощь аналитика но для простых случаев это как гвозди микроскопом забивать. Зацепившись за слово статистика, я подумал о таблицах: в них можно считать всякие метрики с помощью формул, а если понадобится что-то сложнее аналитики смогут настроить выгрузку данных.

Итак, мы можем подключить бота к каналу в Slack, сделать простой интерфейс со списками и лить данные в таблицу. Можно в свою базу данных, можно в Google Таблицы. Выбирая решение для прототипа, я остановился на последних: с ними умеют работать все, особенно менеджеры (мой основной заказчик), и у продукта есть готовое API.


Так за 4 часа ожидания пересадки в аэропорту Хельсинки появился MVP бота.

Теперь, когда в канал падало сообщение, под ним появлялся бот с кнопкой Закрыть обращение. Когда разработчик разбирался с задачей, он нажимал Закрыть и мы просили его выбрать категорию обращения. Данные из выпадающего списка попадали в табличку (при необходимости вместе с текстом оригинального сообщения), а также датой и временем, когда оно появилось в канале и когда было закрыто.

Благодаря таблицам мы стали быстро собирать фидбэк от заказчика, и вскоре бот оброс новыми фичами:

  • появились реакции: время попадания запроса в канал не равно старту работ по нему, поэтому саппорт ставил эмодзи, когда на самом деле брал обращение, и этот момент в формате дата-время тоже попадал в таблицу;
  • саппорт мог отметить, сколько времени занял непосредственно фикс проблемы, так у бота появилась вторая кнопка Залогировать время;
  • если задачу требовалось передать в другой отдел, то можно было указать, кому и в какой.

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

Как бот стал популярным и как я упрощал его поддержку


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

Лебовски быстро заметили в других проектах. И пришли с классическим: А напишите нам похожего. Мы уже шаблонизировали других популярных ботов в компании, чтобы не тратить ресурс разработки на типовые задачи, и пошли этим же путем тут.

  • Простая функциональность для раскатки на другие команды: каждый новый Slack-канал подключается простым добавлением пары ID (к какому каналу подключиться и куда отправлять данные) на GitHub. Завести бота для нового заказчика внутри компании буквально дописать 6 строк в один из файликов.
  • Добавление категорий без участия разработки: в табличке у каждого заказчика есть лист, на котором можно дописывать классы обращений, не привлекая нас. А оно передается в бот.
  • Поддержка Slack Workflows. Что любят люди, помимо таблиц? Формы. Поэтому Slack сделал нативное решение, которое их заменяет. Но т.к. формы реализованы на базе бота, а наш бот обучен не учитывать сообщения от других ботов и не отвечать на них (были забавные инциденты), пришлось чуть повозиться с тонкими настройками фильтра не реагируй на других ботов.

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

И решил, что пора его опенсорсить


Теперь вы можете поднять своего Лебовски на своем сервере (или в Heroku) и пользоваться им в своем рабочем Slack. Инструкции о том, как его развернуть, а также сами исходники бота лежат на нашем GitHub здесь.

Сейчас Лебовски работает так:

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



  • когда саппорт ставит сообщению определенный эмодзи (какой именно можно задать в конфиге), обращение помечается как В работе и дата/время установки эмодзи сохраняется в таблице (так вы можете отслеживать SLA);
  • саппорт может залогировать время, потраченное на обращение, с помощью кнопки Залогировать работу. Ворклоги сохраняются на отдельном листе в удобном формате;
  • как только саппорт закончил работу над тикетом, он нажимает на Закрыть обращение, после чего Лебовски спрашивает категорию и исполнителя обращения и складывает эти данные в таблицу.

С одной стороны может показаться, что этот бот делает не так уж и много, но в повседневной жизни этого достаточно, чтобы посчитать много полезных метрик, например AFRT (Average First Response Time) и ACRT (Average Case Resolution Time).

Но лучше 1 раз попробовать, чем 100 раз прочитать.

Что еще почитать про боты в Slack: 7 cмертных грехов Slack в большой компании (и как победить их автоматизацией).
Подробнее..

Трассировка и логирование в микросервисах как мы втаскивали единый стандарт на 30 независимых команд

20.01.2021 12:12:03 | Автор: admin
Сервисы падали, падают и будут падать

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



Например, наша команда сопровождения маркетинга в Skyeng знала: пользователь с таким-то айдишником нажал в личном кабинете кнопку Сменить преподавателя постучался в наш сервис, дальше ушло три сообщения, в очереди было 2 вызова сервисов и эти сервисы ответили 200. Но на самом деле, что было у команд сопровождения учителей или биллинга, к которым мы стучались, не знал никто

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


В компании есть стандарт окружения: PHP 7.x, Symfony 4.x, RabbitMQ для системы сообщений, GuzzleHTTP для внешних и своих ip, Docker. Благодаря тому, что нам более-менее известно, что будет в каждой команде, мы написали библиотеку, которая помогла бы сделать трассировку запроса по всей системе с первого клика.

У нас два основных транспорта HTTP и RabbitMQ. Запрос приходит или от пользователя, или по cron.

  • Мы берем любой HTTP-запрос, а прежде чем прокинуть дальше, добавляем заголовок request-id от NGINX, у которого возможность генерации вшита прямо в модуле.
  • В случае с cron, сами генерируем request-id внутри нашего бандла.

Дальше прокидываем request-id всем следующим получателям в нашем облаке:

  • Автоматически добавляем в Guzzle-клиент для продолжения передачи request-id через HTTP при синхронных запросах к нашим сервисам.
  • Автоматически добавляем request-id ко всем AMQP-продюсерам для асинхронных запросов к нашим сервисам.
  • Также во всех местах, куда мы вклиниваемся для отправки, прикручен Jaeger opentracing это поможет построить всё дерево/карту запросов. Таким образом наш внутренний request-id в качестве атрибута уходит в трейсы opentracing.

Opentracing, маркировка через request-id и складывание в правильные места первые шаги к тому, чтобы в компании появились хорошие логи. Когда команды подключат нашу библиотеку, схема выше начнет работать: request-id начнет передаваться по HTTP и AMQP, каждый сервис будет знать, в рамках какого глобального запроса он сейчас работает. И мы увидим, как запросы расползаются по нашей инфраструктуре.

Логирование inspired by Java


Мы посчитали, что делая сложную композицию из нескольких классов, логер не нужно вставлять в конструктор. Он должен находиться сбоку и подключаться де-факто. В Java для этого, например, есть SLF4J аннотация.

В PHP нет таких аннотаций, поэтому многие прокидывают в конструктор логеры, как дополнительную зависимость. Нам не нравился такой подход, поэтому мы сделали trait, который служит почти как аннотация (т.к. мы используем Symfony, то trait опирается на функционал autowiring в результате получается практически как в Java).

Что мы логируем? Помимо request-id, это:

  • runtime-id и process-id идентификаторы текущего потока и процесса, внутри которых может обслуживаться несколько request-id,
  • user-id так как в приложениях есть авторизация, этот идентификатор должен быть везде,
  • имя канала канал можно использовать как бэкап для пользователя, если отвалилась база данных и что-то не записалось.
  • id контейнера, id сервиса, id задачи и прочее все возможные мета-данные о Docker-контейнере. Зачем? У нас был момент, когда приложение было раскатано на 3 контейнера, а взял и сглючил один конкретный, потому что он был развернут на глючном воркере.
  • Цепочки исключений на уровне базы, сервиса, всегда создаем исключения более высокого уровня, потому что часто именно в последнем исключении и кроется А что было-то?.
  • имя приложения в процессе оказалось, что многие команды ориентируются на имя репозитория, а приложение внутри не знает, как его зовут: пришлось дособирать эту информацию и дописывать upname в yaml-файл.
  • плюс служебные команды и переменные окружения.

Также есть вещи, которые можно записать заранее: запуск контроллера, запуск консьюмера. Можно заранее проставить уровень debug он не будет попадать никуда, но если нужно, сможете включить на тестинге или на проде, посмотреть, что у вас происходит вплоть до запросов в базу.

В чем хранить логи на проде и тестингах


На приложения у нас заводится New Relic, но мы дополнительно остановились на разделении записей между Sentry и Kibana.

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



Наша панелька с небоскребами если они растут, значит, кто-то сегодня закончит работу попозже.

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



Самое главное не класть ошибки, которые вы не собираетесь править. Например, access denied сюда не летит.

Но в Sentry есть и свой минус он не очень хорошо справляется с большим потоком. Если вы, например, поделили на 0 и выкатили в прод, хранилище может лечь. Плюс надо помнить о том, что ошибка в одном сервисе может породить вал ошибок в другом сервисе. Решение этой проблемы выходит за тему статьи, но в целом смотрите в сторону паттерна circuit breaker.

Лог всего, что не ошибки, мы прокидываем как JSON в Kibana. Для этого сборщик Beats ходит, собирает файлы, наш модуль для Filebeat отправляет их в Elasticsearch, а дальше мы уже в Kibana все вычитываем. Правильнее было бы вычитывать логи с вывода контейнера и местами мы так и делаем.



Наша Kibana Выглядит она как Kibana хороший полнотекстовый поиск, свой язык запросов, всякие поля.

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

Итак, мы молодцы. Сделали полезную вещь. Что должно было быть результатом этих действий? Все команды начнут использовать единообразные библиотеки как часть ядра. Все, от саппорта до CTO, будут знать, где, как искать и что искать в логах. А единая система алертов сделает жизнь еще прекраснее Внедряем? Да, но есть нюанс.

Куда вас пошлют Пошаговый разбор, как внедрить у себя


Зато понимающие люди будут говорить: Он внедрил логи по всей компании

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

Чтобы логи стали единообразными, их идею надо продать. Вот какую многоходовочку соорудили мы.

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

  • Например, сделали документацию с примерами и типовыми кейсами: как искать по логам, что делать, если нужен свой канал. Часть кейсов оказалось удобнее показать на уровне видео. Да, это значит, нужно делать презентацию, писать видео но в итоге оно того стоит.

Шаг 1. Вы идете к CTO и тимлидам. Вы думаете на языке PHP, они уже бизнесом, фичами, деньгами. Аргументируйте на их языке:

  • С библиотекой все будет быстрее подниматься, снизим шанс падения по той же причине, в бизнесе заложено, что что-то упало. Теперь заложим, с какой скоростью оно встает: performance, скорость восстановления это бизнес-метрики.
  • Будет проще новичкам, бизнесу зачастую проще нанять дополнительных разработчиков, чем перекидывать людей с других проектов. Но когда компания построена на микросервисах, часто получается, что единообразия в версиях ядра нет. Библиотека дает хотя бы частичное единообразие.

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

  • Красота и удобство Sentry продают. Начните с них.
  • Минимизируйте телодвижения разработчиков. Именно поэтому мы выбрали уровень библиотеки. Если делать здоровые конфиги или правила, из-за которых придётся менять код, никто этого делать не будет. А здесь мы сами написали бандлы, которые в большинстве своем конфигурируют себя сами и сами встраиваются, куда им надо. Их можно встраивать без конфигурации вообще но если надо, их конфигурацию можно перекрыть.
  • Плюс удобный trait для добавления логера, документация с заранее объявленными каналами, готовыми примерами доработок и расширения конфигов. Так, чтобы не надо было городить что-то свое при внедрении.

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

p.s. Пост основан на опыте и рассказах Вани, Макса и Димы, которые вместе делали этот проект. Мы не придумали, от чьего аккаунта это публиковать, и залили от аккаунта компании. В комментариях ребята готовы ответить от своих аккаунтов.

p.p.s. В посте использованы цитаты из докладов Макса и Вани про этот проект.
Подробнее..

80 докладов и статей, которые запомнились PHP-сообществу в 2020 году

27.02.2021 12:14:40 | Автор: admin

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

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

Как мы получили данные и кто давал ответы

В конце декабря 2020 мы сверстали форму из 14 вопросов: часть подразумевала свободный ответ, часть выбор из вариантов (подробнее тут). Ссылку на форму мы раскидали по чатам и каналам для PHP-разработчиков в телеграме, а также сделали анонсы в твиттере, на Хабре и запустили небольшую таргетированную компанию в ВК. До конца января 2021 мы принимали анонимные ответы.

1506 респондентов заполнили опросник целиком или частично.

44% определили себя как мидлы

24% как сеньоры

13% как тимлиды

Структура поста

1.1. Популярные стримы

1.2 Записи докладов разных лет, которые запомнились

1.3. Доклады на английском, которые запомнились

2.1. Самые упоминаемые статьи и книги

2.2. Часто упоминаемые статьи

2.3. Переводы статей, которые запомнились

2.4. Статьи на английском

1. Что смотрели

Мы получили свыше 240 релевантных свободных ответов, по которым можно было восстановить ссылки на материалы, а затем отобрали самые упоминаемые.

1.1. Стримы

Назад к оглавлению

Встречаем PHP 8: советы по обновлению, мнения и интервью с разработчиком языка

Небольшой холивар про тем ли путем идет язык, доклад о юзерленде и народное интервью с Никитой Поповым и Дмитрием Стоговым. Трансляцию на 3 каналах смотрело свыше 1100 человек в прямом эфире. Всего записи с ноябрьского митапа набрали более 40к просмотров за все время.

Трейты в PHP зло? Валентин Удальцов против всех

Холивар на холиварную тему пожалуй, один из самых удачных онлайн-форматов. Дискуссия состояла из 4 раундов в каждом разбирали новый кейс. После каждого раунда зрители голосовали за и против трейтов.

Выбрать между PHP и Go просто, достаточно

Cтрим, в котором те, кто писал на PHP, но перешел на Go, и те, кто пишет в основном на PHP, не холиварили а написали два сервиса и разбирали их код, параллельно рассказывая про слабые места и границы применимости своего любимого языка.

Рефакторим c Александром Макаровым, Валентином Удальцовым, Валентином Назаровым, Леонидом Корсаковым

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

Митап Фреймворки и инструменты PHP

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

Выше мы представили самые популярные свободные ответы. А вот как распределились голоса по предзаданым позициямВыше мы представили самые популярные свободные ответы. А вот как распределились голоса по предзаданым позициям

Найти все эти и другие записи можно в архиве стримов и митапов или в списке ютуб-каналов для PHP-разработчика.

1.2. Записи докладов

Назад к оглавлению

Cycle ORM и графы доклад Антона Титова с онлайн-конференции PHP fwdays'20. Отличия подходов ActiveRecord и DataMapper. Решение проблемы топологической сортировки зависимостей ORM, используя итеративную сортировку в глубину.

Самое интересное в PHP 8 JIT, Preloading и FFI и не только в докладе Дмитрий Стогова с PHP Russia 2019.

Поговорим про код доклад Александра Макарова с онлайн-конференции PHP fwdays'20. Разбор принципов, которые позволяют писать код, который ломается меньше.

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

PHP: Неправильный путь Кирилл Несмеянов о том, с чего начать изучение PHP, как качать свой скилл и что будет с языком дальше. Запись с митапа в Иваново в конце 2019-го.

30+ примеров угроз: формы, файлы, заголовки, браузер, консоль, БД Александр Макаров про безопасность в веб-разработке: от базовых вещей до особенностей PHP. Запись с казанского PHP-митапа в конце 2019-го.

Разработка гибридных PHP/Go-приложений с использованием RoadRunner доклад Антона Титова с конференции PHP Russia. Как демонизировать PHP-приложение для повышения производительности.

MySQL, который мы не знаем доклад Виктор Зинченко с онлайн-конференции PHP fwdays'20. Как организовать мониторинг MySQL с помощью Prometheus, Grafana и что делать с медленными запросами.

Рефакторинг PHP-кода с применением DDD Виталий Чирков на примерах показывает, какие приёмы сработали в его случае. Запись с митапа в офисе Badoo в феврале 2020-го.

Грамотное ООП: организация надёжной бизнес-логики доклад Дмитрия Елисеева с конференции PHP Russia 2019. Тренируемся в объектно-ориентированной декомпозиции для грамотного проектирования сущностей по обязанностям и учимся сочинять ко этому быстрые, удобные и надёжные юнит-тесты.

Быстрый способ разобраться с легаси и начать жить Сергей Жук про то, как превратить работу с легаси в увлекательное приключение. Или все переписать? Или и так сойдет? Запись с краснодарского PHP-митапа в ноябре 2020-го.

Спасибо этим и другим каналам, что делилсь видеоСпасибо этим и другим каналам, что делилсь видео

Зачем и как писать качественные Unit-тесты Алексей Солодкий рассказывает об основных концепциях unit-тестирования и том, как поддерживать качество тестов на проекте. Запись с митапа в офисе Badoo в марте 2019-го.

Как контрибьютить в Symfony и зачем это делать запись с митапа в рамках PHP Russia 2019.

От Doctrine ORM к CQRS за 20 минут Дмитрий Симушев о том, что делать, когда хаки с оптимизацией доктрины больше не работают. Запись с митапа в офисе Skyeng летом 2019-го.

Очень странные дела на PHP Кирилл Несмеянов о применении PHP за гранью веб-разработки. Запись с онлайн-митапа весной 2020-го.

Перенос проекта на PHP 7: от сбора фактов до результата Максим Шамаев о том, что делать, когда к вам придут и предложат разобраться с очень старым кодом. Запись с онлайн-митапа весной 2020-го.

Различные эволюции от старта до релиза в PHP продукте видео с онлайн-конференции PHP fwdays'20. Александр Савченко про None-Breaking change development , cross-stack контракты, Trunk Based development и много чего еще.

Строим Highload на PHP и Redis Михаил Мазеин про то, что делать с очередями из миллиона сообщений. Запись с нижегородского PHP-митапа в конце 2019-го.

Big Ball of Mud и другие проблемы монолита, с которыми мы справились Юлия Николаева о модульном монолите как альтернативе микросервисам. Запись с онлайн-митапа весной 2020-го.

1.3. Записи докладов на английском

Назад к оглавлению

Effortless Software Development

Package Design Principles in Practice

Queues, busses and the messenger component

Getting the most out of the PHP 7 engine the example of Symfony

More Than a Query Language: SQL in the 21st Century

2. О чем читали

Мы получили 360 уникальных релевантных ответов, по которым можно было восстановить ссылки на материалы, а затем отобрали из них самые упоминаемые.

А вот на закрытые вопросы люди отвечали охотнее)А вот на закрытые вопросы люди отвечали охотнее)

2.1. Самые упоминаемые материалы

Назад к оглавлению

Мне не нравится то, во что превращается PHP мощное заявление от @AlexLeonov, которое получило развитие на стриме к выходу PHP 8.

PHP 8 что нового? обзор нововведений от @rela589n, вышедший за день до релиза 8-ки.

Регулярные PHP-дайджесты от @pronskiy упоминались разные выпуски. Кстати, с этого года они доступны в виде стримов на ютубе.

Архитектура сложных веб-приложений. С примерами на Laravel перевод книги @Adelf перевел он сам и выложил для скачивания на GitHub.

Собеседование php-developer: вопросы и ответы подборка, которую составил @Nidhognit чтобы подготовиться к собеседованиям. Спасибо, что поделился с сообществом!

Что не так с трейтами? превью к стриму, где в итоге победили трейты и @SerafimArts :)

Сейчас я буду убеждать вас использовать статический анализ в PHP расшифровка подкаста Между скобок, в котором @seregazhuk и @vudaltsov обсуждают Psalm и не только.

Зачем ограничивать наследование с помощью final? материал из конца 2019 от @parshikov_pavel который нашел благодарного читателя в 2020-м.

Куда катится PHP, а также про Yii и другие фреймворки презентация от @SamDark, которую удобно читать.

2.2. Также часто упоминали

Назад к оглавлению

Aсинхронный PHP расшифровка доклада Антона Шабовты с PHP Russia 2019.

Как переиспользовать код с бандлами Symfony 5? цикл статей от Романа Науменко.

Как я пытался улучшить Laravel, а сделал только хуже не метод, а монстр или история одного коммита.

Ловушки для современного PHP еще одно размышление о судьбах PHP, если понравились материалы Альберта Степанцева и Саши Макарова, точно зайдет.

Занятное мини-интервью с основными контрибьюторами PHP 8 частичная расшифровка стрима с Никитой Поповым и Дмитрием Стоговым. Продолжение недавно вышло тут.

В карантин нагрузка выросла в 5 раз, но мы были готовы та самая история от Lingualeo. 685 комментариев под постом!

Теперь я не могу сделать даже маленький сайт мини-обзор того, что (не) должен знать современный веб-разработчик.

Среда разработки PHP на базе Docker как быстро создать на локальной машине универсальную среду разработки.

Мёртвый код: найти и обезвредить чтобы что-то добавить, нужно что-то удалить.

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

Spiral: высокопроизводительный PHP/Go фреймворк - обзор от автора инструмента

Почему стоит попробовать Drupal 9 - если вы ищете CMS.

Отпусти меня, PHP - ведущий телеграм-канала PHP Today делится болью.

НЕкостыль: gRPC-клиент на PHP в продакшене боевое решение, которое, к тому же, легко пишется.

Уязвимости PHP-фреймворков сколько из них знаешь ты?

FFI: пишем на Rust в PHP-программе материал из осени 2019-го, который вспоминают до сих пор.

DDD на практике туториал из 2018-го, который был актуален и в 2020.

2.3. Переводы

Назад к оглавлению

Улучшения покрытия PHP кода в 2020 году почему метрики покрытия кода врут (и что с этим делать).

Модернизация старого PHP-приложения какие антипаттерны ловить в вашем легаси. Больше такого из опыта российских компаний на PHP Russia этим летом.

Современный PHP без фреймворков статья о возможности стать лучше как разработчик.

Понимаем JIT в PHP 8 как это работает и почему прирост производительности (кажется) не будет колоссальным.

Эволюция PHP от 5.6 до 8.0 небольшая шпаргалка с продолжением.

2.4. На английском

Назад к оглавлению

Its not legacy code its PHP

Object Oriented Done Right

Laravel beyond CRUD: the next chapter

My journey into event sourcing

PHP 8: before and after

Commits are snapshots, not diffs

Modular Monolith: A Primer

p.s. Спасибо всем, кто поделился тем, что читал и смотрел в 2020-м!

p.p.s. Отдельное и огромное спасибо @jm_sub и @alyssashch за помощь в обработке данных.

Подробнее..

Без окон, без дверей как мы распилили монолит на сервисы

04.03.2021 12:16:30 | Автор: admin

Пост написан по мотивам доклада Антона Губарева @antgubarev, бывшего тимлида команды Teachers он готов ответить на ваши вопросы в комментариях.

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

Мы стали отвечать за набор фичей внутри монолита. Первые месяц или два пытались работать с тем, что было, но быстро поняли, что у двух команд свои процессы и взгляды на работу. Чтобы не утонуть в согласованиях, решили внедрять сервисную архитектуру.

Наши продукты

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

Исторически сложилось, что для ведения сделок используется внешний Мегаплан, который интегрирован с нашей TRM (Teachers Relations Management, внутри ее еще ласково зовут Tramway) системой, в которой хранятся данные по конкретным преподавателям и которую мы дополнительно используем при поиске учителя по узкому запросу.

Множество процессов и работа сотен людей зависят от этой связки.Множество процессов и работа сотен людей зависят от этой связки.

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

Так онбординг видят кандидаты в преподаватели. Асессоры выставляют свои слоты (время, когда они доступны для проведения уроков), а преподаватели бронируют уроки на время из слотов.Так онбординг видят кандидаты в преподаватели. Асессоры выставляют свои слоты (время, когда они доступны для проведения уроков), а преподаватели бронируют уроки на время из слотов.

Осенью 2019-го компания активно взялась за развитие этих систем, и за 2,5 месяца нам предстояло:

  • реализовать новые фичи. Школа росла: если раньше требовалось 100 преподавателей в месяц, то теперь 1000, и от нас во многом зависело, захлебнется или же выстоит подбор при масштабировании (кстати, те наработки здорово помогли в период карантина, когда многие педагоги решили попробовать онлайн-преподавание);

  • не утонуть в старом коде а значит, отрефакторить хотя бы те части, где рефакторинг назрел давно;

  • отделить свою работу от другой команды.

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

Какие были варианты

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

+

Точно успеем с фичами

Ничего толком не отрефакторим

Продолжим мешать друг другу с другой командой

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

+

Организуем фичи

Разберемся с легаси

Перестанем толкаться со второй командой

С высокой вероятностью не успеем по срокам

Нужно увеличить ресурсы на инфраструктуру: не факт, что их дадут

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

Мы взяли последнюю на тот момент LTS версию Symfony и принялись за работу.

Детали реализации

Структура папок. Все независимые сервисы-продукты мы стали складывать в папку modules. У нас получились выделить такие сервисы:

  • интеграция с Мегапланом;

  • бэкенд календаря тренировочных уроков преподаватель асессор;

  • бэкенд онбординга;

  • и несколько прикладных проектов: работа с очередями, декораторы, http-клиенты, мониторинг, метрики, логирование.

В папке src практически ничего не было только Kernel.php и некоторые совсем общие инфраструктурные вещи.В папке src практически ничего не было только Kernel.php и некоторые совсем общие инфраструктурные вещи.

Общие пакеты положили в папку packages: в основном это клиенты для различных API, внешних и внутрикомпанейских сервисов, а также общая структура данных и DTO, которая используется для межмодульного взаимодействия.

Во всех сервисах независимая конфигурация и полное отсутствие межмодульных use.Во всех сервисах независимая конфигурация и полное отсутствие межмодульных use.

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

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

Конфигурация всех модулей лежит в самих модулях.Конфигурация всех модулей лежит в самих модулях.

Межмодульное взаимодействие. Помимо REST API, мы используем шину очередей. Для нас крайне важна целостность данных, а менеджеры сопровождения часто меняют статусы у сделок, и если мы потеряем их из-за того, что не получилось отправить запрос по http, это может поломать весь дальнейший процесс онбординга.

С учетом, что любой сервис может прилечь, очереди решают для нас эту проблему. Если что-то идет не так, то даже многотысячная очередь в Rabbit разгребается за несколько минут.С учетом, что любой сервис может прилечь, очереди решают для нас эту проблему. Если что-то идет не так, то даже многотысячная очередь в Rabbit разгребается за несколько минут.

Например, преподаватель записался на тренировочный урок и нам нужно записать это в карточку (она же сделка) в CRM. Модуль онбординга, используя пакет megaplan-client, положит в очередь сообщение, что нужно изменить конкретное поле. Модуль Мегаплана содержит в себе консьюмеры, которые разгребают эту очередь. Работает и в обратную сторону. Мегаплан пошлет нам веб-хук, когда что-то изменилось, мы положим его в сыром виде в очередь, а онбординг эту очередь вычитает и что-то поменяет. Да, тут есть задержка, но она не критична, а данные точно сохранны.

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

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

Также мониторим все http-запросы: пишем в Elasticsearch, смотрим в Kibana.Также мониторим все http-запросы: пишем в Elasticsearch, смотрим в Kibana.

У нас в компании есть общий request-id бандл, разработанный специально для таких целей. Его довольно просто использовать в контексте Symfony: можно либо handler вручную добавить, либо через контейнер сконфигурировать.

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

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

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

Что получилось в итоге

Уже больше года мы живем на продакшене. При этом вышли за сроки буквально на пару недель, но смогли реализовать все фичи (так что у бизнеса претензий не было) и вынесли 2 из 4 сервисов.

У нас свой код. Весной 2019-го у нас набралось меньше 10 коммитов в старый код: туда мы ходим в крайних случаях. При этому мы успели отрефакторить самые проблемные участки, а еще где-то половину доделывали в спокойном темпе. У нас нет фича-фриза если приходит задача, первым делом думаем, что еще можем вытащить из старого кода, и делаем в ее рамках текущего проекта.

У нас свои процессы. Мы не зависим от команды, с которой работали вначале. Ввели свой контроль архитектуры: если человек, который получил задачу, видит, что где-то надо что-то менять, он собирает всю команду. Мы исторически работаем распределенно, поэтому записываем встречу в Google Meet и тут же на ней фиксируем решения. А потом прикладываем все записи к задаче можно в любой момент пересмотреть и понять, почему было так.

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

Выросли до 5 разработчиков. Новые люди, приходя в проект, не испытывают сложностей со входом: не надо разгребать код, понимать, как он работает и пр.

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

Подробнее..

Каким будет 2021-й год для PHP?

15.02.2021 16:13:26 | Автор: admin

Об этом мы спросили Никиту Попова, Дмитрия Елисеева и еще десяток активных контрибуторов и авторов контента из сообщества. Все они поучаствуют в большом PHP-стриме днем 27 февраля (это суббота). Будет пара свежих докладов, несколько острых дискуссий, розыгрыш фирменных PHP-слонов и других крутых подарков. Подключайтесь!


Роман Пронский (JetBrains), автор PHP-дайджеста. Соведущий нашего митапа

Каким будет 2021-й год для PHP, чего ждешь?

Будет PHP 8.1, будут крутые движухи в сообществе.

Самое плохое, что случилось в мире PHP в 2020?

Отмена конференций. Особенно жалко, что не было офлайн-версии PHP Russia очень жду ее в этом году!

Ок, а самое хорошее, что случилось в мире PHP в 2020 (кроме релиза 8-ки)?

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

Онлайн-митапы плотно вошли в жизнь и оказались довольно крутыми. Благодаря им удалось познакомиться с кучей новых людей. Плюс, мы запустили с Валентином Удальцовым свой канал PHP Point.

А еще на свет появился панк-слоник PhpStorm. Правда, из-за падемии не удалось завезти его в Россию и Украину. Надеюсь в 2021-м он наконец-то доберется до нас физически.


Валентин Удальцов, автор телеграм-канала Пых. На митапе поучаствует в одной из дискуссий

Каким будет 2021-й год для PHP?

Надеюсь, что офигенным! Со своей стороны хочу реализнуть до лета библиотеку Thesis для нативной работы с SQL.

Самое хорошее, что случилось в контексте PHP в 2020?

Я интегрировался с русскоговорящее PHP-сообщество: это и год помогло провести веселее, и узнать-рассказать много нового.


Дмитрий Елисеев, автор блога ElisDN. На митапе поучаствует в одной из дискуссий

Каким будет 2021-й год для PHP и сообщества?

Надеюсь на возобновление оффлайн-активности. Хочется погулять по митапам.

Самое хорошее, что случилось в контексте PHP в 2020?

У меня в 2020-ом случилось внедрение вышедшего в 2019-ом PHP 7.4. Семёрка мне была интересна в основном явной типизацией. Вот наконец type hints добрались и до полей. В проектах вычистил кучи PHPDoc-ов и стало удобнее для Psalm-а.

Понравился рост распространённости Psalm. Его активно внедряют популярные библиотеки вроде Doctrine, Laminas. Да и для кода без него появляются плагины и стабы. Это позитивно сказывается на качестве интерфейсов. Уходят union types. Если в начале года приходилось туго с его внедрением в проект, то сейчас намного приятнее. Но пока с этим туго у некоторых стандартных PHP-функций.

Также за год прилетело много мажорных релизов. Тот же сильно ускоренный Composer 2 и новый Xdebug 3. Обрадовала интеграция сниферов и Psalm в PhpStorm.

Самое плохое, что случилось в мире PHP в 2020?

Немного неприятно, что даже сейчас, в феврале 2021-го ещё не все библиотеки перевели на восьмёрку. А в остальном: меня все устраивает.


Илья Левин, разработчик в Skyeng, на митапе сделает доклад Как работают видеоконференции в браузере: фронт и бэк

Каким будет 2021-й год для PHP?

Жду совершенствования типизации и прочие удобняхи, которые позволят писать короткий и понятный код. 8-ка предоставила новые фичи в этом плане, но хочется еще лучше, короче, быстрее. Жду 8.1 и Symfony 5.3(Symfony просто прелесть, моя прелесть)))

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

Самое хорошее, что случилось в мире PHP в 2020 (кроме релиза 8-ки)?

Если не говорить про 8-ку, отмечу два минорных релиза Symfony 5.1 и 5.2. Несмотря на пандемию, из-за которой пострадал весь мир, команда Symfony стабильно выпускает новинки.


Антон Околелов, ведущий подкаста Цинковый прод

Каким будет 2021-й год для PHP?

Хотелось бы асинхронных штук из коробки. Fibers мне не очень понравились, какие-то громоздкие конструкции, всё вручную. Это скорее подпорка для существующих решений-костылей (Swoole и тд). Но это тоже шаг вперед, по крайней мере кто-то задумался о проблеме.

Enums ждём, а еще лучше сразу с tagged unions и паттерн-матчингом.

Самое плохое, что случилось в мире PHP в 2020?

Отменили конференцию PHP Russia из-за пандемии. И еще я узнал, что Дмитрий Стогов не считает отсутствие асинхронщины в php проблемой номер один, это прям расстроило. Вот тот момент на видео:


Николай Пучко, автор телеграм-канала PHP Today. На митапе поучаствует в одной из дискуссий

Каким будет 2021-й год для PHP?

Судя по некоторым RFC - противоречивым=) Очень боюсь, что станет настолько сладко, что слипнется, - если вы понимаете о чем я. Но некоторые вещи, например, enum, я очень жду.

Также жду релиз Yii3 - хочется чего-то новенького уже потрогать. Когда-то давно Yii поразил своей коробочной завершенностью. Кажется, что тройка по функционалу вполне сможет потягаться с Symfony.

Самое хорошее, что случилось в мире PHP в 2020 (кроме релиза 8-ки)

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

Обратный вопрос

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


Антон Титов (SpiralScout), автор RoadRunner, на митапе представит самые упоминаемые доклады-2020 по версии сообщества

Каким будет 2021-й год для PHP?

Очень клевым! Жду интеграции Temporal.io в PHP, RoadRunner 2.0 и еще больше инструментов.

Самое хорошее, что случилось в мире PHP в 2020?

Релиз 8-ки все затмил!

Самое плохое, что случилось в мире PHP в 2020?

PHPclub окончательно скатился в неюзабельное состояние из-за токсичности.


Сергей Жук (Skyeng), автор подкаста "Между скобок". Соведущий нашего митапа

Каким будет 2021-й год для PHP?

Всё больше и больше внимания уделяется "асинхронной теме". Ожидаю развития в этом направлении: fiber-ы и фреймворки поверх ReactPHP.

Самое хорошее, что случилось в мире PHP в 2020 (кроме релиза 8-ки)?

Язык прошел отметку 25 лет. Дальше только туземун!

Самое плохое, что случилось в мире PHP в 2020?

Не состоялась PHP Russia.


Константин Буркалёв, ведущий подкаста SDCast. На митапе поучаствует в одной из дискуссий

Каким будет 2021-й год для PHP?

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

Самое хорошее, что случилось в мире PHP в 2020 (кроме релиза 8-ки)?

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


Александр Макаров (Yii), руководитель ПК PHP Russia. На митапе поучаствует в одной из дискуссий

Каким будет 2021-й год для PHP?

Интересным. Будут новые RFC. Будут апдейты в рекомендациях PHP-FIG. Надеюсь, релизнем Yii 3.

Самое хорошее, что случилось в мире PHP в 2020 (кроме релиза 8-ки)?

GitHub actions начали повсеместно использовать. Psalm. PhpStorm научился новым крутым штукам.


Пётр Мязин, ведущий подкаста Пятиминутка PHP. На митапе поучаствует в одной из дискуссий

Каким будет 2021-й год для PHP?

Ждём живого общения и тусовок в offline: PHP Russia, BeerPHP и других митапов

Самое хорошее, что случилось в контексте PHP в 2020 (кроме релиза 8-ки)?

Попробовал админ-панель Laravel Orchid, классная штука, по ходу дела появилось миллион идей как бы я всё переделал, улучшил или зарефакторил, но пока держу себя в руках :)

Обратный вопрос

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


Антон Жуков, разработчик в ManyChat. На митапе сделает доклад Тандем приложений на gRPC

Каким будет 2021-й год для PHP?

Время покажет. Я жду скорейшего LTS 8-й версии PHP и перехода на более стандартизированную версию PHP. Слабая стандартизация породила огромное количество legacy и продолжает этим заниматься.

Самое хорошее, что случилось в мире PHP в 2020 (кроме релиза 8-ки)?

Серьезно обновилась документация. Появилось большое количество материалов по дебагу низкого уровня, асинхронке, тестированию. PHP всё быстрее идет в сторону языка с серьезной профессиональной поддержкой со стороны комьюнити. Это не может не радовать. Качество контента постепенно вытесняет количество. Это дорогого стоит.


Никита Попов (JetBrains), один из core team PHP. На митапе поучаствует в одной из дискуссий

Каким будет 2021-й год для PHP?

You tell me ;)

Самое хорошее, что случилось в мире PHP в 2020 (кроме релиза 8-ки)?

Оригинал ответа на английском

Okay, okay I think a small but important thing that happened in 2020, is the introduction of a "life support" phase for PHPUnit: After a PHPUnit version becomes unsupported, it will still be made compatible with new PHP versions for some time. For example, if you want to add support for PHP 8 to your library, you no longer need to also migrate to PHPUnit 9 at the same time, as PHPUnit 8 will also work. This is a big deal for me, because in my experience, making open-source libraries compatible with new PHP versions is very little about breaking changes in PHP, and a lot about breaking changes in PHPUnit.

Тэкс, дай подумать Думаю, что небольшая, но важная вещь, которая произошла в 2020 году, это долгосрочная поддержка PHPUnit: старые версии инструмента, которые больше не поддерживаются, будут оставаться совместимыми с новым версиями PHP какое-то время. Скажем, если вы хотите добавить в свою библиотеку поддержку PHP 8, то не надо еще и мигрировать на PHPUnit 9. C PHPUnit 8 тоже всё будет работать. Это важно, так как по моему опыту, если ты хочешь сделать опенсорсную библиотеку совместимой с новой версией PHP, в основном вопрос будет не в PHP, а именно в PHPUnit.

Самое плохое, что случилось в мире PHP в 2020?

Оригинал ответа на английском

It's not something "bad", but I'm somewhat disappointed in the practical impact of the Just-in-Time compiler in PHP 8. All past experience did indicate that the JIT compiler would probably not have much impact on typical web code, but I was still hoping that it would turn out differently in the end. The inheritance cache recently introduced in PHP 8.1 promises to have a much larger practical impact, with much less effort. Of course, there are cases where the JIT is useful, but I'm not quite convinced that the amount of effort Dmitry put into this project was worth it.Не то чтобы это было плохо, но я немного расстроен практической применимостью JIT-компилятора в PHP 8. Все тесты указывали, что JIT не будет сильно влиять на типичный код веб-приложений, но я до последнего надеялся. Inheritance cache в PHP 8.1 выглядит гораздо более обещающим с практической точки зрения, а усилий на него положено меньше. Безусловно, я знаю примеры, когда JIT оказался по-настоящему полезен, но я пока так и не уверен, что оно стоило времени и сил, которые Дмитрий положил на этот проект.

Не то чтобы это было плохо, но я немного расстроен практической применимостью JIT-компилятора в PHP 8. Весь прошлый опыт указывал на то, что JITособо не повлияет на типичные веб-приложения, но я до последнего надеялся. Inheritance cache в PHP 8.1 выглядит гораздо более многообещающим с практической точки зрения, а усилий на него положено меньше. Безусловно, я знаю примеры, когда JIT оказался по-настоящему полезен, но я пока так и не уверен, что оно стоило времени и сил, которые Дмитрий положил на этот проект.

P.S.

Никита и Дмитрий, судя по опросу русскоязычного PHP-сообщества об итогах 2020-го, JIT зашел многим. Спасибо за него и не только!

Остальные результаты этого опроса огласим на стриме.

P.P.S.

А еще разыграем пару слонов и другие полезные и приятные подарки для PHP-разработчика.

Полный список ништяков и другие подробности - тут.

Подробнее..

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

23.11.2020 14:22:47 | Автор: admin
У PHP отличное сообщество. Пандемия отобрала у нас митапы и конференцию, но мы можем собраться 25 ноября вечером в онлайне на:

  • доклад PHP 8: юзерленд нескучный обзор с примерами и рекомендациями,
  • дискуссию о развитии языка,
  • и сессию Q&A с Никитой Поповым (вопросы соберем по ходу эфира).


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


Роман pronskiy Пронский автор PHP-дайджестов и главный по маркетингу в PhpStorm. Соведущий нашего митапа


Что было самым крутым в PHP 5?
Неймспейсы, оператор goto ( )

А самым ужасным и вредным?
Трейты.

Когда PHP 6 не увидел свет, ты
Искал свою первую работу, связанную с PHP.

Самое крутое в PHP 7?
Zend Engine 3 производительность!

А самое бесполезное в 7-ке?
Не помню ничего такого.


Александр SamDark Макаров соавтор Yii3, главный по программе PHP Russia. Соведущий нашего митапа


Что было самым крутым в PHP 5?
Zend Engine 2. Нормальное ООП в сравнении с PHP 4 + много классных фич в промежуточных релизах.

А самым ужасным?
Ничего прям ужасного не вспомню.

Когда PHP 6 не увидел свет, ты
В общем-то, не заметил этого :) Но потом, когда вчитался, понял, какой объём работ был выкинут и насколько это должно быть обидно.

Самое крутое в PHP 7?
Скорость, типизация, ??, анонимные классы.

А самое бесполезное в 7-ке?
Семёрка хорошая.


Валентин vudaltsov Удальцов автор телеграм-канала Пых, активный контрибьютор в Symfony


Что было самым крутым в PHP 5?
То, что больше не снится мне в страшных снах.

А самым ужасным?
trait

Когда PHP 6 не увидел свет, ты
Перешёл на QBasic.

Самое крутое в PHP 7?
??=

А самое бесполезное в 7-ке?
trait.


Альберт alexleonov Степанцев, автор статьи Мне не нравится, во что превращается PHP


Что было самым крутым в PHP 5?
Самое крутое, что он был вообще. Без него бы не было современных версий.

А самым вредным?
Пятерка была разной: между 5.6 и 5.0 скорее больше различий, чем чего-то общего. Язык изменился неузнаваемо. Лично для меня, наверное, самым вредным стало то, что длина int зависит от разрядности операционной системы.

Когда PHP 6 не увидел свет, ты
Хотел скупить все книги по PHP 6, которые успели издать.

Самое крутое в PHP 7?
NG.

А самое бесполезное в 7-ке?
Когда-то считал бесполезными анонимные классы. Теперь так не считаю.

p.s. Стрим организован совместными усилиями Онтико (PHP Russia), devrel-команды Skyeng и проекта PHP Point. Отдельное спасибо Алисе alyssashch Мартыновой из ростовского ИТ-сообщества RnD Tech за помощь с сайтом митапа.
Подробнее..

Хьюстон, у нас проблема с интерпретацией ошибок

03.02.2021 10:04:43 | Автор: admin


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


В ожидании комбинаторного взрыва


Я работаю в команде сервиса расписания занятий, который оркестрирует весь процесс подбора преподавателя в Skyeng. Когда новый клиент хочет найти учителя, нам приходит запрос: ID ученика такой-то хочет заниматься с такого-то числа и в такое-то время. Чтобы подобрать преподавателя, мы идем в сервисы других команд:


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

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


image


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


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


Бывает и такое бизнес приходит и просит А добавьте тут... Допустим, у нас есть форма ввода данных, в ней 10 полей, мы валидируем девять, а десятое это комментарий, он опционален. В один день нас просят добавить валидацию на комментарий. Фронт говорит: Если придет такой-то код, я у себя доработаю. Ты пишешь новую обработку: и вот на 10 ошибок у фронта появляется 10 обработчиков, а еще у тебя есть разные вариации if-ов, else-ов и т.д.


При этом сами ошибки возникают редко и это тоже проблема.


image


При взаимодействии между сервисами мы используем REST, а он как таковой не регламентирует нам форматы, кроме того, что основан на HTTP.


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


Давайте попробуем решить эту проблему


Мы c командой посмотрели обсуждения в комьюнити, посмотрели ролики на ютубе, статьи на хабре (вот интересный хабра-холивар про 200-й ответ). Похоливарили внутри. Пришли к выводу, что серебряной пули не существует и пошли своим путем. Ввели единый формат, при котором у нас есть:


1. Поле data для полезной нагрузки скажем, вот так может выглядеть успешный ответ:


{    "data": [        {            "field1": 1        },        {            "field2": 2        }    ],    "errors": null}

2. И поле errors для ошибок.


..."data": null,"errors": [    {        "property": null,        "error": {            "message": "The selected time is alr...",            "code": "selected_time_already_taken"        }    }]

Поля data и errors в ответе не могут быть заполнены одновременно.


У нас большое разнообразие ошибок, поэтому на каждую вариацию мы добавили свои правила.


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


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


  • Непредвиденные ошибки: где-то что-то забыли, линтер не допроверил всякое бывает.
  • Инфраструктурные ошибки например, ошибки Redis Cluster, ошибка связанности или если приложение не дождалось чего-нибудь другого. Мы понимаем, что у сервиса что-то произошло в эту секунду, и можем отреагировать с помощью retry. Такие ошибки удобно обрабатывать с помощью воркера, который периодически ходит по сервисам и опрашивает их: прошел, собрал метрики, хоп что-то отвалилось, сделали retry.
  • Ошибки валидации входных данных. Они вылазят где-то на тестингах, на dev-режиме, и должны быть максимально полезными для разработчика. Мы используем Symfony serializer и validator, чтобы маппить все, что пришло на вход: чтобы не просто узнать, что Error validation, а понять, что именно не так, мы выделили отдельный класс ошибок, которые выдаются массивом.

Пример ошибок, которые используем


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


Мы создали иерархию Exception. Например, делаем базовый класс ValidationException и от него наследуем всё, что зависит от валидации. Это может быть TeacherValidationException. А у него свои наследники NicknameException, AgeException и так далее.


Дальше делаем Exception, который хранит подробную информацию о бизнес-ошибке. Бывают ситуации, когда нам важно понимать, на каком слое что-то пошло не так. Тогда каждого слоя мы получаем свои Exception: есть API-репозиторий с APIRepositoryException, выше идет сервис, у него есть ServiceException, потом идёт Exception контроллера и т.д.


Остается сделать единую точку выхода из приложения, которая позволит отлавливать все исключения и выдавать код формате с data и errors. Это просто сейчас так или иначе все фреймворки позволяют отлавливать Exception ивентами.


Вот как это выглядит на практике:


class ValidationException extends \Exceptioninterface HttpExceptionInterface extends \Throwable

ValidationException бросаем, если Symfony validator собрал что-то, что не позволяет нам продолжить дальше. Здесь же есть HttpExceptionInterface, который кидает Guzzle, дабы мы понимали, какой ответ http, какой response, какие headers и так далее нам пришли.


class SomeServiceException extends \Exception

SomeServiceException у каждого сервиса есть FeaturesException или какой-нибудь ещё Exception, которые позволяют нам пробрасывать информацию между слоями приложения. Мы можем понять, что у нас произошла бизнес-ошибка, и вывести ее на фронт, либо подавить и отдать ответ о том, что мы ответим позже. Это дает вариативность в нашей логике.


interface ConventionalResponseExceptionInterface extends \Throwable{    public function getConventionalCode(): string;    public function getProperty(): ?string;}

ConventionalResponseExceptionInterface есть интерфейс бизнес-ошибки, фронту нужно получить информацию и вывести текст. Мы на бэкенде рулим тем, что увидит пользователь. В поле Property у нас может быть запись teacher то есть, что-то произошло на стороне учителя. А ConventionalCode это какой это уникальный код ошибки. Допустим, это может быть Selected time have already taken выбранное время уже занято. Фронт по факту пишет один обработчик на такие типы ошибок, а мы просто бросаем Exception, который наследует такой интерфейс. Он возвращает все нужные данные, а фронт добавляет в обработку ошибок этот код. Больше ничего дорабатывать не нужно.


Как внедрить на практике


Самое главное мы не можем сказать бизнесу, что решили всё переписать, чтобы у нас все работало. Такие вещи делаются с сохранением обратной совместимости: вы вводите API версии 2, которое теперь работает с нужным форматом, и делаете доработки на уровне middleware, либо OnKernelRequest, чтобы каждый раз не проверять, новая это апишка или старая.


Подключаем единый обработчик исключений. Так как мы используем Symfony, то просто сконфигурировали listener, который подписан на события onKernelException. По факту, он перехватывает и обрабатывает любое исключение, которое возникает в системе при версиях PHP старше семёрки проблем не возникает.


kernel.listener.conventional:  class: ConventionalApiExceptionListener  tags:   - {      name: kernel.event_listener,      event: kernel.exception,      method: onKernelException   }

Сам код listener-а очень большой, покажу интересный кусок: приходит event, он содержит в себе instance Exception-а, мы проверяем, к какому типу ошибки это относится, и в зависимости от этого формируем список наших ошибок.


switch (true) {            case $exception instanceof ConventionalResponseExceptionInterface:               ...                break;            case $exception instanceof ValidationException:               ...                break;            case $exception instanceof BadRequestHttpException:               ...                break;            case $exception instanceof HttpExceptionInterface:               ...                break;            case $exception instanceof SomeServiceException:              ...                break;            default:            ...        }

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


В логах есть три уровня приоритета info, warning и critical. В зависимости от приоритета, ошибки попадают в разные хранилища: не критичное в файлы, критичное в Slack. Бизнес-ошибки уходят в info, всё остальное у нас либо warning, либо critical.


Отдельно скажу про 500-е. Мы проверяем environment, и на дев-стенде и тестингах позволяем себе вывести весь стэктрейс пятисотки. Если это прод, мы просто меняем ошибку на текст Internal Error и делаем проброс в Sentry, а Sentry стучится в Slack к разработчику: Бросай все, горим.


Спойлер

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


А теперь нам нужно как-то отличить ошибки от других сервисов по нашему регламенту на нашей стороне.


К старым апишкам мы проверки, о которых расскажу, не применяем, только к тем, о которых договорились. Допустим, у нас есть какой-то service class, который ходит на чужой API, с котором договорились.


Проверяем, корректен ли ответ: если пришел не JSON сразу бросаем Exception.


try {    $decodedResponse = json_decode($contents, true, 512, \JSON_THROW_ON_ERROR);} catch (\Throwable $exception) {   return false;}

Также проверяем HTTP-статусы: есть 4 варианта, о которых мы договорились, что это корректная ситуация.


$isHttpStatusOk = in_array(    $code,    [           Response::HTTP_OK,           Response::HTTP_CREATED,           Response::HTTP_ACCEPTED,           Response::HTTP_NO_CONTENT,    ],    true);if ($isHttpStatusOk === false) {    return false;}

Если отсутствуют ключи data или errors, тоже считаем, что это не по регламенту. В остальном главное, чтобы ошибок не было: если в поле errors не пришло ошибок, значит все ок.


if (!array_key_exists('errors', $decodedResponse)    ||    !array_key_exists('data', $decodedResponse)) {    return false;}return $decodedResponse['errors'] === null;

Положив этот код в trait, мы используем его в фасадах нескольких API-репозиториев: и теперь нам не нужно добавлять новый обработчик на каждый чих, а затем тыкать все апишки упадут или нет.


В каких случаях подход дает выгоду, в чем минусы, как поддерживать


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


Но есть нюансы, которые мы уже увидели на практике:


  • Подход требует ответственного отношения от старших разработчиков и тимлидов: мы должны поддерживать при разработке единый формат. В каждом код-ревью нужно вникать и понимать, тот ли тип Exception-а вернулся, если что создать новый, назвать его корректно, наследовать его от нужного типа ошибки. А когда регламент поменяется (его придется менять), потому что у вас появилась новая договоренность с другими командами, нужно не полениться сходить к соседям и сделать pull request.
  • Регламент усложнит онбординг новичков: но зато когда человек вник, увидев ошибку в логах, он будет четко понимать, что произошло и куда бежать.

Но несмотря на эти трудности мы довольны. Экономим время при обнаружении ошибки и добавление новых однотипных ошибок. А как устроено у вас?

Подробнее..

Ты приходишь в проект, а там легаси

22.04.2021 10:12:30 | Автор: admin

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

Картина, конечно, немного удручающая, но давайте отбросим эмоции. И посмотрим, что можно быстро предпринять, чтобы облегчить страдания.

Почему мы чувствуем себя несчастными, работая с легаси-кодом?

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

Но с точки зрения старшего разработчика часто все выглядит так:

  • приложение очень хрупкое: если фиксим где-то баг, в ответ прилетают два новых;

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

  • новые фичи занимают все больше времени: откладываются релизы, давят дедлайны, вы начинаете сознательно увеличивать время на планировании.

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

У меня есть одна история. Приходит менеджмент, говорит, что нам нужна эта фича, причем еще вчера. Мы работаем изо всех сил, релизим как можно быстрее. Затем говорим менеджменту: Теперь нам нужна неделя, чтобыотрефакторить, написать тесты. На что менеджер отвечает: Что? Выэтого несделали?.

Когда вас просят сделать что-то, от вас ожидают что-то готовое. А если фича нужна настолько срочно, что к ней даже не нужна пожарная сигнализация в виде тестов, это должно быть оговорено в самом начале. И наша задача как разработчиков донести это до бизнеса. Я хочу поделиться подходом, который мы применили в команде мобильного бэкенда Skyeng это путь инкрементального улучшения легаси-проекта.

Что будет, если ничего не делать (и почему переписать не всегда выход)

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

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

Есть другая крайность. Мы видим старую систему. Думаем: Если тот криворукий, кто написал этот ужас, смог заставить всё работать, тонаверняка это просто. Давайте перепишем всё уже по-нормальному. Сделаем красиво. Но это ловушка. Легаси-код уже пережил много деплоев и изменений требований. Начиная с начала, мы думать не думаем и знать не знаем о всех тех трудностях, которые пережил тот код. Да, мы пытаемся делать предположения, но зачастую они неверны. Почему так?

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

Я не хочу сказать, что это не работает. У меня есть позитивный опыт переписывания легаси-системы: но, во-первых, тот, кто писал старый проект, и переписывал его. Во-вторых, бизнес дал добро, что на определенный промежуток времени мы замораживаем систему ничего больше не добавляем, не фиксим без крайней необходимости. Плюс у нас под рукой всегда был доменный эксперт. Но у меня до сих пор есть ощущение, что нам повезло и это было скорее исключение, подтверждающее правило.

Шаг первый: пишем smoke-тесты, которые скажут, что приложение хотя бы живое

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

Что использовать? Нам понравился Codeception у него приятный интерфейс, можно быстро накидать smoke-тесты на наиболее критичные эндпоинты, сами тесты выходят лаконичными и понятными. А еще у него такой выразительный API и тест можно читать прямо как user story.

Что покрывать? Правило простое: покрываем тот функционал, который отвечает за бизнес-домен. В нашем случае это были тренировки слов мыпокрыли smoke-тестами его. Например, у нас был эндпоинт, который отдавал выученные пользователем значения слов.

/** * @see \WordsBundle\Controller\Api\GetLearnedMeanings\Controller::get() */final class MeaningIdsFromLearnedWordsWereReceivedCest{   private const URL_PATH = '/api/for-mobile/meanings/learned';    public function responseContainsOkCode(SmokeTester $I): void   {        }}

Мы явно прописали эндпоинт, который будем тестировать, и через аннотацию @see сделали указание на контроллер. Это удобно тем, что по клику в IDE можно сразу перейти в контроллер. И наоборот, по клику на экшен контроллера можно перейти в тест.

Сам тест был максимально прост: подготавливаем данные, сохраняем в базе выученное пользователем слово, авторизуемся под этим юзером, делаем get-запрос к API и проверяем, что получили 200.

public function responseContainsOkCode(SmokeTester $I): void{          $I->amBearerAuthenticated(Identity::USER);   $I->sendGET($I->getApiUrl(self::URL_PATH));   $I->seeResponseCodeIs(Response::HTTP_OK);}

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

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

public function responseContainsLearnedMeaningIds(SmokeTester $I): void{          $learnedWord = $I->have(       UserWord::class,        ['isKnown' => true, 'userId' => $I->getUserId(Identity::USER)]   );   $I->amBearerAuthenticated(Identity::USER);   $I->sendGET($I->getApiUrl(self::URL_PATH));   $I->seeResponseCodeIs(Response::HTTP_OK);    $I->seeResponseJsonMatchesJsonPath('$.meaningIds');    $I->seeResponseContainsJson(    ['meaningIds' => [$learnedWord->getMeaningId()]]    );

Так постепенно мы покрыли все критичные части нашего приложения и могли перейти непосредственно к коду.

Про тесты

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

Шаг второй: удаляем неиспользуемый код

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

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

Шаг третий: делаем слой вывода

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

Например, у нас есть контроллер, который возвращает баланс пользователя. В этом примере мы просто достаём из репозитория сущность и как есть отдаём наружу то есть как только мы порефакторим, API у клиента поменяется.

final class Controller{   private Repository $repository;   public function __construct(Repository $repository)   {       $this->repository = $repository;   }    public function getBalance(int $userId): View   {       $balance = $this->repository->get($userId);       return View::create($balance);   }}

Чтобы решить проблему, можно вместо этого в ответах всегда использовать простую DTO пускай у нее будут только те поля, которые нужны в ответе клиентам. Добавим маппинг из сущности и уже в контроллере это вернем.

final class Balance{   public int $userId;   public string $amount;   public string $currency;   public function __construct(int $userId, string $amount, string $currency)   {       $this->userId = $userId;       $this->amount = $amount;       $this->currency = $currency;   }}

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

final class Controller{   private Repository $repository;   public function __construct(Repository $repository)   {       $this->repository = $repository;   }    public function getBalance(int $userId): View   {       $balance = $this->repository->get($userId);       return View::create(Balance::fromEntity($balance));   }}

Можно спокойно начинать улучшать и рефакторить, не боясь сломать обратную совместимость API.

Шаг четвертый: статический анализ кода

В современном PHP есть strict types, но даже с ними можно по-прежнему менять значение переменной внутри самого метода или функции:

declare(strict_types=1); function find(int $id): Model{    $id = '' . $id;     /*     * This is perfectly allowed in PHP     * `$id` is a string now.     */     // } find('1'); // This would trigger a TypeError. find(1); // This would be fine.

А так как типы проверяются в runtime, проверки могут зафейлится во время выполнения программы. Да, всегда лучше упасть, чем словить проблем из-за того, что строка внезапно превратилась в число (причем не то число, которое мы бы ожидали увидеть). Но иногда хочется просто не падать в runtime. Мы можем выполнить проверки до выполнения кода с помощью дополнительных инструментов: Psalm, PHPstan, Phan и так далее.

Мы в проекте выбрали Psalm. Возможно, у вас возник вопрос: зачем тащить в легаси-проект статический анализ, если мы и без него знаем, что код там не очень? Он поможет проверить неочевидные вещи. В легаси-проектах часто встречается так называемый array-driven development структуры данных представлены массивами. Например, есть легаси-сервис, который регистрирует пользователей. Он принимает массив $params.

final class UserService{   public function register(array $params): void   {       // ...   }}$this->registration->register($params);

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

final class UserService{   /**    * @psalm-param array{name:string, surname:string, email:string, age:integer} $params    */   public function register(array $params): void   {       // ...   }}$this->registration->register($params);

Прописав через докблоки формат и тип данных, при следующем рефакторинге мы запустим Psalm, он проверит все возможные вызовы и заранее скажет, сломает ли код. Достаточно удобно при работе с легаси.

Что дальше?

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

А дальше просто подумайте, что еще нужно, чтобы чувствовать себя счастливым, работая с этим кодом. Чего еще не хватает? Составьте себе план действий и постепенно улучшайте доставшийся вам в наследство код.

p.s. Несколько полезных материалов для дальнейшего изучения:

Подробнее..

Если у родителей нет планов на будущее собственного ребенка, они есть у государства

28.01.2021 16:20:38 | Автор: admin

А если у подростка, а потом и у взрослого тоже нет планов на собственное будущее вы уже знаете ответ. До этого важного тезиса мне пришлось идти овердофига лет.

Иными словами.

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

У чиновников точно есть планы на наших детей и на нас. Ребенок мечтает быть инженером? Но стране нужны другие специалисты. И можно не сомневаться: если не прикладывать усилия к развитию ребенка, он не будет инженером с очень высокой вероятностью.

Знаешь, чему учить ребенка и чему учиться самому делай. Не знаешь сдавай в детсад, школу, училище и институт, и там точно научат тому, что хочет заказчик.

Пожалуй, самый наглядный пример, к чему это приводит, мы наблюдали в 2020-м

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

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

Как по-вашему, какой процент людей, голосовавших по поправкам к Конституции, их прочитали и обдумали? Или реально прочитали программу кандидата, за которого поставили галочку? Подумали, сравнили, выбрали оптимум? Правильно, единицы. Не процентов, а людей. Я безусловно уважаю любой осознанный выбор. Но именно осознанный. Любой другой могу только принять. Но никак не уважать.

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

Еще раз, медленнее. Люди. За свою жизнь. Тратят больше времени. На выбор вкусов йогуртов и пиццы. Чем на выбор карьеры. И других важных решений.

Что такое важное решение? Выбор, который заметно меняет жизнь человека. Приведу пример. В марте 2020 года банки ставили ежесуточные рекорды по выданным ипотекам. Что может быть опаснее, чем брать огромный кредит в начале кризиса, когда вырастают шансы потери работы или сокращения доходов? Ну и уж точно резко падает предсказуемость жизни. Были ли решения этих людей качественными и продуманными?

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

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

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

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

Формальная система учит по-настоящему глубоко отмахиваться от важных решений от меня все равно ничего не зависит

Еще строем ходить и дружно отвечать хором. И этому она учит блестяще. Проведите эксперимент. Задайте 10-20 случайным людям на улице простые вопросы из курса средней школы. Несложные. Например, как посчитать 5% от 80 и т.д. Из дюжины простых вопросов будет не более половины правильных ответов. А потом покажите жест поднятой руки на уроке и спросите, что он означает. 100% ответов будут правильными. Вот вам проверка остаточных знаний. Повиновение главный предмет образования, когда заказчик само государство.

И наоборот. Если родители начали учить, а затем и сам подросток научился думать своей головой, будут ли они правильно расставлять приоритеты в жизни? Куда более вероятно. Проверочный эксперимент. Посмотрите на 10 людей, чью жизнь вы считаете успешной, и проанализируйте, как эти люди занимались собственным образованием. И речь, безусловно, не просто про вузы или крутые школы. А именно про то, плыли ли они со всеми по образовательной реке или гребли самостоятельно.

Поэтому повторю тезис: или планы есть у вас, или свой план выполнит государство. Альтернативы нет.

Опять же, пример прошлого года

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

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

Но. Локус ответственности. Все дело именно в нем.

Давайте возьмем триаду: учитель, родитель, ученик. И мысленно, для эксперимента, установим точку ответственности за образование на самого ученика. То есть ученик сам хочет учиться, понимает чему и зачем (в нашем эксперименте, конечно). Какой силы будет конфликт тогда? Ми-ни-маль-ной. Когда ответственность на самом ученике, конфликта нет.

Передвинем ответственность на учителя. Что имеем? Конфликт. Сразу начинает собираться комок взаимных претензий и обвинений, кто кому что должен.

Возьмем в триаду дошкольника вместо ученика, поставим (мысленно, мысленно) ответственность на родителя конфликта нет. Вернем на воспитателя в детсаду получаем взаимные претензии.

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

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

Изменить это может только одно: когда родители начнут осознавать ответственность за образование детей, а дети собственную ответственность за свое будущее.

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

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

Подробнее..

Итоги года в PHP мире

04.01.2021 10:13:21 | Автор: admin

К концу 2019-го встречи разных PHP-тусовок проходили чуть ли не каждую неделю, появились новые подкасты и каналы, а статьи и доклады лились рекой. Весна 2020-го не задалась - могло показаться, что все заглохнет, но сообщество быстро перестроилось. Стримы заменили митапы, люди не бросили готовить контент и, конечно, важные релизы.

Спасибо Алисе Мартыновой за картинкуСпасибо Алисе Мартыновой за картинку

Мы с @pronskiy из PHP-дайджеста решили спросить вас - а что из этого всего запомнилось? И запустили небольшой анонимный опрос. Здесь и сейчас собираем ваши мнения, которые агрегируем и опубликуем в феврале.

Пройти опрос

Методология исследования. Сварщики мы ненастоящие. Идея родилась в небольшом чате, где организаторы митапов и ведущие каналов о PHP делятся задумками новых активностей. Никто из нас - не профессиональный социолог. Зато мы честные) Созвонились, набросали вопросы (часть отсеяли), прикинули каналы распространения - чаты о PHP, Хабр, таргет в соцсетях и так далее. И пошли делать.

Но небольшой бэкграунд в исследованиях у нас есть

Нам помогает @alyssashch, которая второй год исследует ИТ-ландшафт Ростова-на-Дону - посмотрите, как круто у нее получается.

Что и зачем исследуем

Мы сформулировали 14 вопросов, часть из них - необязательные. Все вопросы делятся на два типа: с готовым выбором (предусмотрена опция добавить свой вариант) и открытые - без подсказок, со свободным вводом.

Среди открытых вопросов:

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

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

  • Статья, которая запомнилась в этом году - по аналогии с докладами.

  • Также есть открытый вопрос про ИТ-компании с хорошей экспертизой в PHP - это и подсказки для поиска докладчиков на будущие митапы и стримы, и возможность посмотреть вхождение их докладов и статей в опросник.

Среди вопросов с готовыми ответами:

  • Каналы в телеграме и на ютубе (брали самые активные из этого списка), а также подкасты, регулярно выпускавшие контент - запомнились ли и чем.

  • Яркие события, такие как релизы и популярные стримы - что зацепило и стоило ли ожиданий.

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

Что будет с данными

Мы собираем ответы до середины января. Затем нормализуем и сведем в красивые графики, возможно, даже кликабельные - и опубликуем на phpcommunity.ru, Хабре и телеграм-канале PHP-дайджеста. Вероятнее всего, сырые данные (за исключением email-адресов) будут также доступны в виде архива - можно будет забрать его и строить свои срезы.

Опрос анонимен, но мы собираем email - его можно отдать по желанию, чтобы выиграть официального PHP-слона. Тут все просто. Слон плюшевый, физический, вручить его надо кому-то физически. Чтобы как-то связаться с человеком, нужен его контакт.

Итоговый слоник может отличаться цветом.Итоговый слоник может отличаться цветом.

Обладателя выберем при подведении итогов опроса. Возможно, призов будет больше.

Подробнее..

Трансляция с казанского QA-митапа чек-лист код-ревью для автотестов, data QA и не только

19.05.2021 10:20:52 | Автор: admin

Привет! В этот четверг казанский QA-чат проводит первую за долгое время встречу - а для тех, кто не придет очно или живет в другом городе, мы сделаем интерактивную трансляцию.

Митап и стрим стартуют 20 мая в 18:30 по московскому времени.

Зрители ютуб-трансляции смогут задать вопрос докладчику голосом - для этого достаточно будет браузера и пары наушников. Также мы будет педавать в зал вопросы из чата трансляции.

В программе три доклада.

Типичные ошибки при написании кода автотестов

Андрей Шальнев, QAA в Skyeng, расскажет, как его команда внедрила код-ревью как процесс, которого придерживаются в компании, и даст готовый чек-лист с примерами: что проверять и как поправить.

Data QA test reporting

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

Уволиться нельзя работать. Немного про эмоциональное выгорание

Гульшат Афлетунова, QA Lead в Skyeng, говорит о своем докладе так:

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

p.s. Запись митапа будет доступна по той же ссылке, что и трансляция.

Подробнее..

7 QA-шных грехов, которые помогут или помешают тестировщику (стать тем, кем ты хочешь)

26.05.2021 10:15:20 | Автор: admin
image

Привет, меня зовут Иван, я работаю руководителем горизонтали автоматизаторов в Skyeng. Занимаюсь менеджментом ресурсов автоматизаторов, внедряю процессы, которые упрощают работу ребят, пишу инструменты для команды (слак-бот, всякие интеграции с TMS и др.), менторю начинающих автоматизаторов и, иногда, пишу авто-тесты.


Ручные тестировщики и начинающие автоматизаторы из компании часто спрашивают у меня, как им определиться с дальнейшим развитием. Я выделил 7 проблем, с которыми сталкивался сам, постарался рассказать, как боролся с ними и как можно обратить некоторые из своих слабых сторон на пользу себе и окружающим. Учиться на своих ошибках хорошо, а на чужих еще лучше. Надеюсь, мой рассказ поможет вам пойти вторым путем :)


Эгоизм


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

Я сам начинал с техподдержки. Без эгоизма в тех условиях не выжить. Каждый день на работе я задавал вопросы по личкам и получал очень информативные ответы: погугли, хз, наверное, делай как сам знаешь. Уже тогда я понимал, что что-то идёт не так. Все пеклись за себя и своё время. Помогать новым сотрудникам считалось зазорным: это была обуза, ответы давались без желания объяснить. Работал принцип Колизея: выжил значит можешь работать дальше.


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


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


Но оказалось, что я не так понял концепт команды. Зачем обычно набирают новых людей в команду QA? Чтобы её расшить. Следовательно, от её сплочённости зависят показатели качества всего проекта. Ты отвечаешь не только за себя: поэтому из новичка в QA не пытаются выжать всё, а стараются как можно быстрее погрузить в процессы, максимально менторить, отвечать на вопросы и помогать всеми силами.


Так случилось и со мной. Я был поражён, когда получал ответы на свои вопросы: развёрнутые, с примерами, иногда и созвонами, чтобы показать материал наглядно. И могу честно сказать: это круто чувствовать, когда ты нужен команде.


Мораль главы: не будь эгоистом всегда уделяй время своим коллегам. Ты человек, и они тоже люди. Всем нужно общение и помощь. Помог ты, помогут и тебе. Я всё ещё верю в карму. Быть эгоистом это зазорно. Так что если ты собираешься в QA из Колизея оставь эгоизм, всяк сюда входящий :) А теперь немного о том, как войти в QA.


Нерешительность и страх перемен


Совершить переход с тёплого местечка в неизвестность иногда страшно. Вот как было у меня.

В 2016 году я, будучи студентом 3-го курса технического университета, решил искать работу, которая как-то связана с ИТ. Друг предложил пойти с ним в техподдержку Skyeng. Честно говоря, я не сразу согласился: мне сложно коммуницировать с людьми, обычно боюсь первым начать разговор и чувствую себя неловко при общении с незнакомыми. Но затем рискнул и прошёл собеседование.


Уже через 3-4 месяца я точно понял, что мне нужно расти над собой и пойти во что-то более айтишное. Мой друг тогда перешёл в отдел QA. Общаясь с ним, я открыл для себя совершенно новую область в разработке тестирование ПО! С первого взгляда это было именно то, что мне нужно: я хотел набраться опыта, залезть с головой в программную часть и расти дальше ведь учился я на разработчика.


Но в то же время в отделе поддержки резко начал расти штат сотрудников. Я уже был старшим специалистом, до руководящей должности рукой подать. В итоге я встал перед выбором:


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

Решение мне далось не сразу. Около месяца я взвешивал все за и против. Руководить отделом в 22 и иметь долгосрочную стабильность (мне даже родные говорили: Да зачем тебе этот айти? Вон, начальником, может, будешь!) ну разве не кайф?


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


Сотрудник техподдержки постоянно будет сотрудником техподдержки, как его не назови. Мануальный тестировщик это только начало пути. Человек осваивается в ИТ-среде, понимает возможные точки роста: хард-скиллы ручного тестирования, автоматизация тестирования, проектный менеджмент, менеджмент QA, аналитика, разработка и многое другое. Часто бывает, что зарплата начинающего QA равна зарплате сотрудника техподдержки, но при этом ты работаешь на перспективу и повышаешь свою стоимость, получая опыт на боевых задачах и качая скиллы. Движовое окружение, интересные и сложные задачи, возможности роста в любую сторону это склонило чашу весов.


Я выбрал долгую и туманную перспективу: сначала вкладываешься, потом пожинаешь. Но есть одно но: чтобы добиться результата, нужно постоянно самосовершенствоваться, думать головой, брать ответственность за свои действия и идти на риски, которые не всегда могут окупиться. Страшно. Но я решил, что мечты надо добиваться любыми силами, и начал готовиться к собеседованиям. Специально взял отпуск, чтобы понять и впитать всю возможную литературу и лучшие практики. Но этого оказалось недостаточно Это интересный опыт: быть уверенным в том, что всё знаешь, но тебя никуда не берут.


Непроактивность


Я отношу себя к достаточно проактивным людям: всегда стараюсь быть в курсе всех нововведений и проблем, остаюсь в диалоге с коллегами, первым реагирую на какие-то анонсы. Собственно, благодаря постоянному шилу желанию что-то улучшать и помогать я в итоге и стал руководителем горизонтали автоматизаторов в Skyeng. Но я часто замечал проблему пассивности у других и хочу рассказать, почему не надо быть такими.

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


Именно проактивность помогла мне пробиться в QA. Ведь поначалу казалось, что весь мир против меня: три первых собеседования я завалил.


Субъективно, я был достаточно неплохо подготовлен для джуна, но не имел большого практического опыта. Это плюс пара ошибок по теории привело к тому, что на первом собесе мне сказали: Не быть тебе QA!. Но! Я почти сразу попросил дать мне второй шанс. Следующие 2 недели я не только зубрил почти всё что можно, но и активно практиковался в тестировании полей, оплат, покупок, а также составлял тест-кейсы, чек-листы и тому подобное.


Но! Придя второй раз, я столкнулся далеко не с джуниорскими вопросами: спрашивали про пентестинг и всё такое. Кажется, со мной не захотели возиться. Но! Я написал другому тимлиду тестирования и сразу договорился о третьем собеседовании.


Мы просто по-человечески общались: я ответил на все вопросы, кроме одного, и был точно уверен, что меня возьмут! А в итоге Прости, но взяли человека с опытом. Если до этого момента я не понимал, что такое мораль ниже плинтуса, то вот тогда вкусил это в полной мере.


Моя проактивность резко упала. Я даже почти перегорел. Однако Через месяц я попросил о четвёртом собеседовании. И в этот раз решил пойти на отчаянный шаг попросился работать в QA на парт-тайме, хотя бы бесплатно. И ребята согласились. Теперь моей задачей было совмещать работу в техподдержке (8 часов в день) и параллельно работать Trainee QA на подхвате (4 часа в день). Я начал писать тест-кейсы, майндмэпы, тестировать что-то простое вроде правок в вёрстке, проходить тест-ран из 4000 кейсов. Сказать, что я был счастлив, ничего не сказать.


Приходилось потеть по 12 часов в день 5 (а иногда и 6) дней в неделю на протяжении месяца, но это того стоило. Когда испытательный срок длиною в месяц подошёл к концу, меня взяли в QA! Я даже был удивлён, потому что успел продолбать один критикал, но ребята сказали, что я не знал продукт, и поэтому они дали мне шанс всё исправить.


QA для меня было неизведанной областью, глаз не успел замылиться и я тут же увидел колоссальный фронт для нововведений. Естественно, процентов 90 моих предложений выкидывали в мусорку. Но хотя бы аргументированно :) Именно конструктив добавлял ещё больше сил генерировать идеи, к которым в итоге нельзя было подкопаться.


Из-за моего рвения постоянно улучшать процессы меня перевели в другой отдел, я стал тимлидом команды тестирования. Около года я лидил команду ручного тестирования, но параллельно сам проявил инициативу и совершенствовался в автоматизации, в чём мне очень помогал коллега Андрей AndrewQA777 Шальнев. Ну а год спустя я всё-таки решил идти в хард, поэтому перешёл на фуллтайм автоматизацию. Нетрудно догадаться, что в автоматизации мне тоже сиделось крайне неспокойно: в свободное время я начал обучать других ребят автотестам, ревьюить код коллег, советовался с разработчиками, как лучше что-то написать. В какой-то момент оказалось, что я по факту руководил уже 5 проектами в автоматизации. Я вырос из суетолога в лида, который может аргументированно проталкивать хорошие процессы и внедрять их. Конечно, не всё сразу получалось, т.к. в начале были проблемы с расфокусом на задачах, но я научился планировать и приоритизировать их. После этого лиды всей гильдии тестирования пришли к выводу, что пора отдать мне все проекты автоматизации, чтобы улучшения происходили повсеместно.


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


Недооценка себя


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

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


Полгода я усердно работал, изучал что-то новое, уже мог довести какие-то проектные задачи до логического завершения. Потихоньку вставал вопрос о зарплате, потому что я выполнял задачи быстрее, чем раньше, брал более сложные кейсы, уже немного менторил (обучал сотрудника автоматизации тестирования). Мы договорились с моим лидом, что составим мне PDP (план личного развития) на 3 месяца и после его выполнения обсудим зарплату.


PDP заключался в доработке фреймворка автотестов (внедрение API-тестов в код), адаптации автотестов под тестовые окружения и расширении покрытия проекта автотестами. Я предоставил подробный отчёт, что покрыл, как это сделал, где добавил стабильности. Но после очередного 1-на-1 с тимлидом я узнал неприятную новость для всего проекта: нам жестко урезают бюджет, полностью останавливают найм новых сотрудников и, естественно, на повышение зарплаты средств нет и подавно. Я расстроился, но решил попробовать снова договориться о новом PDP, тоже сроком на 3 месяца. Все пункты я сделал ещё до конца срока PDP, но в итоге снова узнал, что бюджета на повышение ЗП не было.


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


На новом месте я сразу сказал, что для меня важны обязательства не только со своей стороны, но и со стороны проекта, в котором я работаю. И на меня сразу заложили бюджет на повышение ЗП на год. Я знал, что, выполнив поставленные задачи, получу то, что заслужил. Так и случилось. Happy end :)


Мораль этой истории следующая: надо любить то, что ты делаешь, но и не забывать любить себя.


Неумение превращать свои слабые стороны в сильные


Лень закаляет характер, если вспомнить, сколько требуется усилий, чтобы её побороть. (с) Тристан Бернар.

Сам по себе я очень ленивый человек. (с) я

К работе я относился и отношусь ответственно, потому что понимаю, какие могу создать риски, если сделаю что-то с опозданием. Например, если поленюсь протестировать объёмную и важную задачу, то рискую выкатить поломанный функционал на прод и сломать критический сценарий пользователя. Если я вовремя не покрою критический путь пользователя на проекте автотестами, то при выкатке задачи может поломаться смежный важный функционал. Всё это потеря прибыли для компании.


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


По соседству был отдел автоматизации тестирования. Я был знаком оттуда с Андреем AndrewQA777 Шальневым ещё со времен работы в техподдержке, у нас сразу сложились хорошие отношения. Я стал интересоваться, как работает автоматизация, и, немного поговорив с ним, понял, что это:


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

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


Именно не желая день за днём щёлкать однотипные задачки, я закопался в многочисленные доклады, статьи и литературу (начиная с общей теории у Куликова и заканчивая описанием технической документации Jasmine, Protractor, WDIO, Selenium и др.) по теме автотестов. Так и началась моя карьера автоматизатора.


Я превращал свой негатив, свою лень в программный код, который делал за меня всё: тестировал различные проекты, автоматизировал процессы обработки фэйлов на проде, поступления задач в бэклог и многое другое. И так до сих пор. Лень одно из самых отвратительных качеств, которое обычно мешает развиваться и вам, и другим, но её можно приручить. Главное, найти ту область, где энтузиазм будет подпитываться вашей ленью!


Зависть


Я уже не раз упоминал про своего друга. Настало время познакомиться с ним поближе. Его зовут Туан. Сейчас он возглавляет ключевую команду QA-core в компании. И с самого начала я ему завидовал конечно же, в хорошем смысле. В том плане, что равнялся на него.

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


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


Потом Туан стал руководителем нескольких проектов QA. Желание равняться на этого человека становилось всё больше. Я тоже хотел быть крутым руководителем! Тогда я уже плавно перешёл в фуллтайм автоматизацию, и в смежном проекте открылась вакансия руководителя отдела тестирования. Я сразу понял, что это мой шанс подняться ещё выше и догнать своего друга в карьерном развитии.


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


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


Неудовлетворенность собой


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

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


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


Выгорание


Для меня выгорание это полное моральное истощение, когда тебе не хочется ни работать, ни отдыхать. Не хочется ничего, только уйти подальше. Я уверен, что каждый испытывал подобные эмоции на работе.


Я сталкиваюсь с этим стабильно раз в 2-3 месяца. В основном из-за постоянного расфокуса. Моя проактивнось, постоянное желание сделать какой-то улучшайзинг текущих процессов приводят к тому, что я беру на себя кучу новых задач и бывает, что часть задач я не добиваю до конца. Просто потому, что резко может прийти более интересная задача, и моё внимание переключается уже на неё. А как только я закончу делать более интересную задачу, то уже через силу возвращаюсь к прошлой и могу потратить на её выполнение в 2-3 раза больше усилий. И бывает, на этом пути я понимаю, что всё И это чувство может не покидать меня неделями.


Как бороться с этим? Честно, я бы с радостью ответил, но еще не нашёл действенного решения для себя. Пока мне помогают следующие моменты:


  • почаще брать отпуск (на неделю раз в 2-3 месяца);
  • чаще менять локацию, из которой работаешь (на удалёнке это сделать проще);
  • строить диаграмму Ганта для своих задач (крутая практика, помогает чётко распланировать задачи на определённый срок);
  • планировать активности в календаре (например, с 10:00 до 11:00 я пишу код, с 11:00 до 12:00 на созвоне, с 12:00 до 14:00 ресёрчу новый инструмент и т.д.).

Основная суть этих практик не сидеть на одном месте и отсеивать входящие запросы для достижения максимального фокуса на текущих задачах.


Отсутствие мотивации


Если побороть выгорание можешь ты сам, то с мотивацией чуть-чуть сложнее, потому что это не всегда зависит от тебя. Чаще всего с отсутствием мотивации сталкиваются в достаточно однообразных и (или) стрессовых позициях. Для себя я вывел следующее правило: работай, пока тебе нравится то, что ты делаешь. Если не нравится, то или меняй всё вокруг под себя, или ищи себе новый проект. Что я и делал достаточно часто в рамках своей компании.


Отсутствие поддержки


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


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


Как итог, могу только посоветовать побольше интересоваться своими коллегами: если ты не сделаешь первый шаг, вряд ли кто-то другой сделает его навстречу тебе. И, конечно, прыгать через себя нужно в меру, чтобы не выгореть и не быть апатичным долгий срок :)

Подробнее..

Как я помогал делать хакатон в Красной Поляне

23.12.2020 10:20:51 | Автор: admin
В начале марта я взял мотоцикл, вещи и приехал в Поляну на постоянную удаленку.



У мотоциклистов своё сообщество люди легко идут на контакт: подъехал, пообщался, а там заходишь в кафе, видишь знакомые лица, подсел, еще поговорили. Один из новых знакомых оказался гидом так что большую часть весны мы гоняли по окрестностям. В общем, карантин прошел мягко) Летом я уехал на Алтай тоже путешествовать. А когда вернулся

Здесь зародилось ИТ-сообщество


В конце августа в общей группе жителей Поляны появилось предложение: Кто тоже из ИТ: давайте соберемся где-нибудь, пообщаемся. Тут же выделился отдельный телеграм-чатик Поляна.tech.

Уже через пару дней ребята провели первую встречу, я не попал, но подумал: Круто, надо дойти до следующей.


3 сентября: пока вся страна переворачивала календарь, в Поляне делали митап в шатре.

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


За фото спасибо Сергею из ИТ-чата Сочи.

Вернувшись назад, пошли в бар, разговорились. И я вписался в помощь со следующими митапами: немного занялся поиском докладчиков, сам решил сделать доклад. Формат был прост: находим любое место, каждый желающий получает слот (около 20 минут) для рассказа, можно выступать без слайдов. Вскоре докладов набиралось на встречи почти каждый четверг мы миксовали бизнесовые и технические темы.

Комьюнити стало расти 100, 200, 300 участников в чате. Параллельно с основным чатом айтишников Красной Поляны появился чат организаторов в него добавляли всех, кто хотел помогать с текущими или делать новые форматы. На митапы стало собираться по 50 человек. В ноябре стали проводить часть встреч в недавно открывшемся коворкинге.


Плюс Поляны сюда заглядывают многие крутые ребята: остается пригласить их выступить на митапе.

А в декабре мы научились делать стримы со встреч и завели ютуб-канал.


22:30, мы после очередного митапа решаем: А давайте замутим доставку дроном!


Тыжпродакт девиз, с которым я живу после того, как перестал быть тыжпрограммистом. Пару лет как работаю technical product manager в Skyeng, занимаюсь биллингом (вот отличный пост моей команды), а за спиной больше 10 лет разработки веб-сервисов. Это помогает смотреть на проблемы с разных сторон.

Красная Поляна сейчас такая ИТ-деревня. Тут много айтишников, но мало сервисов: нет доставки еды, подбора клинеров, всего, к чему я привык в Москве. Спрос есть, но обычные большие классификаторы его не закрывают.

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


В теме хакатонов я давно: 2017-й, моя команда выигрывает 100к на хакатоне, который кто-то организовал.

Когда вышли из бара проветриться, смотрим коворкинг открыт. Заходим, говорим хотим у вас хакатон провести. Они говорят мы за. Начали думать, когда все проводить: в ноябре не успеем, в конце декабря против хакатона выступят все дедлайны мира, В январе людям тоже не до работы на выходных. Остался один вариант 12-13 декабря. Так и выбрали даты.

Фантазировали до часа ночи: в процессе упростили тему любые сервисы для жителей и гостей Поляны.


Подумали, что все проекты в итоге стоит собрать в суперапп. Место небольшое, аудитории тут немного. Не надо быть стратегом, чтобы предположить: с приходом крупных игроков отдельное локальное приложение про отдельную услугу едва ли выживет. А вот если приложение будет решать несколько болей, у него есть шанс.

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


Установочная встреча Дмитрий Перчило (сооснователь коворкинга Steford), Евгений Маталыга (основатель RiderHelp, житель Поляны с многолетним стажем), я и Евгений Шваров (директор по развитию экосистемы разработчиков InterSystems). Да, вы верно поняли, у меня есть специальная толстовка для хакатонов.

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

Оставалось полторы недели пора начинать анонсы. Я взял типовой шаблон конструктора, домен коворкинга, завел простую анкету для регистрации. Мы кинули анонс в чат и в первый день у нас было 4 заявки.


Тогда мы начали рекламную кампанию все продвижение уложилось в 5 тысяч рублей. Перед вами пример рекламного поста.

Каждый день придумывали что-то новое:

  • положили полторы тысячи в рекламный кабинет инстаграма и начали промо от аккаунта коворкинга,
  • купили пост в инстаграме Это Сочи, детка, написали в сочинское медиа SCAPP,
  • договорились, что RiderHelp тоже сделает анонсы.


Сайт целиком можно посмотреть тут.

С каждым днем число заявок увеличивалось. Нам повезло, что хакатон решили посетить несколько сложившихся команд из Адлера и Краснодара. Еще одна команда прилетела из Челябинска: оказалось, ребята давно планировали тимбилдинг и хотели на юг, а тут такая удача.


Как прошел сам хакатон в 3 фактах


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


Тот момент, когда фраза Улыбнитесь, вас снимают теряет смысл.

Участники. Мы собрали 57 заявок. До хакатона дошли 35 человек. Среди проектов:

  • виртуальный ассистент с NLP (natural language processing), который бы подсказывал, что поделать в Поляне,
  • сервис для безопасной аренды жилья, это очень актуальная проблема в сезон,
  • ЧемЗаняться сервис для поиска, с кем бы сходить поиграть в настолки и не только. Его уже отправили в бету App Store, можно потестировать!
  • сервис анализа очередей на подъемники с машинным обучением,
  • каталог услуг для активного спорта.


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

image

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

Менторы и команда организаторов. Тут нам очень повезло. Надо придумать критерии оценки проектов сели, за полчаса набросали. Надо разложить печеньки? Убрать коробки от пиццы? Каждый был готов включиться и помочь.

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


2020-й, чья-то команда выиграла 100к на хакатоне, который я соорганизовал. На фото тот самый чек, который срочно отпечатывали в Адлере.

Еще раз хочу поблагодарить всех и каждого. Я был на десятке хакатонов: на многих как участник, еще на трех ментором. Одни из них делали крупные корпорации, другие крупные сообщества. И могу честно сказать, что у нас вышло не хуже.

Надеюсь, 2021 пройдет для местного сообщества еще более ярко.

p.s. Будете в Поляне заходите на митап: вот канал с их анонсами.
Подробнее..

Мы посадили за телефон робота вместо человека и чуть все не сломали

06.11.2020 10:14:52 | Автор: admin

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

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

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

Казалось, настроить робота плевое дело

У нас в Skyeng есть бесплатные вводные занятия. Люди на них записываются, но мы знаем, что часть из них передумает. Поэтому, например, на 10 преподавателей в слоте записываем 13 учеников. Преподаватель ищет себе ученика ближе к уроку и обзванивает записавшихся получить у них подтверждение. Каждый раз нужно обзвонить несколько контактов, подсказанных системой.

Это съедало время, которое преподаватель мог потратить на урок или на отдых. И мы решили пусть звонит робот!

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

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

Первого робота звали Антон

На мой вкус у него был самый противный голос из стандартных: мужской-скрипучий-роботизированный.

Этим голосом он напоминал день и время, когда состоится урок, спрашивал, готов ли студент к занятию, и принимал два ответа: да или нет, а потом направлял их в соответствующие ветки разговора.

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

В скрипучем голосе робота Антона, естественно, было мало приятного (я же говорила). Мы подумали: сейчас сделаем голос красивее, люди будут слушать робота до конца и конверсия заживет.

Неприятного Антона заменила Влада.

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

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

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

Оказалось, робот не должен быть слишком человечным

Тогда мы взяли третий вариант робот Захар, не такой противный как Антон, но и не такой живой как Влада. Захар просто норм. Не так раздражает, но и не вводит в заблуждение.

Плюс, после опыта с Владой мы расширили Захару словарь. К примеру, мы научили его распознавать популярные ответы КОНЕЧНО и НЕ. Но это привело к новым проблемам. Например, слово КОНЕЧНО робот отправлял в ветку НЕТ. Потому что часто при произношении в слове коНЕчно четко слышился слог НЕ. С распознаванием ничего не сделаешь. Словарь не помог.

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

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

Тестирование гипотез начиналось с 5-10% пользователей и могло доходить до 50% перед принятием решения о сохранении такого изменения.Тестирование гипотез начиналось с 5-10% пользователей и могло доходить до 50% перед принятием решения о сохранении такого изменения.

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

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

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

Главное, что мы поняли: назвался роботом упрощай

Самые лучшие результаты получились, когда мы стали подсказывать людям, как отвечать. Если вы придете на вводный урок, скажите "да". Америку мы не открыли, так делают многие роботы. Человек слышал четкий инструктаж, как ответить, но это тоже не панацея.

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

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

А может ну их, эти звонки?

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

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

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

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

Но пока звонки все еще часть мира, их надо автоматизировать так, чтобы всем было удобно и хорошо. Через тернии и скрипучие голоса.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru