Владислав Тимофеев

Владислав Тимофеев

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

  • 10+ е-ком проектов;
  • помог 4+ стартапам;
  • 3+ страховые компании в качестве разработчика;
  • обучил 50+ мидлов и синьор;
  • 10+ тимлидов и техлидов.
TIMOFEEV Tech Talk
93 участников
25 августа
Ну и финалимся по теме Long Running

6 из 8 Мониторинг

Long Running скрипт, который работает около бесконечно, но как понять действительно ли он работает или просто завис?

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

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

Второй способ -- это использование внешних систем мониторинга типа sentry или new relic.

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

Вопрос, когда писать лог, или метрики для healthcheck. Здесь опять несколько вариантов:

1) самый простой: где-то в цикле , в самом начале, например;

2) специфичный: используя fibers, особенно если в проекте используются reactphp или amphp;

3) надежный, но и самый неоптимальный — register_tick_function

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



7 из 8 Деплой

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

В зависимости от способа оркестрации приложение может как перезапускаться, так и нет.

Опять нужно помнить на этом этапе про Graceful Shutdown.



8 из 8 Лимит дескрипторов ресурсов в 8,6 миллиарда

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

Но существует такой баг.

Дескриптор используется каждый раз, когда открывается какой-то ресурс, сокеты, соединение с БД, открытие файла и т.д. Их доступное кол-во 8,6 миллиардов. Но при закрытии ресурса, дескриптор нельзя переиспользовать.

Более подробно о баге можно почитать здесь https://bugs.php.net/bug.php?id=47396

Баг был обнаружен в PHP 5.2 , остается открытым. Я не проверял, актуален ли он для поддерживаемых версий php но вы можете проверить самостоятельно ради интереса )

Ну, собственно, на этом всё)
🔥
10
👏
4
3
164
15:00
14 августа
5 из 8 Определение $_ENV происходит только при запуске

Чтение из env происходит только при запуске скрипта

Вообще, есть два варианта инициализации env:

1) приложение читает переменные из файл .env - в этом случае можно просто перечитывать файл раз в какой-то период


$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();


2) переменные задаются при запуске контейнера, например, из AWS Secrets Manager, Gitlab Variables, или так же из файл .env через docker compose.

В этом случае простого решения нет. Как правило, всё сводится к тому, что нужно перезапускать контейнер, соответственно и скрипт, поэтому в этом случае также важен Graceful Shutdown.

Для кубера есть решение в виде stakater/Reloader для остального у меня нет решения, но придумать какие-то костыли, оснастки точно можно, все зависит от оркестрации приложения и того, по какой причине понадобилось обновить переменные окружения.
🔥
9
👍
1
🙏
1
184
15:59
8 августа
4 из 8 Обработка исключений и ошибок

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

Решение простое, обернуть процессинг в try-catch


<?php

...

while (shouldStop() === false) {
...

try {
$messageProcessor->process($message);
} catch (Throwable) {
// тут стоит добавить логирование
}

...
}

...


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


<?php

...

set_error_handler(
callback: static function (int $code, string $message, string $filename, int $line) {
throw new ErrorException(
message: $message,
code: $code,
filename: $filename,
line: $line,
);
},
error_levels: E_ERROR | E_PARSE | E_CORE_ERROR | E_USER_ERROR
);

