Я изначально написал пост, расписав каждый пункт, но получилось очень много текста. Если тема интересна для вас, накиньте огня, я закину серию мини-постов по каждому пункту.
🔥27
Простой Long Running скрипт выглядит так:
Добавим в него консюм из очереди:
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
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
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, либа, которая часто используется для логирования.
Это более частные проблемы, которые в целом известны.
Но если говорить более обобщенно, то и решение должно быть общим.
Один из вариантов - выставить безлимитное использоавние памяти
Но ресурсы не безграничны, на физической машине может просто закончиться память. А если вы используете кубер, то OOM Killer просто убьет ваш процесс и дело с концом.
Более оптимальное решение этой проблемы - это корректный перезапуск скрипта при достижении потолка по памяти.
Так, с помощью 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
Утечка памяти
Ну тут всё понятно, могут быть проблемы как с вендорскими пакетами, так и в коде приложения, и в самом 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
1🔥12❤1👏1
3 из 8 Graceful Shutdown
Если убить скрипт, то он завершится на том месте, где сейчас выполняется код, а это чревато неконсистентными данными в БД или потерей сообщений.
Скрипт может завершиться в ходе исполнения, например, скрипт обрабатывает очередь, получил сообщение, начал что-то писать в БД и завершился по причине того, что пользователь нажал
Если не используются транзакции, то могут получиться неконсистентные данные в БД: часть данных сохраниться, часть нет. Также если бывает логика, которую не завернуть в транзакцию. Плюс есть риски потерять сообщение из очереди, если например нет механизма подтверждения получения сообщения.
При некорректном завершении могут остаться временные файлы, лок-файлы, что может привести к некорректному перезапуску скрипта.
Чтобы корректно завершить приложение, нужно обработать сигналы SIGTERM, SIGINT, SIGUSR1, SIGUSR2, SIGQUIT, SIGHUP с помощью pcntl_signal.
Если убить скрипт, то он завершится на том месте, где сейчас выполняется код, а это чревато неконсистентными данными в БД или потерей сообщений.
Скрипт может завершиться в ходе исполнения, например, скрипт обрабатывает очередь, получил сообщение, начал что-то писать в БД и завершился по причине того, что пользователь нажал
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
{
...
}
1🔥12👍2👏1
4 из 8 Обработка исключений и ошибок
Нужно более тщательно обрабатывать ошибки, ловить exceptions, чтобы скрипт не падал в случае какой-то ошибки, которой можно пренебречь. Например, не удалось обработать одно из сообщений из брокера очередей, это не значит, что мы должны остановить обработку очереди.
Решение простое, обернуть процессинг в try-catch
Так мы обработаем исключения, но остаются ещё ошибки. Для решения этой задачи я предлагаю конвертировать ошибку в исключение и обрабатывать её в стандартном флоу. Это можно сделать с помощью set_error_handler .
Нужно продумать, что именно указать в
Нужно более тщательно обрабатывать ошибки, ловить 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 будет критичным, и его стоит обрабатывать как ошибку.1🔥12👍6❤2
5 из 8 Определение $_ENV происходит только при запуске
Чтение из env происходит только при запуске скрипта
Вообще, есть два варианта инициализации env:
1) приложение читает переменные из файл .env - в этом случае можно просто перечитывать файл раз в какой-то период
2) переменные задаются при запуске контейнера, например, из AWS Secrets Manager, Gitlab Variables, или так же из файл .env через docker compose.
В этом случае простого решения нет. Как правило, всё сводится к тому, что нужно перезапускать контейнер, соответственно и скрипт, поэтому в этом случае также важен Graceful Shutdown.
Для кубера есть решение в виде stakater/Reloader для остального у меня нет решения, но придумать какие-то костыли, оснастки точно можно, все зависит от оркестрации приложения и того, по какой причине понадобилось обновить переменные окружения.
Чтение из env происходит только при запуске скрипта
Вообще, есть два варианта инициализации env:
1) приложение читает переменные из файл .env - в этом случае можно просто перечитывать файл раз в какой-то период
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
2) переменные задаются при запуске контейнера, например, из AWS Secrets Manager, Gitlab Variables, или так же из файл .env через docker compose.
В этом случае простого решения нет. Как правило, всё сводится к тому, что нужно перезапускать контейнер, соответственно и скрипт, поэтому в этом случае также важен Graceful Shutdown.
Для кубера есть решение в виде stakater/Reloader для остального у меня нет решения, но придумать какие-то костыли, оснастки точно можно, все зависит от оркестрации приложения и того, по какой причине понадобилось обновить переменные окружения.
4🔥10👍1🙏1
Ну и финалимся по теме 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 но вы можете проверить самостоятельно ради интереса )
Ну, собственно, на этом всё)
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 но вы можете проверить самостоятельно ради интереса )
Ну, собственно, на этом всё)
6🔥12👏4❤3
Хотел написать пост про package by feature, но когда начал собирать фактуру, понял, что проблема, которую я хотел обозреть, куда глубже. Поэтому сегодня поговорим про упаковку кода в целом.
На своей практике я чаще всего вижу 2 варианта упаковки кода:
1. Package by type - когда проект состоит из таких папок, как Controllers, Services, Repositories, Entities, DTOs и т.д. Обычный скелетон, который предлагает любой фреймворк.
2. Package by layer - когда проект состоит из папок, как App, Domain, Infra и т.д. Те, кто только начинают практиковать DDD, часто используют этот вариант.
Есть третий вариант, о котором я очень часто слышу, но в реальных проектах, которые проходили через меня, пока не встречал в чистом виде.
3. Package by feature - когда проект делится на фичи: Feature1, Feature2, Feature3 и т.д.
Например, фича регистрации пользователя содержит и сущность, и DTO, и контроллер, и сервис, и репозиторий. Если сущность переиспользуется в другой фиче, то её выносят в общий модуль и импортируют.
А вот какой подход лучше?
Каждый подход хорош, пока кодовая база небольшая.
Package by type -- считаю самым отвратительным способом. Можно сказать, одна фича размыта по 5 или 6 папкам, между которыми приходится постоянно переключаться.
Package by layer -- супер, мы выделили слои, но у нас по-прежнему каждый слой содержит кучу функциональности, файлов и папок. Весь проект просто разделен на технические слои, а не по функциональности.
Package by feature -- код фичи инкапсулирован, но вероятно, фич может быть также много, они могут быть связаны между собой и использовать одни и те же сущности. Разбухает количество shared классов. Также несколько фич может относиться к какой-то определенной зоне функциональности продукта.
Что делать?
Забирать лучшее от каждого подхода.
В DDD есть понятие домена. В рамках такого домена существует своя модель, термины и свои правила. Каждый домен развивается независимо друг от друга. И это хорошая точка для начала упаковки. Таким доменами могут быть, например, billing, catalog, users и т.д. Здесь прослеживается подход package by feature. Домены могут быть простыми и сложными, например, billing может быть очень сложным, включать в себя оплату, выплаты, счета и т.д. И такой домен можно ещё покомпозировать и поделить на поддомены. Продолжаем делить по типу package by feature.
Код домена/поддомена можно поделить по-разному, всё зависит от его сложности. Если домен состоит из пары файлов, то смысла делить вообще нет. Если домен типа support или generic, то можно использовать package by type. Если домен сложный и/или core, то стоит поделить на слои App, Domain, Infra, UI, получив все преимущества чистой архитектуры.
Каждый слой также требует декомпозиции. Возьмем слой Domain, который может быть достаточно большим, классы в нём захочется упаковать по папочкам. И тут снова стоит поделить на фичи. Например, Order, Customer, Product. Не стоит делить по типу: Entity, Repository, ValueObject и т.д. Иначе это снова приведет к тому, что код, относящийся к одной функциональности, будет разделен по разным каталогам.
Комбинация этих подходов к упаковке кода даст хорошо структурированный проект, с независимыми модулями, каждый из которых может применять ту архитектуру, которая будет приносить максимальную пользу для конкретного домена.
18🔥16✍4❤3👏1
В одном из предыдущих постов я писал про упаковку кода.
Хочу ещё обратить внимание на папку utils в проектах.
Ставь 💊 , если в вашем проекте есть папка utils и она похожа на солянку после новогоднего стола.
utils/common/base - набор бессмысленных названий пакетов и модулей, которые открываешь со словами из мема "ЧТО У ВАС ЗДЕСЬ ПРОИСХОДИТ?"
Да, у нас у всех есть дублирующийся код, который вообще не зависит от предметной области, конкретного модуля, и есть желание его вынести и переиспользовать.
Но не нужно бездумно выносить его в "utils", как "нужную" вещь на балкон.
Я призываю четко выделять пакеты, определять их границы и давать наименования, отталкиваясь от того, что они делают, а не от того, что в них содержится.
Вот пример
❌
Декомпозированный вариант, с выделением пакетов по смыслу:
✅
Бессмысленное название пакетов прямой путь к складированию кода, нарушению SRP, запутыванию кода и поиску нужных вещей.
Если у вас был опыт с такой папкой, встречали ли вы две одинаковые функции в ней, написанные разными разработчиками?
У меня было и такое, но одна и та же функция по смыслу, написана дважды разными способами один разработчиком. Не потому что у него биполярка, просто
когда utils разрастается, там реально хер что найдешь 🙂
Хочу ещё обратить внимание на папку utils в проектах.
Ставь 💊 , если в вашем проекте есть папка utils и она похожа на солянку после новогоднего стола.
utils/common/base - набор бессмысленных названий пакетов и модулей, которые открываешь со словами из мема "ЧТО У ВАС ЗДЕСЬ ПРОИСХОДИТ?"
Да, у нас у всех есть дублирующийся код, который вообще не зависит от предметной области, конкретного модуля, и есть желание его вынести и переиспользовать.
Но не нужно бездумно выносить его в "utils", как "нужную" вещь на балкон.
Я призываю четко выделять пакеты, определять их границы и давать наименования, отталкиваясь от того, что они делают, а не от того, что в них содержится.
Вот пример
❌
utils/
- Floats.php
- Integers.php
- ChunkHelper.php
- RequestHelper.php
- Masker.php
- TimezoneShiftInterface.php
- UuidUtils.php
- Pagination.php
- Config/
- Parser.php
- ParseException.php
Декомпозированный вариант, с выделением пакетов по смыслу:
✅
math/
- FloatOperations.php
- IntegerOperations.php
http/
- CreateRequest.php
- ErrorHandler.php
datetime/
- TimezoneShiftInterface.php
uuid/
- UuidFactory.php
crud/
- Pagination.php
config/
- Parser.php
- ParseException.php
Бессмысленное название пакетов прямой путь к складированию кода, нарушению SRP, запутыванию кода и поиску нужных вещей.
Если у вас был опыт с такой папкой, встречали ли вы две одинаковые функции в ней, написанные разными разработчиками?
У меня было и такое, но одна и та же функция по смыслу, написана дважды разными способами один разработчиком. Не потому что у него биполярка, просто
когда utils разрастается, там реально хер что найдешь 🙂
12👍15💊7🔥6❤5
Tech generation gap
Или старая школа против новой школы
1950-1960
В 50-х случилась первая революция в программировании — появился Fortran. Олды, привыкшие писать на ассемблере и колдовать с перфокартами, были в шоке: "Высокоуровневый язык? Серьёзно?" Они считали, что настоящий программист обязан понимать каждый байт и такие абстракции убьют всю производительность. В те времена айтишники были реально как небожители — попасть в профессию стоило космических денег, а знания передавались чуть ли не из рук в руки. Но всё резко изменилось с появлением пк, и началась новая эра программирования.
1970-1980
70-е принесли C и Pascal, и снова началось: "Слишком много абстракций!" Олды были уверены, что эти языки превратят программирование в детскую игру в конструктор. "Как можно писать код, не зная, что происходит на уровне железа?" — возмущались они. И конечно, все были уверены, что производительность упадёт в пропасть. Знакомо звучит, да?
1990
90-е: на сцену выходят Java и .NET, и С/С++ разработчики хватаются за голову: "Виртуальная машина? Сборщик мусора? Да вы издеваетесь!" По их мнению, garbage collection был костылём для ленивых кодеров, которые не хотят разбираться с управлением памяти. "Настоящие программисты сами освобождают память!" — звучало со всех сторон. История повторялась, только теперь уже C++ разработчики оказались в роли консерваторов.
2000
Нулевые встретили Python, Ruby и PHP громким "фейспалмом" от корифеев: "Интерпретируемые языки? Серьёзно?" Опытные разработчики крутили пальцем у виска: "Какие большие системы? Это же просто игрушки для джунов!" Но мир менялся — open source движение набирало обороты, и молодёжь чувствовала себя в нём как рыба в воде. Старая гвардия, годами строившая закрытые системы, с трудом принимала новую философию разработки, а их богатый опыт внезапно стал казаться менее релевантным.
2010
2010-е принесли мобильную революцию и облака. "Какой еще Swift? React Native? Да на нативном Android/iOS писать надо!" — ворчали опытные разработчики. А молодежь уже вовсю запускала стартапы на AWS и Azure, пока старая гвардия недоверчиво косилась на "эти ваши облака" и продолжала держаться за собственные сервера. "Доверить данные третьей стороне? Да вы с ума сошли!" — и снова история повторялась, теперь уже с новыми технологиями.
2020
2020-й ворвался в нашу жизнь ковидом и тотальной удалёнкой. "Какая эффективность на удалёнке? Как вообще можно командой работать через Zoom?" — паниковали олды, привыкшие к опенспейсам и личным стендапам. А молодёжь уже вовсю гоняла код через GitHub, чилила в Дискорде и успевала фрилансить между стендапами. "Офис — это место силы!" — настаивали менеджеры старой школы, пока их команды доказывали обратное, разбежавшись по домам и коворкингам. И снова одни топили за стабильность и традиции, а другие — за новые подходы к работе.
2022
2022-й принёс нам ChatGPT, и понеслось: "Какой ещё AI-ассистент? Нормальные программисты сами пишут код!" — возмущались опытные разрабы, видя как джуны генерируют целые функции одним промптом. Но пока одни причитали про "деградацию профессии", другие уже вовсю использовали нейронки для рутины и прототипирования. "Это не настоящее программирование!" — кричали хардкорщики, наблюдая как их младшие коллеги решают за час те задачи, на которые раньше уходили дни. И снова знакомая картина: старая гвардия защищает традиционный подход, пока новое поколение осваивает инструменты будущего.
По-любому, лет через 10 мы сами будем те самые олды, которые ворчат на очередную революционную технологию
Ну что, почитали? пойдемте тогда работать , кто байтики перекладывать, кто массивы в дтоошки и обратно, или может с ноги в AI?
Или старая школа против новой школы
1950-1960
В 50-х случилась первая революция в программировании — появился Fortran. Олды, привыкшие писать на ассемблере и колдовать с перфокартами, были в шоке: "Высокоуровневый язык? Серьёзно?" Они считали, что настоящий программист обязан понимать каждый байт и такие абстракции убьют всю производительность. В те времена айтишники были реально как небожители — попасть в профессию стоило космических денег, а знания передавались чуть ли не из рук в руки. Но всё резко изменилось с появлением пк, и началась новая эра программирования.
1970-1980
70-е принесли C и Pascal, и снова началось: "Слишком много абстракций!" Олды были уверены, что эти языки превратят программирование в детскую игру в конструктор. "Как можно писать код, не зная, что происходит на уровне железа?" — возмущались они. И конечно, все были уверены, что производительность упадёт в пропасть. Знакомо звучит, да?
1990
90-е: на сцену выходят Java и .NET, и С/С++ разработчики хватаются за голову: "Виртуальная машина? Сборщик мусора? Да вы издеваетесь!" По их мнению, garbage collection был костылём для ленивых кодеров, которые не хотят разбираться с управлением памяти. "Настоящие программисты сами освобождают память!" — звучало со всех сторон. История повторялась, только теперь уже C++ разработчики оказались в роли консерваторов.
2000
Нулевые встретили Python, Ruby и PHP громким "фейспалмом" от корифеев: "Интерпретируемые языки? Серьёзно?" Опытные разработчики крутили пальцем у виска: "Какие большие системы? Это же просто игрушки для джунов!" Но мир менялся — open source движение набирало обороты, и молодёжь чувствовала себя в нём как рыба в воде. Старая гвардия, годами строившая закрытые системы, с трудом принимала новую философию разработки, а их богатый опыт внезапно стал казаться менее релевантным.
2010
2010-е принесли мобильную революцию и облака. "Какой еще Swift? React Native? Да на нативном Android/iOS писать надо!" — ворчали опытные разработчики. А молодежь уже вовсю запускала стартапы на AWS и Azure, пока старая гвардия недоверчиво косилась на "эти ваши облака" и продолжала держаться за собственные сервера. "Доверить данные третьей стороне? Да вы с ума сошли!" — и снова история повторялась, теперь уже с новыми технологиями.
2020
2020-й ворвался в нашу жизнь ковидом и тотальной удалёнкой. "Какая эффективность на удалёнке? Как вообще можно командой работать через Zoom?" — паниковали олды, привыкшие к опенспейсам и личным стендапам. А молодёжь уже вовсю гоняла код через GitHub, чилила в Дискорде и успевала фрилансить между стендапами. "Офис — это место силы!" — настаивали менеджеры старой школы, пока их команды доказывали обратное, разбежавшись по домам и коворкингам. И снова одни топили за стабильность и традиции, а другие — за новые подходы к работе.
2022
2022-й принёс нам ChatGPT, и понеслось: "Какой ещё AI-ассистент? Нормальные программисты сами пишут код!" — возмущались опытные разрабы, видя как джуны генерируют целые функции одним промптом. Но пока одни причитали про "деградацию профессии", другие уже вовсю использовали нейронки для рутины и прототипирования. "Это не настоящее программирование!" — кричали хардкорщики, наблюдая как их младшие коллеги решают за час те задачи, на которые раньше уходили дни. И снова знакомая картина: старая гвардия защищает традиционный подход, пока новое поколение осваивает инструменты будущего.
По-любому, лет через 10 мы сами будем те самые олды, которые ворчат на очередную революционную технологию
Ну что, почитали? пойдемте тогда работать , кто байтики перекладывать, кто массивы в дтоошки и обратно, или может с ноги в AI?
6🔥15❤5💯5👍2🆒1
Я не использую LLM, потому что она украдёт мой код
Только этот код никому не нужен, кроме меня/заказчика/работодателя. Заказчику даже нужен не код, а решение проблемы.
Код сам по себе не приносит деньги, пока не превращается в продукт или услугу. Монетизация кода заключается в его использовании для создания решения, которое можно продавать разово или по подписке, лицензировать, обеспечивая тем самым доход и ценность для бизнеса.
Код является важной частью бизнеса, особенно в технических компаниях. Но помимо кода, нужно ещё уделять кучу времени на другие вещи. Возьмём аренду квартиры и коммерческих помещений. Представь, я получил код Airbnb. Я готов запустить его уже сегодня и стартовать свою площадку по объявлениям аренды квартир. Это лишь 10%. Нужно подумать о следующем:
1) как привлекать клиентов? Сразу все рванут в аналог Airbnb?
2) как заполучить доверие клиентов?
3) как регулировать юридические вопросы?
4) как регулировать конфликты между арендодателями и арендаторами?
5) как наладить операционные процессы?
6) как выстроить команду разработки, поддержки, маркетинга, продаж, бухгалтерии и т.д.?
Даже если у меня уже собрана вся команда замотивированных ребят, есть бюджет, то остаётся маркетинг, в который нужно вкладываться, развивать бренд, искать новые каналы привлечения клиентов и выстраивать с ними доверительные отношения. Нужно своевременно адаптировать продукт к изменениям на рынке и в законах.
Поэтому код без продукта не имеет особой ценности.
Вот пару примеров:
Хабр Фриланс - моё мнение, он обрёл популярность, потому что было создано определённое комьюнити вокруг основного продукта - Хабра. Но оказался невыгоден, его закрыли. Сложно ли реализовать такую площадку с точки зрения кода? -- Нет. Простой CRUD на Ларавеле.
Яндекс - слили огромную кодовую базу, такси, маркет, почта, диск, трекер. Был ли взращён на этом новый конкурент? -- Нет. К нам в город заходил DiDi и Ситимобил. Они активно развивались в моём городе, но сейчас я про них уже не слышу. Помог ли им код Яндекса? -- Нет.
Ну и возвращаюсь от размышлений к практичному. Например, я пишу сервис авторизации. Его уже написали миллион и один человек. Витрину в магазине? -- реализация есть в каждом ИМ. Верификация данных -- обычная задача. Куча похожего кода в открытом доступе. LLM ничего не выиграет с моей реализации. После обучения на моём коде, может быть, станет даже хуже решать подобные задачи, извините. 😁😁😁
Провайдеры LLM только и ждут, когда я солью им свою кодовую базу регионального интернет-магазина, реализацию витрины и доски объявлений, или очередную "уникальную" задачу.
Но когда это всё же оправдано, код - это core домен, например:
- алгоритм сжатия данных
- алгоритм шифрования
- алгоритм машинного обучения, разработчики DeepSeek разработали алгоритм для дешёвого обучения моделей, было бы странно, если бы они эту кодовую базу слили в OpenAI, используя ChatGPT.
- военная промышленность
- и т.п.
Короче, никому ваш код не нужен, а вот ассистент ускорит вашу работу в множество раз.
Только этот код никому не нужен, кроме меня/заказчика/работодателя. Заказчику даже нужен не код, а решение проблемы.
Код сам по себе не приносит деньги, пока не превращается в продукт или услугу. Монетизация кода заключается в его использовании для создания решения, которое можно продавать разово или по подписке, лицензировать, обеспечивая тем самым доход и ценность для бизнеса.
Код является важной частью бизнеса, особенно в технических компаниях. Но помимо кода, нужно ещё уделять кучу времени на другие вещи. Возьмём аренду квартиры и коммерческих помещений. Представь, я получил код Airbnb. Я готов запустить его уже сегодня и стартовать свою площадку по объявлениям аренды квартир. Это лишь 10%. Нужно подумать о следующем:
1) как привлекать клиентов? Сразу все рванут в аналог Airbnb?
2) как заполучить доверие клиентов?
3) как регулировать юридические вопросы?
4) как регулировать конфликты между арендодателями и арендаторами?
5) как наладить операционные процессы?
6) как выстроить команду разработки, поддержки, маркетинга, продаж, бухгалтерии и т.д.?
Даже если у меня уже собрана вся команда замотивированных ребят, есть бюджет, то остаётся маркетинг, в который нужно вкладываться, развивать бренд, искать новые каналы привлечения клиентов и выстраивать с ними доверительные отношения. Нужно своевременно адаптировать продукт к изменениям на рынке и в законах.
Поэтому код без продукта не имеет особой ценности.
Вот пару примеров:
Хабр Фриланс - моё мнение, он обрёл популярность, потому что было создано определённое комьюнити вокруг основного продукта - Хабра. Но оказался невыгоден, его закрыли. Сложно ли реализовать такую площадку с точки зрения кода? -- Нет. Простой CRUD на Ларавеле.
Яндекс - слили огромную кодовую базу, такси, маркет, почта, диск, трекер. Был ли взращён на этом новый конкурент? -- Нет. К нам в город заходил DiDi и Ситимобил. Они активно развивались в моём городе, но сейчас я про них уже не слышу. Помог ли им код Яндекса? -- Нет.
Ну и возвращаюсь от размышлений к практичному. Например, я пишу сервис авторизации. Его уже написали миллион и один человек. Витрину в магазине? -- реализация есть в каждом ИМ. Верификация данных -- обычная задача. Куча похожего кода в открытом доступе. LLM ничего не выиграет с моей реализации. После обучения на моём коде, может быть, станет даже хуже решать подобные задачи, извините. 😁😁😁
Провайдеры LLM только и ждут, когда я солью им свою кодовую базу регионального интернет-магазина, реализацию витрины и доски объявлений, или очередную "уникальную" задачу.
Но когда это всё же оправдано, код - это core домен, например:
- алгоритм сжатия данных
- алгоритм шифрования
- алгоритм машинного обучения, разработчики DeepSeek разработали алгоритм для дешёвого обучения моделей, было бы странно, если бы они эту кодовую базу слили в OpenAI, используя ChatGPT.
- военная промышленность
- и т.п.
Короче, никому ваш код не нужен, а вот ассистент ускорит вашу работу в множество раз.
3👍11🔥7💯4🤓3❤🔥1
А знаете, в чем парадокс?
В DDD одно из ключевых идей заключается в том, что код должен быть написан так, чтобы бизнес-эксперт (доменный эксперт) мог его прочитать и понять, что происходит, даже не будучи программистом.
Внедрить DDD оч сложно, сопротивление ждет на каждом уровне, от разработчиков, экспертов и начальства. Не смог продать? -- Сидишь юзаешь тактические паттерны в свое удовольствие, и спросишь с тимлидом о решениях, вот и всё -- типичное внедрение. Тут же от тебя DDD требует и дисциплину, добавляет огоньку в жаркие споры и ссоры с коллегами, а также задачу ещё вчера нужно было сдать. А ещё, а ещё -- реализовали крутой тактический паттерн, а он дал тебе оверхед и нагрузку на инфу, ну счастье 🙂
Поэтому внедрение DDD затягивается или не внедряется.
LLM сейчас активно внедряется в разработку, 25% стартапов имеют 95% кода, написанного AI, 60% разработчиков юзают ллм, ттм растет, а время внедрять ддд, обучаться и обучать команду стало ещё меньше. Код генерится быстрее, чем ты успеваешь подумать.
Появились вайб-кодеры, пишут приложения с нуля, без знания ЯП и в целом технологий, и всё гуд, а кто может написать таким образом приложение и получит профит? Конечно же доменный эксперт. Тот самый, из которого мы пытаемся постоянно извлечь знания, чтобы их описать в программную модель. И логично, что он общается с LLM на том же языке, который ему привычен и этот язык является единым, с присущими терминами для домена.
Вот такой парадокс, ддд зашел с другой стороны
В DDD одно из ключевых идей заключается в том, что код должен быть написан так, чтобы бизнес-эксперт (доменный эксперт) мог его прочитать и понять, что происходит, даже не будучи программистом.
Внедрить DDD оч сложно, сопротивление ждет на каждом уровне, от разработчиков, экспертов и начальства. Не смог продать? -- Сидишь юзаешь тактические паттерны в свое удовольствие, и спросишь с тимлидом о решениях, вот и всё -- типичное внедрение. Тут же от тебя DDD требует и дисциплину, добавляет огоньку в жаркие споры и ссоры с коллегами, а также задачу ещё вчера нужно было сдать. А ещё, а ещё -- реализовали крутой тактический паттерн, а он дал тебе оверхед и нагрузку на инфу, ну счастье 🙂
Поэтому внедрение DDD затягивается или не внедряется.
LLM сейчас активно внедряется в разработку, 25% стартапов имеют 95% кода, написанного AI, 60% разработчиков юзают ллм, ттм растет, а время внедрять ддд, обучаться и обучать команду стало ещё меньше. Код генерится быстрее, чем ты успеваешь подумать.
Появились вайб-кодеры, пишут приложения с нуля, без знания ЯП и в целом технологий, и всё гуд, а кто может написать таким образом приложение и получит профит? Конечно же доменный эксперт. Тот самый, из которого мы пытаемся постоянно извлечь знания, чтобы их описать в программную модель. И логично, что он общается с LLM на том же языке, который ему привычен и этот язык является единым, с присущими терминами для домена.
Вот такой парадокс, ддд зашел с другой стороны
🔥12👍7🤔4😢1👀1
Наверное никто так сильно не бесит чем ии-агент, который не может решить задачу, если она чуть сложнее чем просто заменить текст 😠
Ну а что, интроверты мои, нужно выйти из зоны комфорта и попиздеть 1-1 с машиной
Вот простой алгоритм действий для формирования задачи для ии-агенту:
1) Начать с мозгового штурма. Не торопитесь с готовым планом, сначала обсудите все требования, вместе поизучайте текущую кодовую баз, уточните детали, проанализируйте варианты решения.
Тут если что, мы не на "вы", а имеется в виду ты и агент, мини-тимворк)
2) Четко сформулируйте ожидаемый результат. Определите, что именно должно получиться на выходе: какие файлы будут созданы или изменены, как они должны называться, и как проверить, что всё сделано правильно
3) Просим ИИ агента выявить потенциальные риски. ИИ поможет определить возможные проблемы и предложит пути их минимизации
4) Создаем подробный промпт для реализации, который включает в себя пошаговый план, с четкими шагами, которые можно выполнять без дополнительных пояснений. В курсоре есть режим Plan, например, для этого
5) Включаем проверки каждого шага. Чтобы гарантировать, что задача выполнена правильно, добавьте способы проверки результатов. Агенты хорошо справляются с curl запросами, неплохо пишут юниты или e2e тесты, с которыми можно проверить работу
Ну и всё, запускаем план в работу и идем наливать кофе, ведь впереди отладка на 100 часов😁
P.S. Если нужно, могу сделать более короткий или более технический вариант.
Ну а что, интроверты мои, нужно выйти из зоны комфорта и попиздеть 1-1 с машиной
Вот простой алгоритм действий для формирования задачи для ии-агенту:
1) Начать с мозгового штурма. Не торопитесь с готовым планом, сначала обсудите все требования, вместе поизучайте текущую кодовую баз, уточните детали, проанализируйте варианты решения.
Тут если что, мы не на "вы", а имеется в виду ты и агент, мини-тимворк)
2) Четко сформулируйте ожидаемый результат. Определите, что именно должно получиться на выходе: какие файлы будут созданы или изменены, как они должны называться, и как проверить, что всё сделано правильно
3) Просим ИИ агента выявить потенциальные риски. ИИ поможет определить возможные проблемы и предложит пути их минимизации
4) Создаем подробный промпт для реализации, который включает в себя пошаговый план, с четкими шагами, которые можно выполнять без дополнительных пояснений. В курсоре есть режим Plan, например, для этого
5) Включаем проверки каждого шага. Чтобы гарантировать, что задача выполнена правильно, добавьте способы проверки результатов. Агенты хорошо справляются с curl запросами, неплохо пишут юниты или e2e тесты, с которыми можно проверить работу
Ну и всё, запускаем план в работу и идем наливать кофе, ведь впереди отладка на 100 часов
P.S. Если нужно, могу сделать более короткий или более технический вариант.
Please open Telegram to view this post
VIEW IN TELEGRAM
1🔥14😁6❤5👍3
Чаще всего в проектах встречаю загрузку файлов в S3 через бекенд.
В типичном проекте в прод окружении файл проходит следующие этапы:
Если предполагается часто используемый функционал с загрузкой файлов, то это решение может стать узким горлышком. Особенно если речь заходит о больших файлах. В такой конфигурации большой latency за счет большого кол-во узлов, через которые проходит файл, сюда может добавить API Gateway, например, KrakenD или kong. Если проект на php-fpm - может забиться пул. И в целом приложение начинает потреблять больше ресурсов инфры
Этот подход называется Backend File Uploads.
Есть и второй -- Frontend File Uploads
В этом подходе, загрузка файлов происходит с фронта напрямую в S3 bucket. А для достижения безопасности используется Presigned Url (подписанная ссылка).
В этом подходе файл загружается в два этапа:
1) получение подписанной ссылки на бекенде
2) загрузка в S3 по подписанной ссылке
Преимущества подхода:
- производительность и масштабируемость -- это и увеличение пропускной способности и снижение потребления ресурсов инфры
- снижение latency -- файл проходит меньшее кол-во узлов
- открывается возможность загружать файлы до 5 ТБ
- скорость загрузки ограничивается клиентом и сервером S3. В backend upload нужно учитывать сетевые задержки + ограничение скорости между узлами в зависимости от их сети. Например, провайдер выделяет только 100 МБит/с
Реализацию можно поизучать в opensource, например, как в outline/outline загружаются прикрепляемые к документу файлы
https://github.com/outline/outline/blob/0d1c8490b5ffd0823a1f9ae7c5fa3438ed112eee/server/routes/api/attachments/attachments.ts#L137
В типичном проекте в прод окружении файл проходит следующие этапы:
frontend -> reverse proxy -> backend -> S3
Если предполагается часто используемый функционал с загрузкой файлов, то это решение может стать узким горлышком. Особенно если речь заходит о больших файлах. В такой конфигурации большой latency за счет большого кол-во узлов, через которые проходит файл, сюда может добавить API Gateway, например, KrakenD или kong. Если проект на php-fpm - может забиться пул. И в целом приложение начинает потреблять больше ресурсов инфры
Этот подход называется Backend File Uploads.
Есть и второй -- Frontend File Uploads
В этом подходе, загрузка файлов происходит с фронта напрямую в S3 bucket. А для достижения безопасности используется Presigned Url (подписанная ссылка).
В этом подходе файл загружается в два этапа:
1) получение подписанной ссылки на бекенде
2) загрузка в S3 по подписанной ссылке
Преимущества подхода:
- производительность и масштабируемость -- это и увеличение пропускной способности и снижение потребления ресурсов инфры
- снижение latency -- файл проходит меньшее кол-во узлов
- открывается возможность загружать файлы до 5 ТБ
- скорость загрузки ограничивается клиентом и сервером S3. В backend upload нужно учитывать сетевые задержки + ограничение скорости между узлами в зависимости от их сети. Например, провайдер выделяет только 100 МБит/с
Реализацию можно поизучать в opensource, например, как в outline/outline загружаются прикрепляемые к документу файлы
https://github.com/outline/outline/blob/0d1c8490b5ffd0823a1f9ae7c5fa3438ed112eee/server/routes/api/attachments/attachments.ts#L137
1🔥8❤7👍4💯1
Наткнулся на классное объяснение как устроен Claude Code , но по факту это про то, как работает любая агентская система
https://github.com/shareAI-lab/learn-claude-code
https://github.com/shareAI-lab/learn-claude-code
GitHub
GitHub - shareAI-lab/learn-claude-code: Bash is all you need - A nano claude code–like 「agent harness」, built from 0 to 1
Bash is all you need - A nano claude code–like 「agent harness」, built from 0 to 1 - shareAI-lab/learn-claude-code
🔥8❤3👍3⚡1
DX (DevEx) — то же самое, что UX, только для разработчиков.
Насколько удобно, продуктивно и приятно инженеру работать с проектом.
Обычно закладывают в это понятие онбординг, документацию, качество API/интерфейсов/контрактов, мониторинг системы, инструменты, которые оплачивает компания и которые используются в проекте (линтеры/hot reload/итд), CI/CD и так далее.
Хороший DX — разработчик тратит время на бизнес-задачу, а не на борьбу с инструментами.
В рф-компаниях я редко встречаю, чтобы об этом сильно беспокоились. Или чтобы даже была выделена отдельная роль под это дело.
Но мало того, что этим почти никто не занимается — так он теперь ещё и нахер не нужен))
Потому что пришли агенты, и пора пилить AX (Agent Experience) — дать агенту руки (MCP) и обучить его:
• доступность контента с сайтов через внедрение llms.txt / llms-full.txt
• MCP — доступ к БД / логам / трейсам / документации и т.д.
понятные коды ошибок
• markdown-first docs — документация, которая нормально парсится
• skills — навыки для агента. Как писать код, как его декомпозировать и упаковывать, и многое другое. У меня, например, полный набор по DDD в скиллах лежит
Классный опыт из моей практики: мы начали делать MCP-тулы прямо в приложении, которые дают доступ на чтение к БД / трейсам / Temporal.
Это дало очень крутой буст:
• Для разработчиков: локально дебажить фичу. Что-то пошло не так — скидываешь id агенту, тот идёт сам, всё проверяет: что где упало, с какой ошибкой, что пошло не так.
Оч круто работает на разработке агентских систем, когда нужно проследить трейс, чтобы понять, где агент свернул не туда, и предложить улучшения по промптам и инструментам.
• Для продактов: прямо в проде смотрят стату по продукту, аналитику, разбирают фидбек юзеров.
Кстати, отсюда же, по фидбеку, можно проработать негативные сценарии и получить предложения по улучшению кейсов. Интеграция с Claude/OpenCode — изи.
• Для саппорта/инцидентов: можно одним походом найти проблему — вместо 15-минутного серча руками.
А самый прикол — AX не заменяет DX, а развивает его.
В случае с DX разработчик готов смириться с проблемами и жить дальше. А без AX ты не получишь хорошей автоматизации.
Ну и для онбординга разработчика агент может стать классным AI Buddy по проекту 😉
Насколько удобно, продуктивно и приятно инженеру работать с проектом.
Обычно закладывают в это понятие онбординг, документацию, качество API/интерфейсов/контрактов, мониторинг системы, инструменты, которые оплачивает компания и которые используются в проекте (линтеры/hot reload/итд), CI/CD и так далее.
Хороший DX — разработчик тратит время на бизнес-задачу, а не на борьбу с инструментами.
В рф-компаниях я редко встречаю, чтобы об этом сильно беспокоились. Или чтобы даже была выделена отдельная роль под это дело.
Но мало того, что этим почти никто не занимается — так он теперь ещё и нахер не нужен))
Потому что пришли агенты, и пора пилить AX (Agent Experience) — дать агенту руки (MCP) и обучить его:
• доступность контента с сайтов через внедрение llms.txt / llms-full.txt
• MCP — доступ к БД / логам / трейсам / документации и т.д.
понятные коды ошибок
• markdown-first docs — документация, которая нормально парсится
• skills — навыки для агента. Как писать код, как его декомпозировать и упаковывать, и многое другое. У меня, например, полный набор по DDD в скиллах лежит
Классный опыт из моей практики: мы начали делать MCP-тулы прямо в приложении, которые дают доступ на чтение к БД / трейсам / Temporal.
Это дало очень крутой буст:
• Для разработчиков: локально дебажить фичу. Что-то пошло не так — скидываешь id агенту, тот идёт сам, всё проверяет: что где упало, с какой ошибкой, что пошло не так.
Оч круто работает на разработке агентских систем, когда нужно проследить трейс, чтобы понять, где агент свернул не туда, и предложить улучшения по промптам и инструментам.
• Для продактов: прямо в проде смотрят стату по продукту, аналитику, разбирают фидбек юзеров.
Кстати, отсюда же, по фидбеку, можно проработать негативные сценарии и получить предложения по улучшению кейсов. Интеграция с Claude/OpenCode — изи.
• Для саппорта/инцидентов: можно одним походом найти проблему — вместо 15-минутного серча руками.
А самый прикол — AX не заменяет DX, а развивает его.
В случае с DX разработчик готов смириться с проблемами и жить дальше. А без AX ты не получишь хорошей автоматизации.
Ну и для онбординга разработчика агент может стать классным AI Buddy по проекту 😉
1🔥10💯2👏1👨💻1