Блог Александра Денисюка

PHP-программист

Вконтакте Твиттер Хабрахабр

Любые предложения или замечания принимаю на
почту a@denisyuk.by или на номер +375 (29) 203-79-48.

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

Оригинальный пост называется Кодинг больше не гарантия бутерброда с икрой, который написала в своём Фейсбуке Алёна Владимирская, экс-хантер в «Mail.Ru Group» со стажем 12 лет. Пост был бы актуален 20 лет назад, актуален сегодня, будет актуален в будущем, не только программистам, но и любой профессии в IT. Решил стянуть его в свой блог с небольшими правками, как напоминание о грядущих изменениях.


Я старая рекрутинговая черепаха. Я помню, что ещё 12 лет назад просто знание английского языка было абсолютной гарантией хорошей работы в международной компании. Знаешь хорошо английский? Приходи на приличную зарплату! Остальному доучим. Сейчас знание английского — необходимый санитарный минимум в некоторых отраслях и компаниях (все международные компании, ИТ, телеком, консалт, качественная реклама и пр.), никаким образом трудоустройство не гарантирующий.

То же самое сейчас происходит и с кодингом:

1) Если вы только начинаете кодить, вам блестящую карьеру в кодинге сделать всё сложнее. Ваши начальники — руководители отделов разработки — люди с опытом кодинга и опытом решения сложных задач. Они имеют уже лет 10 кодинга и при этом их возраст 32—35 лет. И развиваться на своих позициях (меняя компании, но растя как руководители разработки) они будут еще лет 30. Поэтому в крупнейших и самых желанных интернет-компаниях для кодера стеклянный потолок совсем низкий. Да, открываются там новые направления. Но при этом на них претендуют те, у кого уже сейчас 5 лет кодинга. А дальше им тоже расти некуда — более старшие ещё долго не уйдут на пенсии. Поэтому до вас очередь не дойдет или дойдет не скоро.

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

3) Объем выпускаемых говнокодеров (извините, ну термин такой в отрасли) всяким школами программирования таков, что нет потребности в джуниорах — на самом деле, рынок нулевыми плохими кодерами переполнен. Есть потребность в качественных кодерах с 3—5 летним стажем и с опытом работы в хорошем проекте. А чтобы его получить, нужно в такой проект попасть. Куда из левой школы «учим кодить» не берут.

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

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

Всё больше востребованы продакты и проджекты с опытом кодинга в прошлом. Портрет таков: вы программист, который понимает бизнес и специализируется на каком-то одном направлении бизнеса, знаете два востребованных языка программирования, понимаете экономику проекта. Например, я 3 года кодил рекламную крутилку, я всё понял про её экономику, я стал продактом большой рекламной крутилки. Бинго — я нужен всем!

Что делать:

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

Скорость выполнения call_user_func_array() на разных версиях PHP

Решил сравнить скорость выполнения call_user_func_array() с обычным вызовом функций на разных версиях PHP и последним HHVM. Весь код измерения времени я запускал на 3v4l.org и фиксировал результаты в сводные таблицы. За основу взял функцию array_merge() с массивом MIME-типов и 1 млн раз вызывал её в цикле разными способами.

Код выше сравнивает скорость выполнения функции array_merge() с её вызовом через call_user_func_array(). Как видно, в седьмых версиях PHP нет никакой разницы по времени выполнения, а вот HHVM 3.22.0 остался на уровне PHP 5.6.30, хотя немного быстрее:

+--------------------------------------------------+----------+----------+----------+-----------+ | |PHP 5.6.30|PHP 7.0.24|PHP 7.1.10|HHVM 3.22.0| +--------------------------------------------------+----------+----------+----------+-----------+ |array_merge(...$args) |0.9381 |0.2910 |0.2945 |0.7795 | |call_user_func_array('array_merge', $args) |1.4194 |0.3073 |0.2938 |1.0372 | +--------------------------------------------------+----------+----------+----------+-----------+

В PHP 7.*.* провели хорошую оптимизацию и при вызове функций можно не задумываться об производительности call_user_func_array(). Это касается и вызова самописных функций и методов вызываемых из класса.

Замеры скорости получились следующие:

+--------------------------------------------------+----------+----------+----------+-----------+ | |PHP 5.6.30|PHP 7.0.24|PHP 7.1.10|HHVM 3.22.0| +--------------------------------------------------+----------+----------+----------+-----------+ |fn($args) |1.0934 |0.6369 |0.4660 |1.2059 | |$fn($args) |1.4507 |0.5399 |0.6090 |1.2501 | |$class->fn($args) |1.5088 |0.5622 |0.5282 |1.4568 | |call_user_func_array('fn', [$args]) |1.5022 |0.4198 |0.3750 |1.2159 | |call_user_func_array([new A, '__invoke'], [$args])|1.9369 |0.5629 |0.6538 |1.3701 | |call_user_func_array([new B, 'fn'], [$args]) |2.1243 |0.7088 |0.6625 |1.9792 | +--------------------------------------------------+----------+----------+----------+-----------+

В PHP 5.6.30 и HHVM 3.22.0 видно большую разницу при разных способах вызова функций, а в PHP 7.* как бы вы функцию не вызывали, её время выполнения будет примерно на одном уровне. Проще говоря, в последних версиях PHP вызов функций через call_user_func_array() и её исходных вариантах не имеет разницы.

10 октября   benchmark   call_user_func_array()   php

Ликвидность знаний