while (shouldStop() === false) {
...

try {
$messageProcessor->process($message);
catch (Throwable) {
// сюда будут попадать и исключения и ошибки
}

...
}

..



Нужно продумать, что именно указать в error_levels и что лучше подходить для вашего случая. Я обычно ставлю E_ALL , но для прода игнорирую E_WARNING, E_DEPRECATED. Такие предупреждения, как правило, не доходят до прода, но если так случилось, что они выстрелили в проде, то пытаемся обработать ситуацию как есть. Возможно для вашего кейс E_WARNING будет критичным, и его стоит обрабатывать как ошибку.
🔥
11
👍
6
2
187
17:43
5 августа
3 из 8 Graceful Shutdown

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

Скрипт может завершиться в ходе исполнения, например, скрипт обрабатывает очередь, получил сообщение, начал что-то писать в БД и завершился по причине того, что пользователь нажал ctrl+c, пришел OOM Killer, идет деплой и все воркеры перезапускаются.

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

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

Чтобы корректно завершить приложение, нужно обработать сигналы SIGTERM, SIGINT, SIGUSR1, SIGUSR2, SIGQUIT, SIGHUP с помощью pcntl_signal.


<?php

$queue = new Queue('topic-exm');
$messageProcessor = new MessageProcessor();
$connection = new DbalConnection();

$pcntlSignal = false;

// определеяем сигналы которые хотим ловить
$signals = [SIGTERM, SIGINT, SIGUSR1, SIGUSR2, SIGQUIT, SIGHUP];

// и добавляем обработчик в виде функции setPcntlSignal
// будем просто запсывать факт того, что интересующий нас сигнал пришел
// и записывать флаг в переменную $pcntlSignal
pcntl_async_signals(true);
foreach ($signals as $signal) {
pcntl_signal($signal, 'setPcntlSignal');
}

while (shouldStop() === false) {


$message = $queue->consume();

if ($message === null) {
continue;
}

checkConnection();

$messageProcessor->process($message);

}

function setPcntlSignal(int $signal): void
{
$pcntlSignal = $signal;
}

function shouldStop(): bool
{
$shouldStop = memoryLimitExceeded();

// если сигнал получен $pcntlSignal === true, то звершаем работу
$shouldStop = $shouldStop && ($pcntlSignal === false);

return $shouldStop;
}

function checkConnection() use ($connection): void
{
...
}

function memoryLimitExceeded(): bool
{
...
}
🔥
12
👍
2
👏
1
176
13:00
2 августа
2 из 8

Утечка памяти

Ну тут всё понятно, могут быть проблемы как с вендорскими пакетами, так и в коде приложения, и в самом php или в его расширениях.

Одна из частых проблем - это использование Doctrine ORM, которая использует механизм UnitOfWork , и копит сущности в памяти. Здесь решение простое - очищать UOW на middleware.

Symfony Messanger делает так:

https://github.com/symfony/doctrine-bridge/blob/7.1/Messenger/DoctrinePingConnectionMiddleware.php#L50

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

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

Это более частные проблемы, которые в целом известны.

Но если говорить более обобщенно, то и решение должно быть общим.

Один из вариантов - выставить безлимитное использоавние памяти


memory_limit = -1


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

Более оптимальное решение этой проблемы - это корректный перезапуск скрипта при достижении потолка по памяти.


<?php

$queue = new Queue('topic-exm');
$messageProcessor = new MessageProcessor();
$connection = new DbalConnection();

$shouldStop = false;

while ($shouldStop === false) {
$shouldStop = memoryLimitExceeded();

$message = $queue->consume();

if ($message === null) {
continue;
}

checkConnection();

$messageProcessor->process($message);
}

function checkConnection() use ($connection): void
{
...
}

function memoryLimitExceeded(): bool
{
$memoryLimit = 256 * 1024 * 1024;
$usedMemory = memory_get_usage(true);

return $usedMemory > $memoryLimit;
}


Так, с помощью memoryLimitExceeded проверим не превышаем ли память , и если превысили, то завершаем работу скрипта.

Как это делаем Symfony Messenger: https://github.com/symfony/messenger/blob/7.1/EventListener/StopWorkerOnMemoryLimitListener.php

Перед запуском можно попробовать принудительно запустить сборщик мусора gc_collect_cycles() как делает это Octane

https://github.com/laravel/octane/blob/2.x/src/Listeners/CollectGarbage.php#L17C13-L17C30
🔥
11
1
👏
1
163
13:06
Простой Long Running скрипт выглядит так:


<?php

while (true) {
// do something
}


Добавим в него консюм из очереди:


<?php

$queue = new Queue('topic-exm');
$messageProcessor = new MessageProcessor();

while (true) {
$message = $queue->consume();

if ($message === null) {
continue;
}

$messageProcessor->process($message);
}


1 из 8

1) Открытые соединения с внешними системами могут закрываться.

