dd

Рубрика: Разработка

  • От Form к DataHandler: рефакторинг с помощью реддита

    У нас новый бэкендер появился. Первым же его проектом стало создание апи для сайта. Ничего сложного, просто надо выдать весь контент, который в админке заполняется. А так как у меня уже за последние месяцы чуть не свой мета-фреймворк для Yii2 нарисовался, то было принято решение его и использовать, ввиду удобства.
    Среди всего прочего мне пришлось ему объяснять что такое Form и Result и для чего их используют. И если с Result всё понятно, то что такое Form? Это как-то связано с html-формой? Если нет, то почему так называется? Почему функционал никак не связано с сабмитами и прочим? Пока я ему всё это объяснял меня осенило — я говнокодер. Я намешал всё в одну кучу и в следствии этого назвал как попало. Если кратко, то класс Form — это абстрактный класс, который проводит валидацию данных и потом передаёт управление дочернему классу, при этом дочерний класс ещё должен определить правила валидации (те, что были в одном из предыдущих постов). Херня, если честно.
    По интересному совпадению я на днях смотрел девлог разработчика инди-игр. Он рассказывал про создание своей системы частиц и как он к ней прикрутил Entity Component System. Тут пазл сошёлся — зачем я в самом классе Form провожу валидацию, когда это всё можно делегировать? Принялся за работу, переименовал класс Form в DataHandler, чем он по сути и являлся всегда, добавил новый тип атрибутов — DataTransformer. Теперь, на дочерний класс достаточно навесить атрибут Validated, которому при вызове метода DataHandler::run() будут переданы данные для валидации.
    Таким образом, мы получаем легко расширяемый класс с гибким функционалом, который можно гнуть как нам надо в любой ситуации.
    Теперь не надо называть дочерние классы ReplacementCreationForm, для этого сейчас лучше всего подходит императивная форма — CreateReplacement, или вместо PermissionListCollector теперь будет ListPermissions. Красота.
    Ко всему этому я пришёл не только благодаря новому бэкендеру. Он этот процесс запустил, но больше всего работы проделали ребята с реддита. Я там задал вопрос — как мне лучше назвать этот класс, который сейчас зовётся Form? Некоторые начали выяснять что он конкретно делает, некоторые начали говорить, что я оскорбляю богов SOLIDа. Всё это в итоге заставило мой мозг крутить шестерёнки и думать — а чем же и правда занимается этот класс? Почему он так спроектирован? Один из комментаторов открыл мне глаза — это «шаблон» ApplicationService (Action в Laravel), который делает ровно то, что мне нужно.

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

    P.S. ссылка на пост на реддите, где можно почитать все обсуждения — https://www.reddit.com/r/PHPhelp/comments/ys3x91/help_with_class_naming/?utm_source=share&utm_medium=ios_app&utm_name=iossmf

  • http.cat для HTTP-кодов

    Лучший способ запомнить значение всех http-кодов: https://http.cat/

  • Продолжение истории с подработкой

    В продолжение к прошлому посту🥴

  • Fullstack-подработка за выходные

    В прошлую пятницу почти в конце дня мне от проектного менеджера пришло предложение запилить небольшое приложение за доп плату, но оно ни жить, ни быть должно быть готово к понедельнику
    Быстро осмотрев прототип было принято решение почти нахаляву получить денег и до конца рабочего дня (+- 3 часа) бэкенд был готов. Наличие готовых инструментов и базы приложения с полноценной поддержкой апи из прошлых постов повышают продуктивность в разы.
    А вот дальше сложнее — надо делать фронт. Наши фронтендеры и так по уши увязли в работе и наравне со мной в выходные работали в поте лица. Пилить фронт на голом html через php — самоубийство. Во-первых, потому что вся подготовленная мной инфраструктура для подобных проектов уже была нацелена на использование по апи, а во-вторых потому что слишком много динамики. До того как стать чисто бэкендером я год работал на Vue. Прекрасный фреймворк. А потом увидел нечто ещё более крутое — Nuxt. Год назад, когда я только переходил в текущую компанию, для меня это было даже как-то обидно — нифига себе какую штуку выстроили, я сам такое же могу и может даже лучше. Да и вообще свой реактивный фреймворк сделаю. И так далее. Работая фулл-стеком я на всё глядел как на испытание, стараясь доказать хз кому, что я могу круче сделать.
    Однако, теперь, работая бэкендером я был счастлив вернуться к Nuxt. Простой как кирпич, удобный, понятный, особенно учитывая, что всё это же самое можно сказать про голый Vue. И вишенкой на торте стал Vuetify. Я представить не могу как бы я без него вообще хоть что-нибудь сделал. Я не особо люблю material ui, я больше по флэт-дизайну, но набор компонентов и их функциональность и простота использования меня просто поразили. В итоге фронт был готов к 03:00 и я довольный ушёл спать. Может стоило посмотреть в сторону Astro, но это в другой раз, с Nuxt я был хотя бы немного знаком.
    Но не всё так просто. В понедельник человека, который должен был всё это проверять не было на работе — дали отпуск. Я должен был наверное, почувствовать что-то неладное, но работы выше крыши и я просто забил. Во вторник оказалось, что срок на этот проект давался до конца следующей недели. Проектный менеджер пожал плечами, «я думал до конца этой недели». Ладно, главное, что за это заплатят и что я получил возможность с огромным удовольствием посидеть снова над fullstack приложением, особенно ночью. Но и это ещё не всё — к концу недели оказать, что надо сделать ещё правок на 10 часов. Приехали.
    Я в который раз от заказчиков слышу чуть ли не клятвы на крови о том, что больше функционала не потребуется, но они каждый раз что-то забывают и вносят правки в самом конце. Каждый раз обжигаюсь, но каждый раз зачем-то верю. Не самый умный поступок, что ещё сказать.
    Вывод? Нет вывода, есть только слова благодарности людям, которые делают сумасшедше упрощённые продукты для таких как я, кому нисколько не хочется разбираться как это всё работает, но надо чтобы как можно скорее работало.
    P.S. Обязательные слова о том, что в реакте такого нет, там всё сложно и вообще они слишком усложнили фреймворк. После этого идёт обязательное горение жёпы от @artemmarenkoff

  • Код как шутка

    Увидел сейчас отличное выражение: «код как шутка — если его приходится объяснять, то это плохой код»
    Отличный принцип, как по мне

  • Осторожно, простыня!

    За последние 2 месяца у начальства компании развился серьёзный интерес к разделению бэка и фронта. Не знаю с чем это конкретно было связано, но надеюсь я в этом сыграл не последнюю роль
    Так как фронтом теперь занимаются отдельные люди, я подумал, а почему бы и в бэке не привнести существенные изменения? например, сменить фреймворк🤡
    Звучит смешно и слишком радикально, однако я так просто от этой идеи отказываться не захотел и попросил дать мне шанс испытать symfony, сделав небольшой проект (реально небольшой — одна апи точка + одна страница для отображения хранимых данных)
    Естественно, я убил на этот проект в 3 раза больше времени, чем если бы делал на yii2, но я для себя много чего нового узнал
    Например, symfony — это очень удобно, это разделение всего и вся на компоненты, это грамотно, но это ОЧЕНЬ медленно
    Я взял 2 демо-проекта, yii2 и symfony, развернул их и начал закидывать запросами через утилиту hey. В итоге оказалось, что yii2 до 5 раз быстрее при запросе обычной странички, которая выводит hello, $_GET['name']. Понятно, что я не могу прийти на собрание и сказать — давайте-ка перейдём на новый фреймворк, он в 5 раз медленнее, но удобство разработки в разы выше. Меня просто засмеют — я пока не привил должного отношения к хорошему коду в компании.
    Но это дало повод задуматься — что я могу стащить из symfony, чтобы получить скорость yii2 и удобство symfony?
    Самое очевидное удобство, которое бросается в глаза в первый час разработки — валидация на атрибутах. При чём не на старющих аннотациях через комментарии, а на нормальных, уже нативных в php атрибутах.
    Подумано — сделано. За полтора часа накидал систему валидации и простецкие валидаторы, типа not null, equals и тд. Написал тест, сравнивающий нативную валидацию yii2 и самописную. Не знаю, чего я ждал, но получил, что моя реализация в 7 раз быстрее нативной. Я не знаю что творится за кулисами валидаторов в yii2 и уже не хочу выяснять.
    На этом однако процесс не остановился — я часто пишу апи, часто пишу консольные команды. Все они должны быть транзакционными от и до, и в лог должно падать сколько время каждый процесс занял. Это огромная куча кода, который постоянно дублируется. Выход? 2 простых атрибута — #[Timed] и #[Transactional]. Вешая их на любой экшен я теперь получаю весь нужный мне функционал, не тратя ни капли сил.
    Следующий шаг логичен как ничто другое — вынести всё это дело в композер-пакеты и использовать их в текущих проектах. Это заняло около часа может, не более, но я по итогу в проектах занимаюсь нужными вещами, а не написанием бойлерплейтов, получаю чистейшие исходники, которые не заляпаны кучей дублирования и читаются как книга. Детский восторг и всё такое.

    Выводы очень простые:
    1. Просите давать вам возможность пробовать что-то совершенно новое в обмен на опыт и обучение коллег. Это может быть фреймворк, может быть язык, может быть новая БД (я смотрю на тебя, SurrealDB), что угодно — важен факт обучения и познания.
    2. Что-то понравилось? Тащите себе в проект, адаптируйте, расширяйте функционал. Это бесценный опыт, который в итоге упростит жизнь вам и вашим коллегам.
    3. Атрибуты — огонь

  • REST, который не совсем REST

    Сегодня я узнал, что то, что все называет rest api на самом деле даже близко не rest; теперь у вас тоже есть инструмент душнилы😃

    https://htmx.org/essays/how-did-rest-come-to-mean-the-opposite-of-rest/

  • Некоторые интересные и полезные вещи

    1. Плагин для PHPStorm, добавляющий много разных проверок по качеству кода: https://plugins.jetbrains.com/plugin/7622-php-inspections-ea-extended-
    На что мне плагин открыл глаза:
    — В функцию in_array нужно передавать третьим параметром true, чтобы сравнение элементов было строгим (===), без приведения типов (==)
    — Анонимные функции/замыкания необходимо всегда объявлять статическими, не делая этого только в крайне необходимых случаях: https://habr.com/ru/post/561550/
    — В юнит-тестах вместо assertTrue($cond1 === $cond2) можно использовать assertSame($cond1, $cond2)
    — Не стоит использовать array_merge внутри циклов. Однако, решения этой проблеме не указано. Я заменил на $output = […$output, …$arrayToMerge]; Подробнее: https://chemaclass.medium.com/never-use-array-merge-in-a-loop-74917b056ff9
    — Модификаторы abstract/final идут перед модификаторами области видимости: https://www.php-fig.org/psr/psr-12/
    — Приведение типов через (int)/(bool)/etc до 2 раз быстрее, чем аналогичные функции приведения intval/boolval/etc: https://stackoverflow.com/a/35949025/5320740
    — Вместо базового класса Exception лучше кидать RuntimeException
    — Void в качестве типа возврата функции лучше, чем его отсутствие

    2. Была задача скачать и обработать 30 гигабайтный файл с одного государственного ресурса
    Соединение обрывалось примерно на 25-30 процентах завершённости
    Начал думать что делать и решением в лоб оказалось пытаться скачать файл несколько раз, при этом передавая в curl сдвиг
    Пример реализации тут: https://gist.github.com/ndunks/778c7efad0956e0f99deae73c52e718f

    Осталось его только распаковать. В памяти, естественно, это всё не поместится, поэтому я использовал файловые потоки, чтобы по кускам вытаскивать данные из архива в отдельную папку. Пример реализации тут: https://stackoverflow.com/a/8102667/5320740

  • Спор про ORM и DTO

    Недавно на реддите натолкнулся на заминусованный комментарий, в котором автор яростно пишет о том, какая ORM плохая технология
    Мне стало интересно что он может предложить в ответ и почему с ним так много несогласных
    По итогу он скинул ссылки на 2 статьи, которые по-моему заслуживают прочтения:
    1. https://www.yegor256.com/2014/12/01/orm-offensive-anti-pattern.html
    2. https://www.yegor256.com/2016/07/26/active-record.html

    Вся суть статей сводится к тому, что использование объектов-данных (DTO) в связке с ORM — зло и вообще грубое нарушение принципов ООП
    По мнению автора статей объект должен сам знать о подлежащей структуре в хранилище и должен сам им управлять
    И тогда возникает много вопросов: чем же это отличается от ActiveRecord или паттерна Repository? И куда помещать методы с бизнес-логикой? Кто мешает в DTO хранить бизнес-логику?
    Ни на один вопрос я пока ответа в статье или комментариях к статье не нашёл, нашёл только несогласных с постом, например: http://disq.us/p/1v8un8u

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

    P.S. в комментариях на реддите мне подсказали, что этот паттерн называется DataAccessObject, если хотите дальше в эту углубиться

  • Nginx как reverse proxy для нескольких проектов

    На этом же проекте пришлось овладеть навыками "продвинутой" конфигурации нгинкса
    Было принято решение накатить нгинкс в режиме реверс прокси для того чтобы размещать больше одного проекта на сервере
    В каждом проекте не один контейнер и поэтому нгинкс проекта тоже нужно настраивать и в режиме реверс-прокси
    Итого имеем:
    1. Запрос приходит на железный сервер
    2. Нгинкс по server_name определяет в какой проект направить запрос
    3. Контейнер с нгинксом этого проекта определяет в какую часть проекта направить запрос (статика, админка, апи, фронт)
    4. Нужный контейнер делает свою работу и возвращает результат
    Звучит просто (оно собственно и есть просто), но сложным моментом было связать это всё с абсолютно неинтуитивной системой урлов в yii2, когда нужно было в нескольких разных местах и конфигах прописать базовый урл для каждого проекта (/admin для админки, /api для апи, и тд)
    Тут наверное надо бы поднять вопрос поддоменов, потому что это бы сильно всё упростило, но начальство пока против, буду дальше думать над аргументацией🙂