Недавно задался вопросом: что такое ликвидность знаний? Могут ли знания быть ликвидными и если могут, то о какой ликвидности идёт речь? Сразу сходу я сгенерировал следующие варианты определения термина «ликвидность знаний»:

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

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

Новое определение мне далось не сразу.

Ликвидность знаний — это не про окупаемость, а умение извлечь из себя именно те знания, которые могут быть применимы для решения поставленной задачи в нужный момент времени. Человек может извлечь совершенно не те знания, которые нужны для решения поставленной задачи, т. е. ошибиться, и таким образом, пойти по ложному пути, увеличивая время решения, тратя ресурсы впустую, или, самое худшее, перешагнуть точку невозврата. Знания здесь выступают встроенным «решебником» для человека, и чем больше знаний, тем выше ваша ликвидность — возможность решения всяческих проблем, конечно.

Вы смотрели фильм Марсианин с Меттом Деймоном? Оказавшись на другой планете в одиночку, да ещё без интернета, астронавт Марк Уотни смог с помощью своих знаний противостоять сверхсложным проблемам, к которым никто не готов даже на Земле и с интернетом. Он налету синтезирует новые знания и сразу применяет их, чтобы решить свою проблему —
выжить. Посмотрите этот фильм, чтобы понять о чём я тут говорю.

Обработчики запросов вместо контроллеров

Вашему внимание представляю перевод статьи Goodbye controllers, hello request handlers от Jens Segers, который опубликовал её в своём блоге. Если переводить буквально, то статья в русском переводе должна называться «Прощайте контроллеры, да здравствуют обработчики запросов», но я решил назвать её проще — «Обработчики запросов вместо контроллеров». Вся статья сводится к тому, что все действия контроллеров нужно выносить в отдельные исполняемые классы. Как, зачем и почему читайте ниже.


За последние годы в ландшафте PHP многое изменилось. Мы стали использовать больше паттернов программирования и таких принципов, как DRY и SOLID. Но почему мы всё ещё используем контроллеры?

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

Позвольте представить вам обработчики запросов (request handlers). Концепция очень проста, но малоизвестна среди PHP-разработчиков. Обработчик запроса является контроллером, но ограничен одним действием. Эта концепция очень похожа на шаблон Action-Domain-Responder, предложенная Paul M. Jones, альтернативой шаблону MVC, которая фокусируется на более «чистых» запросах и ответах (request-response) для веб-приложений.

Модель Action-Domain-Responder (ADR), которая является объектно ориентированной надстройкой над MVC. Оперирует понятиями близкими к доменной области приложения.

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

<?php

class Greeting
{
    public function __invoke($name)
    {
        echo 'Hello ' . $name;
    }
}

$welcome = new Greeting();

// Hello John Doe
$welcome('John Doe');

На данный момент вы, скорее всего, спрашиваете себя: «Зачем мне это нужно?». Это имеет смысл при ожидании маршрутизатором callback-функций или конкретных зависимостей. Проще говоря, не нужно парсить строку и разбивать её на контроллер и метод. Вы можете сразу указать свою зависимость из http-слоя. Посмотрите наглядный пример, как обработчики запросов уже сейчас можно регистрировать в Laravel или Slim:

<?php

Route::get('/{name}', Greeting::class);

А теперь сравните это с тем, что обычно происходит в ваших проектах:

<?php

Route::get('/{name}', 'SomeController@greeting');

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

Принцип единственной ответственности

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

Ниже приведен пример двух обработчиков запросов, которые вынесены из двух методов, вшитых в UserController (редактирование и обновление данных пользователя):

<?php

class EditUserHandler
{
    public function __construct(
        UserRepository $repository,
        Twig $twig
    ) {
        /* ... */
    }

    public function __invoke(Request $request, Response $response)
    {
        /* ... */
    }
}

class UpdateUserHandler
{
    public function __construct(
        UserRepository $repository,
        UpdateUserValidator $validator,
        ImageManager $resizer,
        Filesystem $storage
    ) {
        /* ... */
    }

    public function __invoke(Request $request, Response $response)
    {
        /* ... */
    }
}

Это подводит нас к следующему преимуществу.

Тестируемость

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

Недавно Jeffrey Way в своём Твиттере дал совет по тестированию: сделайте свои классы тестов как можно более конкретными, а затем используйте каждый тест для описания важного правила/способности (rule/ability).

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

Рефакторинг

В современных IDE есть мощные варианты рефакторинга. Но если вы привязываете методы контроллеров к маршрутам в конфигурационных файлах (например, Laravel или Slim), то вы сталкнётесь с проблемой замены старых вызовов (строчных) на название объектов. Ничего не поделать. Вот так должно выглядеть:

<?php

Route::get('/{name}', Greeting::class);

И это намного проще, чем такой вариант:

<?php

Route::get('/{name}', 'SomeController@greeting');

Вывод

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

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

Ссылки по теме
1) Porto (Software Architectural Pattern)
   https://github.com/Mahmoudz/Porto

2) Action-Domain-Responder
   https://github.com/pmjones/adr

3) Request-Driven Development
   http://martinbean.co.uk/blog/2017/01/05/request-driven-development/

4) Classes vs. Namespaces
   http://antonkril.github.io/classes-vs-namespaces
3 октября   ADR   mvc   php   SOLID   SRP   паттерны   перевод

Специальная заметка для тех, кто задаёт вопросы

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

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

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

а отдохните 45 минут от компьютера;
б сформулируйте простой поисковой запрос;
в поищите примеры в документации;
г посмотрите в StackOverflow на английском;
д забудьте про копипаст и начинайте думать.
Ctrl + ↓ Ранее