Я думаю, что каждый встречал такую ошибку

MySQL server has gone away

Этот пункт часто проявляет себя в работе с БД, обычно решение здесь такое: используется middleware, которое пингует БД , и если есть проблемы с соединением, то переоткрывает его. Проблема точно решена в RoadRunner и Symfony Messenger. Вероятно, что другие либы ориентированы на long running, тоже решают эту проблему.

При работе с Redis тоже открывается постоянное соединение, а вот с ElasticSearch взаимодействие происходит по http, о нем беспокоиться не нужно.

Снизу прикрепил ссылки как реализуют популярные либы реконнект Doctrine DBAL

Symfony Messenger:

https://github.com/symfony/doctrine-bridge/blob/7.1/Messenger/DoctrinePingConnectionMiddleware.php

RoadRunner:

https://github.com/Baldinof/roadrunner-bundle/blob/3.x/src/Integration/Doctrine/DoctrineORMMiddleware.php#L92

Symfony/Bridge , сабскрайбер, который отрабатывает на событие симфы onKernelRequest:

https://github.com/symfony/symfony/blob/5938c51403b567fca54ef758071f54622475f141/src/Symfony/Bridge/Doctrine/Middleware/IdleConnection/Listener.php#L41


Для Doctrine DBAL достаточно просто закрыть протухшее соединение, и при следующем запросе к БД, будет соединение открыто заново


<?php

$queue = new Queue('topic-exm');
$messageProcessor = new MessageProcessor();
$connection = new DbalConnection();

while (true) {
$message = $queue->consume();

if ($message === null) {
continue;
}

checkConnection();

$messageProcessor->process($message);
}

function checkConnection() use ($connection): void
{
try {
$connection->executeQuery('SELECT 1');
} catch (Throwable) {
$connection->close();
}
}


Важный момент, что кол-во соединений, которое может держать БД тоже ограниченно, поэтому не стоит его держать все время если у нас нет полезной нагрузки, поэтому в примере проверка соединения идет непосредственно перед обработкой сообщения
🔥
9
👍
3
1
148
09:01
1 августа
Я изначально написал пост, расписав каждый пункт, но получилось очень много текста. Если тема интересна для вас, накиньте огня, я закину серию мини-постов по каждому пункту.
🔥
27
139
12:47
PHP, который не умер

Решил сыграть на кликбейте 😄 сори

Есть такой термин Long-Running PHP Processes — это процессы PHP, которые работают длительное время, минутами, часами, сутками, не завершаясь.

Обычно используется для:

1. обработки очередей, Consumers, Gearman и т. д.

2. выполнения длительных задач:

- построение проекций (projection) в БД;

- генерация фидов;

- выгрузка данных из внешних систем;
- формирование отчетов;

- очистка устаревших данных в БД или файлов в файловом хранилище;

- рассылка почты и т. д.

3. для асинхронной обработки запросов ReactPHP, AMPHP, RoadRunner, Swoole


Изначально я хотел написать, что PHP не приспособлен для этих задач, но это не совсем так.

Но есть ряд проблем. Дураки и дороги 😄

Во-первых, большинство PHP-разработчиков начинают свой путь с веб-скриптов, веб-сайтов, и основную часть своей деятельности работают с php-fpm, когда скрипт выполняет логику и умирает. В этой схеме не нужно заботиться об утечках памяти, об освобождении ресурсов, обработке сбоев. А во-вторых, некоторые библиотеки PHP, фреймворки и CMS с оглядкой на первый пункт позволяют себе пренебрегать некоторыми вещами. Как правило, это утечка памяти. У PHP тоже имеет проблемы с утечками памяти, но у которого из ЯП их нет? Можете глянуть топики на GitHub.

Но вот с чем точно придется столкнуться:

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

Я думаю, что каждый встречал такую ошибку:

MySQL server has gone away

Просто пока скрипт ждет сообщения от брокера, например, соединение с БД может закрыться.

2. Утечка памяти — ну тут всё понятно, могут быть проблемы как с вендорскими пакетами, так и в коде приложения, так и в самом PHP или в его расширениях. Последнее очень редко встречается.

3. Graceful Shutdown — если убить скрипт, то он завершится на том месте, где сейчас выполняет код, а это чревато неконсистентными данными в БД или потерей сообщений.

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

5. Определение $_ENV происходит только при запуске.

6. Мониторинг — нужно как-то понимать, что скрипт дышит и он не завис.

7. Деплой — нужно не забыть перезапустить скрипт после деплоя, иначе изменения не подтянутся.

8. Есть лимит дескрипторов ресурсов в 8,6 миллиарда, если грубо говоря, вы не сможете открыть 8,6 миллиарда файлов за один запуск скрипта.


Скрипты нужно как-то запускать и перезапускать при сбоях, и для используют следующее:

1. для задач по расписанию используют cron
2. для воркеров/консьюмеров:
- в среде ОС supervisord, systemd
- докер:
- docker-compose — один воркер = один контейнер
- kuber — один воркер = один под
- можно supervisord запихать в докер-образ, но не советую так делать
- roadrunner — так же позволяет менеджерить воркеры
🔥
19
👍
4
❤‍🔥
2
👏
1
🤯
1
👌
1
🍓
1
155
12:47
19 июля
В связи с последними новостями https://www.youtube.com/shorts/_yEWlLPSr2s
😁
6
🤣
6
254
11:58
12 июля
Тема этой недели для меня Outbox Transactional, я рассказывал про него аж 3 командам 7 раз за эту неделю.

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

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

Представьте у вас есть use case, в рамках которого вы пишите данные в БД и отправляете сообщение в брокер сообщений.

Можно выделить три компонента:

1. приложение
2. база данных
3. брокер сообщений

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

Какие состояния из этого можно получить:

1. В БД стейт изменен, в брокер сообщение отправлено - это ОК
2. В БД стейт изменен, в брокер сообщение не ушло -- это НЕ ОК, может приводить к неконсистентным данным между различными частями системы.
3. В БД стейт не изменен, в брокер сообщение ушло -- это НЕ ОК, может приводить к неконсистентным данным между различными частями системы.
4. БД стейт не изменен, в брокер сообщение не ушло - это ОК, данные не были изменены, соотвественно проблем с консистентностью не будет

Какое решение? Это транзакции , но транзакция возможна только в рамках одного хранилища, которое гарантирует свойство atomicity см. ACID

А одну транзакцию между разными системами не откроешь 🙂 2pc тоже не особо крутой вариант для этой задачи

Здесь и вступает в игру паттерн Outbox Transactional. Суть которого заключается в том, что в рамках одной атомарной транзакции записывается стейт в БД и в эту же БД записываются сообщения, которые нужно отправить в брокер. Затем отдельным скриптом забираем сообщения из БД и шлем в брокер. Гарантии доставки : at-least once , т.е. сообщение может быть отправлено больше одного раза в брокер

Считаю этот паттерн MUST HAVE для построения надежных систем, поэтому если вы ещё не знакомы с этим паттерном, рекомендую ознакомиться
🔥
13
👍
3
🤯
2
🤔
1
307
13:00
Кейсы
Каталог ИМ электротоваров nda

Интернет-магазин в стандартной связке Bitrix — 1C, управление каталогом происходит в 1С, ежедневный обмен между сайтом и 1С, проблема: во время импорта ИМ начинает тормозить, 1С тяжело работать с товарной базой, 1С долго загружает каталог, из-за чего менеджер тратит 15-20 минут на составления одного документа (накладная, акт и т.д.).
Моя задача была провести аналитику, разработать архитектуру, собрать и организовать команду разработки.
Мы внедрили и доработали под нужды клиента PIM, за основу взяли один из продуктов Ensi, адаптировали схемы кафки под проект. Разработали микросервис «Витрина» с использованием elasticsearach, для отображения, полнотекстового и фасетного поиска товаров на стороне интернет магазина. Реализовали отдельный микросервис для импорта товаров из 1С и RAEC.
Результат: загрузка страницы каталога меньше секунды с товарной базой в 1,5 млн товара. Удобный интерфейс управления товаром. Автоматическое масштабирование сервисов при больших объемах обмена товарами между системами. Техническое задание для 1С разработчиков для интеграции с системой PIM.
Роль: Teamlead, Архитектор
Технологии: PHP 8.1, Symfony, PostgreSQL, Kafka, Redis, Grafana, Elasticsearch, Docker, Filebeat, S3, Roadrunner, DDD

Отдел разработки веб-приложений аутсорс-компании

Руководил отделом разработки аутсорс компании с оборотом 50+ млн в год.

HR и рекрутинг

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

Обучил HR-менеджера стеку компании и взаимозаменяемым технологиям.

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

Проработали ограничивающую мотивацию, она включала в себя кодекс поведения, например, где разрешено курить, а где нет и чем это карается, рекомендации по коммуникациям (например, тл, не любят, когда их отвлекают оффлайн, они могли готовиться к собеседованиям или созвону с клиентам, им комфортнее, когда им предварительно пишут в личку), соблюдение порядка на кухне и т.д. Это позволило быстрее адаптироваться новым сотрудникам, а старые сотрудники меньше испытывали дискомфорт от пополнения команды. После этого, мы разработали Playbook, куда вошла ограничивающая мотивация, а также информация о компании, кейсы компании, раздел who is who, вопросы ЗП, больничных и отпусков, FAQ. Что способствовало ещё скорой адаптации новых сотрудников и знакомству с компанией.

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

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

Программа обучения для стажеров

Курировал программу обучения стажеров направление Bitrix:

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

Разработал программу обучения Laravel.

Курировал разработку программы обучения фронтенд-разработчиков React , и запустил 3 потока стажеров.

People management
  • занимался организацией внутренних митапов, выступал сам;
  • разработал систему грейдов и организовал аттестацию сотрудников по защите грейдов;
  • составлял ИПР;
  • проводил one-to-one встречи;
  • отвечал за ЗП и премии;
  • отвечал за мотивацию сотрудников и климат в командах;
  • менторил тимлидов;
  • внедрил roadmap для тимлидов и менторов.
Инженерная культура

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

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

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

Проектное управление
  • занимался построением цикла разработки (прием задач/оценка/сроки/сдача/раскатка/приёмка), внедрял в команды готовые решения Kanban/Scrum, упрощенные и смешанные типы на их базе;
  • занимается генерацией бэклога и его приотизацией;
  • интегрировал клиентов в процесс разработки, чтобы сделать процесс прозрачнее, помогал с продуктовой стратегией как технический специалист, участвовал в составлении роадмапа продукта;
  • управлял рисками проектов;
  • занимался подбором команды под проект.
Продажи
  • участвовал в пресейлах, составлении сметы, изучение требований и рисков;
  • составлял роадмап проекта;
  • занимался подготовкой коммерческого предложения и проводил его защиту;
  • внедрил систему критериев оценки лидов;
  • развивал аутстаф канал, общение и знакомство с партнерами, подбор разработчиков.
Экосистема аутсорс-компании

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

Корпоративный портал

Корп. портал предоставлял функционал для управления списками сотрудников, создание учетных записей сотрудников при найме, и блокировки при увольнении; интеграция с Yandex360 для создания корпоративной почты. На базе Btirix 24 оцифровал бизнес-процесс для стажировки разработчиков в компании, включал в себя автоматическое создание задач, трек времени, контрольные точки и сбор метрики для повышения качества стажировки, интеграция с gitlab для автоматического создания шаблонного репозитория стажера. Основной функционал реализован на базе Bitrix 24. Мы с командой придумали как внедрить React-компоненты в стандартные шаблоны битрикса, это позволило нам получать обновления битрикса без проблем. Проектом занималась команда из 4 человек: Тимлид, два Backend и один Frontend разработчики. Ну и я пятый, в качестве Product owner.

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

Технологии: Bitrix24, ReactJs

Outstaff направление
  • я спроектировал микросервис для оформления резюме сотрудников, команда реализовала сервис с помощью laravel + react, в команда было 3 человека: TL, Backend и Frontend разработчики;
  • мы разработали телеграмм-бота, который собирал запросы поиска разработчиков из телеграмм каналов и добавлял задачи в jira. Это помогло аккаунт-менеджеру и сейлеру не пропускать актуальные заявки из большого числа чатов.
Планирование и учет прибыли

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

С командой разработчиком создали MVP планирование на базе Bitrix24.

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

Внедрил SSO на базе Keycloak для всех сервисов компании.

Кол-во сервисов росло, и становилось всё тяжелее управлять пользователями, потому что при устройстве новых сотрудников или увольнении текущий вносить изменения почти в десяток сервисов, Keycloak позволил организовать единое место для управления учетными записями сотрудников.

Внедрил Outline для ведения документации компании.

Outline/Outline - крутой инструмент для документации, использовали его как для проектной документации, так и для внутренних регламентов.

Внедрил Jira для отдела разработки и сейлеров
YouTravel.me - площадка для организации путешествий

С этим проектом я познакомился в начале своей карьеры, занимался разработкой и сопровождением площадки, работал над локализацией проекта, автоматическим переводом контента через deepl, выполнил интеграцию телефонии Vox Implement, принимал участие в разработке функционала платежей и мини crm для турооператоров. Разработал интеграцию с MailChimp.
Сегодня я консультирую разработку бекенд части одного из сервисов YouTravel.me. Мы решаем вопросы распила монолита на битрикса, способы коммуникации между сервисами, работаем над качеством проекта. Помогаю подобрать сотрудников и построить крутой skillmap для развития компетенций разработчиков в штате.
Роль: Консалтинг
Технологии: Vue, Laravel, RabbitMQ, Bitrix, MySql, VoxImplant, Stripe, Deepl

Калькулятор расчета пошлин nda

Клиент обратился за помощью запустить MVP для показа инвесторам. Я занимался сбором требований, функциональный и нефункциональных требований, мы с командой разработали прототипы, реализовали базовый функционал и уложились в 2 недели.
Роль: Teamlead
Технологии: PHP 8.1, Laravel, Docker, ReactJs, PostgreSQL

CRM для торговых сетей по продаже бытовой техники и электроники nda

У заказчика две торговых сети и два интернет-магазина. Задача нашей команды была разработать модуль интеграции с интернет-магазинами для обмена данными по B2B сегменту: контакты, заказы, сделки. Все сущности проходили этапы валидации, а компании проверяли в реестре ЕГРЮЛ. Я выступал в качестве техлида, занимался архитектурой модуля, интеграцией с репликой и кафкой. В моменте мы выгрузили около 180к действующих компаний и 800к контактов.
Роль: Techlead
Технологии: PHP, Битрикс 24, Kafkа, MySql

Интернет-магазин электроники, цифровой и бытовой техники федерального уровня nda

Я принимал участие в проекте в качестве бекенд-разработчика, занимался развитием жизненного цикла заказа, системой лояльности покупателей, поддержкой механики скидок, интеграции с CRM и системой управления заказами. Принял участие в разработке API для мобильного приложения. Одна из интересных задач - необходимо было перевести проект с PHP 7.4 на 8.0.
Роль: Developer
Технологии: PHP 8, 1С-Битрикс, RabbitMQ, Kafkа, REST, SOAP, Rector

😎