Обновить

Бэкенд

Сначала показывать
Порог рейтинга

📊 Multi-LLM Orchestrator v0.7.0: подсчёт токенов и мониторинг через Prometheus

На этой неделе вышел релиз v0.7.0 — завершена фаза observability. Теперь библиотека автоматически считает токены, оценивает стоимость запросов и экспортирует метрики в Prometheus. Всё работает из коробки.

Предыдущие релизы:

🔢 Автоматический подсчёт токенов

Библиотека автоматически считает токены для каждого запроса — и для prompt, и для completion. Используется tiktoken с fallback на оценку по словам.

from orchestrator import Router
from orchestrator.providers import GigaChatProvider, ProviderConfig

router = Router()
router.add_provider(GigaChatProvider(ProviderConfig(
    name="gigachat",
    api_key="your_key",
    model="GigaChat",
    verify_ssl=False
)))

# Токены считаются автоматически
response = await router.route("Напиши стихотворение про Python")

# Получаем статистику
metrics = router.get_metrics()
print(f"Total tokens: {metrics['gigachat'].total_tokens}")
print(f"  Prompt: {metrics['gigachat'].total_prompt_tokens}")
print(f"  Completion: {metrics['gigachat'].total_completion_tokens}")

Результат:

Total tokens: 75
  Prompt: 20
  Completion: 55

💰 Оценка стоимости запросов

Расчёт стоимости в реальном времени. Цены настраиваются в pricing.py (фиксированные значения для демонстрации — для production рекомендуется настроить под свои тарифы).

Результаты тестов с реальными провайдерами:

  • GigaChat: 75 tokens → ₽0.0750

  • YandexGPT: 105 tokens → ₽0.1575

  • Streaming: 342 tokens → ₽0.3420

📈 Интеграция с Prometheus

HTTP-эндпоинт /metrics в формате Prometheus. Метрики обновляются в реальном времени и готовы для scraping.

# Запускаем metrics server
await router.start_metrics_server(port=9090)

# Делаем запросы
await router.route("Привет!")

# Метрики доступны: http://localhost:9090/metrics

Экспортируемые метрики:

  • llm_requests_total — количество запросов

  • llm_request_latency_seconds — histogram латентности

  • llm_tokens_total — токены (prompt/completion)

  • llm_cost_total — стоимость в RUB

  • llm_provider_health — health status (0-1)

Готово для визуализации с Grafana.

🏗️ Архитектура

Router → Metrics Engine → Prometheus Exporter → Grafana.
Router → Metrics Engine → Prometheus Exporter → Grafana.

✅ Тестирование на реальных провайдерах

Все функции протестированы с production API и реальными ключами:

Подсчёт токенов:

  • GigaChat — 75 токенов, ₽0.0750 (стихотворение про Python)

  • YandexGPT — 105 токенов, ₽0.1575 (объяснение ML концепции)

Streaming-режим:

  • GigaChat — 342 токена, ₽0.3420 (генерация длинного текста)

Prometheus endpoint:

  • HTTP /metrics — корректный формат, все метрики экспортируются

Качество кода: 203 теста • 81% покрытие • mypy strict без ошибок


📦 Установка

pip install multi-llm-orchestrator==0.7.0

Новые зависимости: prometheus-client, tiktoken, aiohttp

🎯 Планы на v0.8.0

В следующей версии планируется добавить:

  • Динамическое обновление цен — автоматическое получение актуальных тарифов через API провайдеров

  • Provider-specific tokenizers — нативные токенизаторы для GigaChat и YandexGPT (вместо универсального tiktoken)

  • Расширенная аналитика латентности — percentiles p50, p95, p99 для детального анализа производительности

  • Cost analytics — уведомления о превышении бюджета, детальная разбивка расходов по моделям

  • Prometheus Pushgateway — поддержка push-модели для serverless окружений

Если используете библиотеку — пишите в комментариях, какие функции нужны вам!

🔗 Ссылки

Теги:
+1
Комментарии0

Required или нет?

Работая над одним из проектов, который недавно переехал из Framework 4.8 на Core 9, обнаружил множество самых разных вариантов использования модификатора required и атрибута Required, примерно каждый второй из которых был использован неправильно. Я написал это коллегам и хочу поделиться этим здесь. Это не обязательные правила, но сильно упрощают работу с кодом.

Небольшое пояснение

Атрибут Required нужен для проверки входящих преимущественно строковых данных в эндпоинтах. Возвращает ошибку, если значение null или пустая строка для строк (если не отключено параметром AllowEmptyStrings). Работает в Runtime. Также применяется в Entity Framework в подходе code-first но с включением опции <Nullable> в csproj про эти случаи можно забыть, сделав код чище.

Модификатор required нужен для обязательного указания значений полей при создании класса. Работает в Compile-time.

Примеры использования

// имеем класс с required полем
public class Example
{
    public required string Name { get; set; }
}

// пытаемся создать экземпляр в коде
var example1 = new Example();  // будет ошибка при попытке сборки проекта
var example2 = new Example { Name = string.Empty };  // тут ошибки не будет

// Вывод: модификатор required нужен для разработчика
// имеем класс с полем, у которого атрибут Required
public class Example
{
    [Required]
    public string Name { get; set; }
}

// пытаемся создать экземпляр в коде
var example = new Example();  // проект спокойно собирается

// имеем эндпоинт в контроллере
public IActionResult PostMethod([FromBody] Example model) => Ok();

/* передаём в теле запроса:
{}
или
{"Name": null}
или
{"Name": ""}
или
{"Name": "   "}
Получаем BadRequest с текстом ошибки. */

// передаём в теле запроса: {"Name": "name"}. Получаем OK.

// Вывод: атрибут Required нужен для пользователя

Как стоит и не стоит использовать.

public class BadExample
{
    public required string Field1 { get; set; } // 1
    
    public required string? Field2 { get; set; } // 2
    
    [Required]
    public required string Field3 { get; set; } // 3

    [Required]
    public string? Field4 { get; set; } // 4

    [Required]
    public int Field5 { get; set; } // 5

    public required int Field6 { get; set; } = 10; // 6
      
    public required List<int> Field7 { get; set; } // 7
}
  1. Ошибка, если класс используется как входящий параметр в эндпоинте. Соответственно, не стоит использовать, если десериализуем в него.

  2. Либо required, либо nullable.

  3. Надо выбрать одно из двух в зависимости от места использования.

  4. Либо Required, либо nullable. Тут даже AllowEmptyStrings = true не поможет.

  5. Required используется для строк. Но есть нюанс (*).

  6. Не нужно использовать required со значением по умолчанию.

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

public class GoodExample
{
    public required string Field1 { get; set; } // 1
    
    [Required]
    public string Field2 { get; set; } = null!; // 2
    
    public string? Field3 { get; set; } // 3

    public int Field4 { get; set; } // 4

    public List<string> Field5 { get; set; } = []; // 5
}
  1. Хорошо где угодно за пределами эндпоинтов и десериализации, а значение не может принимать null.

  2. То что нужно для эндпоинта.

  3. Поле nullable. Поэтому никаких required.

  4. Не используем атрибут Required с не строками. Но есть нюанс (*).

  5. Избегаем использование required, проинициализировав коллекцию.

* - если передаётся json, в котором явно указано значение null ({"Field4": null}), то использование атрибута Required вернёт BadRequest.
Если же в json поле было опущено, то будет присвоено значение по умолчанию.

Надеюсь, это поможет сделать код чище и избежать неоднозначностей.

Теги:
+1
Комментарии0

Компьютерное зрение для кода: что PVS-Studio разглядел в OpenCV

Что общего у компьютерного зрения и статического анализа? Оба ищут смысл в данных. OpenCV находит образы среди миллионов пикселей, а PVS-Studio — ошибки среди тысяч строк кода. Изучим же исходники крупнейшей библиотеки компьютерного зрения.

На примере 14 фрагментов кода из OpenCV предлагаю посмотреть, как статический анализ помогает избежать попадания багов в релиз и облегчить жизнь разработчикам.

Давайте посмотрим на кусок кода из проекта:

template<typename T>
struct Ptr : public std::shared_ptr<T>;
// ....
Ptr<FlannNeighborhoodGraph> FlannNeighborhoodGraph::create(....) 
{           
    return makePtr<FlannNeighborhoodGraphImpl>(....);
}

void Utils::densitySort (const Mat &points, int knn, 
                         Mat &sorted_points, std::vector<int> &sorted_mask) 
{
  // ....
  FlannNeighborhoodGraph &graph =                                      // <=
                         *FlannNeighborhoodGraph::create(....);

  std::vector<double> sum_knn_distances (points_size, 0);
  for (int p = 0; p < points_size; p++) {
    const std::vector<double> &dists = graph.getNeighborsDistances(p);
    for (int k = 0; k < knn; k++)
      sum_knn_distances[p] += dists[k];
  }
  // ....
}

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

  1. Функция create создаёт и возвращает умный указатель на тип FlannNeighborhoodGraphImpl, и его счётчик ссылок на объект равен единице;

  2. Создаётся ссылка graph на значение этого умного указателя, при этом счётчик ссылок на объект не изменяется;

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

  4. В цикле for происходит обращение к невалидной ссылке.

В итоге код, который казался правильным, приводит к неопределённому поведению. Кроме того, эту проблему находит не только PVS-Studio, но и санитайзер. Пруф.

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

std::vector<double> sum_knn_distances (points_size, 0);

{
  // get neighbors
  auto graph = FlannNeighborhoodGraph::create(....);

  for (int p = 0; p < points_size; p++) {
    const std::vector<double> &dists = graph->getNeighborsDistances(p);
    for (int k = 0; k < knn; k++) 
      sum_knn_distances[p] += dists[k];
  }
}

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

Хотите узнать больше?

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

Теги:
+3
Комментарии1

20 демо-уроков, которые нельзя пропустить бэкенд-разработчикам

Привет, Хабр. Сегодня делимся подборкой открытых уроков, которые пройдут в Otus в декабре. Уроки проводят преподаватели курсов в формате живых вебинаров — это шанс не только получить нужные знания, но и задать свои вопросы экспертам. Участие бесплатное (нужно только зарегистрироваться). Присоединяйтесь!

Полное расписание бесплатных демо-уроков смотрите в календаре.

Теги:
+4
Комментарии0

Слои валидации

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

Валидация на клиенте (если он есть)

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

Структурная валидация

Это валидация, которая, обычно, происходит на уровне самого фреймворка или в самом начале цикла обработки запроса. Для этого данные прогоняются через валидаторы json схемы, которая в идеале генерируется из openapi спеки. В самых деревянных случаях ручками (так делать не надо). Что важно понимать, это не доменная валидация (бизнес правила). Да тут можно проверить формат, наличие/отсутствие, но нельзя и не правильно пытаться проверять уникальность, выполнение каких-то условий, например количества денег на счету и тому подобное.

Кстати с точки зрения кодов ответа, на этом уровне несовпадение со схемой воспринимается не как ошибка валидации, а как неверный запрос с неверной структурой, а это код ответа 400.

Доменная валидация

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

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

В случае провала такой валидации, в api принято возвращать код 422

Валидация на уровне базы данных

Все предыдущие уровни не могут дать 100% гарантий, особенно учитывая, что данные в базе обновляются далеко не только по запросам снаружи. Поэтому есть вещи, которые обязательно делать на уровне базы данных. Сюда относятся уникальные индексы, внешние ключи (если делаете их), nullable, ограничение по длине и т.д. Технически многие базы данных позволяют писать кастомные валидации, которые соблазнительно использовать как доменную валидацию. Не надо этого делать :)

Проверка корректности данных

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

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

Больше про разработку в моем телеграм-канале Организованное программирование

Теги:
+3
Комментарии0

Где учиться кибербезопасности?

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

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

Защита информации. Шифрование, резервное копирование и защита от утечек данных.

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

Анализ вредоносных программ. Анализ вредоносного ПО (вирусы, трояны, программы-вымогатели) и разработка защиты.

Кибербезопасность. Общее направление: защищаем сети, системы и приложения от атак.

Системное администрирование. Администрирование серверов, сетей, IT-инфраструктуры, настройка прав и обеспечение стабильности работы систем.

Мониторинг сетей. Мониторинг сетевой активности, выявление аномалий и угроз, настройка систем обнаружения вторжений и анализ трафика.

→ Еще больше полезных курсов на нашей витрине

Теги:
+1
Комментарии0

Совет по Joomla: использовать выключенное состояние для кнопок в списках элементов админки - listCheck().

Мы добавляем в тулбар панели администратора Joomla некую кнопку, которая что-то делает со списком id выделенных элементов и ajax-запросом отсылаем их в свой плагин. Но нам надо предупредить нажатия на кнопку в тех случаях, когда ни один элемент не был выбран. Для этого можно написать свою проверку на js. А можно воспользоваться встроенной в Joomla.

Добавить кнопку в тулбар Joomla 6.

use Joomla\CMS\Toolbar\Button\BasicButton;
use Joomla\CMS\Language\Text;

// ниже по коду, где-нибудь в плагине на onAfterDispatch()
// Предварительно проверяем в каком компоненте мы находимся по option из $app->getInput()
// пример из плагина, поэтому $this->getApplication()
$app = $this->getApplication();
// Берём текущий тулбар
$toolbar = $app->getDocument()->getToolbar('toolbar');

// Создаём кнопку
$button = (new BasicButton('send-to-indexnow'))
    ->text(Text::_('PLG_WTINDEXNOWSWJPROJECTS_BUTTON_LABEL'))
    ->icon('fa-solid fa-arrow-up-right-dots')
    ->onclick("window.wtindexnowswjprojects()");

// Добавляем кнопку в тулбар
$toolbar->appendButton($button);

Заблокировать кнопку тулбара Joomla, если не выбраны элементы списка.

Теперь нам надо проверить находимся ли мы в списке. Делаем это по view из $app->getInput().

if(in_array($app->getApplication()->getInput()->get('view'),
            ['categories','documentation','projects','versions'])
  ) {
        $button->listCheck(true);
}

И если мы в списке - используем метод $button->listCheck(true), который сделает проверку за нас. Если ни один элемент не выбран - кнопка в тулбаре Joomla будет заблокирована и JS-обработчик не будет вызван. Этот метод есть у всех классов кнопок, наследующих класс \Joomla\CMS\Toolbar\ToolbarButton.

Теги:
0
Комментарии0

Хочу немного поплакаться о поиске работы в IT…

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

Я — .NET backend-разработчик с опытом 6+ лет. Казалось бы, с таким бэкграундом я должен быть для HR вполне «лакомым кусочком». Но реальность оказалась совсем другой.

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

Складывается впечатление, что компании ищут кандидатов, которые совпадают с их стеком на 100%, и им плевать, что ту или иную технологию можно подтянуть за пару вечеров. Иначе я не могу объяснить, почему в вакансиях требуют конкретные библиотеки вроде Dapper или Autofac, которые изучаются буквально за вечер.

Есть ChatGPT и Gemini — хочешь, они разжуют любую тему. Но если в резюме не указал какую-то библиотеку — красный флаг. Отказ.
«Мы лучше ещё пару месяцев поищем кандидата, который совпадает на 100%, чем возьмём тебя». Примерно так это и выглядит.

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

Про технические интервью я вообще молчу. Иногда спрашивают такую дичь, которая в реальной работе не нужна от слова «совсем». Например, у меня спросили разницу между структурой и классом. Я ответил по классике: копирование, расположение в памяти, наследование. Но от меня ожидали услышать ещё про блок синхронизации в объекте и почему структуры нельзя использовать в lock.
Может, я и читал это когда-то у Шилдта или Троелсена, но в реальной работе это знание ни разу не пригодилось.

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

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

Да и честно говоря, всё равно все на собеседованиях врут. Потому что попробуй скажи правду — например:
«Мне надоело бесконечно ковыряться в легаси, поэтому я решил уйти».
Мгновенно получишь красный флаг.

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

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

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

В итоге фильтруют не тех, кого нужно, а просто создают лишние барьеры.

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

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

Теги:
+11
Комментарии6

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

API First и Богатая доменная модель не являются взаимоисключающими понятиями, если правильно выстроить процесс и архитектуру.

Главная проблема: API First фокусируется на контрактах внешнего мира, что часто приводит к созданию DTO/Model классов, которые являются чистыми структурами данных без поведения (анемичная модель).

API First — это про контракты, а не про реализацию. Не позволяйте инструментам кодогенерации диктовать структуру вашей доменной модели!

Правильный workflow:

1. API Design First: Создайте/обновите OpenAPI спецификацию.

2. Генерация DTO: Сгенерируйте или создайте классы для API‑контрактов.

3. Projection First: Спроектируйте доменную модель независимо от DTO, фокусируясь на поведении и инвариантах.

4. Создание Mapping Layer: Напишите преобразователи между DTO и Domain Objects.

5. Реализация Application Service: Тонкий слой, который координирует работу домена.

Стек технологий, который помогает:

MapStruct: Для автоматизации маппинга между DTO и Domain
JUnit 5: Для тестирования доменной логики
OpenAPI Generator: Для автоматической генерации DTO из спецификации
ArchUnit: Для проверки архитектурных правил (например, запрет на анемичные модели)

// ArchUnit тест, проверяющий, что в доменных классах есть поведение
@ArchTest
static final ArchRule domain_models_should_have_business_methods = 
    classes()
        .that().resideInPackage("..domain..")
        .and().areAnnotatedWith(Entity.class)
        .should().containAnyMethodsThat(
            DescribedPredicate.describe(
                "have business methods", 
                method -> !method.getName().startsWith("get") && 
                         !method.getName().startsWith("set")
            )
        );
Теги:
-1
Комментарии0

👉 Декабрь в разгаре, и у нас есть вакансии присоединяйтесь к SSP SOFT 💻


Говорят, что в декабре никто не нанимает, но это не про нас! Приглашаем опытных специалистов присоединиться к команде SSP SOFT 🌐

✨ У нас в SSP SOFT — проекты, которые удивлят своей сложностью, а затем станут точками роста.
✨ С первого дня вы не одни: за вами закрепляется наставник, который помогает входить в работу спокойно и без лишнего напряжения.
✨ Карьерный скачок здесь реален: наш Проектный офис — это не просто управление, а среда, которая ускоряет профессиональное развитие.
✨ Вы сами выбираете формат: работайте удаленно, приходите в офис в Москве (ЦАО) или в Томске — или комбинируйте (по согласованию с тимлидом).
✨ Мы не пропагандируем культ переработок — рабочие процессы настроены, а личное время и забота о здоровье уважаются.

Бонусы тоже есть:
🎁 ДМС (включая стоматологию) для штатных сотрудников
🎁 обучение за счет компании
🎁 бонусные программы
🎁 общие ивенты — от онлайн-квизов до выездных сборов

📢 Вот кого мы ищем в декабре (Подробности о вакансиях читай на ХХ.ру):

  • Python developer (LLM)

  • С# Разработчик (интеграции с Lekton)

  • Администратор второй линии поддержки/Devops специалист

  • Аналитик- консультант 1С

  • Аналитик 1С (Middle)

  • Системный аналитик (Senior)

  • Аналитик 1С (Senior)

  • Разработчик Directum

👉 Присылайте резюме напрямую в ЛС нашему HR Lead Алине. Не забудьте добавить сопроводительное письмо с ключевой фразой «Нашел(ла) вас на Хабре».

Спасибо за интерес к нашим вакансиям и желаем успеха на собесе )

Теги:
0
Комментарии0

Как оптимизировать бюджет на интеграции в 2026 году: практики и инструменты для DATAREON

11 декабря в 12:00 проведём практический вебинар для ИТ-директоров и архитекторов, которые хотят сократить стоимость интеграций на DATAREON и избавиться от рутинных трудозатрат.

Чаще всего для интеграций, особенно на предприятиях с большим количеством 1С, мы используем DATAREON Platform. Удобный 1С-коннектор, широкое распространение и доступная стоимость лицензий делают его одним из самых популярных решений для интеграционных задач.

Однако цена лицензии — лишь часть картины: важную роль играет также стоимость внедрения и дальнейшего сопровождения.

Сколько на самом деле стоит интеграционный проект? 

Какие существуют реальные способы снизить расходы без потери качества? 

Об этом мы поговорим на вебинаре.

Что покажем на вебинаре

Технический директор ИТ-интегратора «Белый код» Сергей Скирдин разберёт реальные кейсы и продемонстрирует, какие технические решения ускоряют работу и снижают стоимость интеграционного проекта:

  • Использование API DATAREON для создания типов данных. 

  • Генерация обработчиков 1С.

  • Отладка обработчиков и функций без боли.

  • Использование формата Enterprise Data как основы для интеграции типовых объектов 1С

  • Заглянем в будущее — покажем, как с помощью ИИ-агента можно дорабатывать интеграционный проект в DATAREON.

Какой эффект получит бизнес

✔ Снижение стоимости интеграций. Автоматизация процессов уменьшает объём ручной работы и бюджет проекта.
Быстрый запуск интеграций. Готовые инструменты ускоряют разработку и ввод в эксплуатацию.
Масштабируемость интеграций. Единые стандарты позволяют быстро подключать новые системы без усложнения архитектуры.

Кому будет полезно

  • Руководителям, которые выбирают подрядчика для внедрения интеграционной шины и хотят объективно оценить подходы и стоимость.

  • Архитекторам перерабатывающим технологию замены существующего интеграционного решения на DATAREON Platform

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

Регистрируйтесь и приходите!
Принять участие

Теги:
0
Комментарии0

Участие в нескольких проектах снижает результаты работы — так ли это?

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

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

Почему спрашиваю?

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

Вопрос к сообществу:

Правда ли, что разработчик, участвующий в нескольких проектах part-time, будет менее эффективен, допустит больше багов и в целом ухудшит качество релизов? Или это миф, и всё зависит от процессов, коммуникации и личной организованности?

Теги:
+5
Комментарии17

Этот финт сэкономит вам время и нервы

Хочу написать о финте, который позволит вам сохранить нервы и сэкономить время. Правда некоторые (многие, почти все) впадают в ступор от него. Поэтому тут использована КДПВ с поста. Я наверно чувак слева.

А именно добавление первым условием if единицы:

if (1
    && $cond1
    && $cond2
    && $cond3
)

Использование финта дает нам возможность:
1. Быстро выключать фичу заменой 1 на 0:

if (0
    && $cond1
    && $cond2
    && $cond3
)

2. Быстро выключать любое условие в PhpStorm через горячие клавиши:

if (1
//    && $cond1
    && $cond2
    && $cond3
)

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

if (
    /*$cond1
    &&*/ $cond2
    && $cond3
)

Или такую:

if (
    $cond2
    && $cond3
)

3. Быстро добавлять новое первое условие:

if (1
    && $cond2
    && $cond3
)

легко превращается в:

if (1
    && $cond1 // в изменениях одна строка
    && $cond2
    && $cond3
)

4. Быстро дублировать любое условие.

5. Быстро менять порядок условий.

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

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

Теги:
+10
Комментарии14

Ближайшие события

CWE-295. А Вы точно выясняете все способы отключения SSL-проверок в коде?

CWE-295 — неправильная проверка сертификата, включая игнорирование ошибок цепочки доверия. Близкие CWE: CWE-296 (Improper Certificate Validation in TLS) и CWE-297 (Improper Hostname Verification).

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

Давайте проверим насколько хорошо Вы контролируете внешние взаимодействия Вашего кода со сторонними приложениями при помощи SAST/линтеров.

Не важно о чем код: запуск удаленной команды на Power Shell, запуск cURL, запуск Node.js и т.д. Если в Вашем коде имеется обращение к стороннему ресурсу, вполне вероятно, хотя бы какая-нибудь строка вызова составлена небезопасно, коробочные правила используемых Вами SAST не могут покрыть все кейсы, особенно, в неподдерживаемом файле.

Сами регулярные выражения для проверки Вашего кода:

{
"r1": "(?i)((ssl|session)[.]{0,1}verify[: -_\"'=]{1,6}false['\"\\]{0,3}|^ *ssl *[: -_\"'=]{1,6} *false|disableTLS['\": ]{2,4}true|requests.get(.*verify[ ]{0,5}= *False.*)|verify=False|requireSSL *}= *('|\")false('|\")|-(SkipCACheck|SkipCNCheck|SkipRevocationCheck))"
}
(?i)(-allowUntrusted[ \"\']{0,3}=[ \"\']{0,3}true|tls(\.|)Enabled[ ]{0,3}(=|:)[ ]{0,3}false|strict-ssl false|strict-ssl[ ]{1,3}=[ ]{1,3}false|--insecure-skip-tls-verify|--skip-tls-verify|--(insecure|allow(|-)untrusted)( |\r?$)|--insecure-registry|http\.ssl\.(insecure|ignore\.validity\.dates)[ \"\']{0,3}=[ \"\']{0,3}(1|true)[ \"\']{0,1}|<AllowUntrustedCertificate>[ ]{0,2}True[ ]{0,2}<\/AllowUntrustedCertificate>|(skipCertCheck|sslmode|allow(-|)Untrusted(|Certificate)|NODE_TLS_REJECT_UNAUTHORIZED)[ \"\']{0,3}(:|=)[ \"\']{0,3}(0|true|disable)[ \"\']{0,1}|--no-check-certificate https:\/\/|urllib3\.disable_warnings|--[ ]{1,3}disable-tls[ ]{1,3}true|--[ ]{1,3}secure-http[ ]{1,3}false|connection\.ssl\.enabled:[ ]{0,3}("|)false("|)|ssl-errors[ ]{0,3}(=|:)[ ]{0,3}true|rejectUnauthorized(|:).*false|TrustServerCertificate *= *False|isAllowInsecureProtocol *= *true|--use_tls *= *false|('|")use_tls('|"), *false|isAllowInsecureProtocol *= *true|NODE_TLS_REJECT_UNAUTHORIZED:? *\n?\r? *value: *("|'|)0("|'|)|tls: *\n?\r?(.*#.*\n?\r?)? *enabled: *("|'|)false("|'|)|Verify-Peer *("|'|=|)false|http\.ssl\.allowall[ \"\']{0,3}=[ \"\']{0,3}(0|false)[ \"\']{0,1}|--disable-host-check|ENABLE_TLS *(:|=) *false|"insecureTls": *("|'|)true("|'|)|((Enable|use)SSl("|'|) *(:|=) *(false|0))|"tlsConfigurationType": "(?!require).*"|host_key_checking *= *False|ANSIBLE_GALAXY_IGNORE *= *(yes|true)|ignore_certs *= *(yes|true)|\s+tls:\s*false|\s+ssl:(\s*enabled:)? *false)

На случай, если длинные строки поломаны блоком кода (ели вставил в пост):

  1. https://regex101.com/r/7iJCxw/1

  2. https://regex101.com/r/SGOz1L/1

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

Пример работы
Пример работы
Теги:
0
Комментарии0

Периодически натыкаюсь в YouTube на видео с названиями типа «Стоит ли идти в IT в 2025 году».

И видео / статьи с подобным содержанием меня всегда немного коробят...

По поводу «входа в IT» и в другие сложные специальности у меня есть совершенно чёткое и однозначеное мнение — если вы хотите освоить эти специальности ТОЛЬКО из конъюнктурных соображений: «это модно и там много платят» — то лучше не надо.

Пожалуйста, не идите в IT — вам там будут не рады, я во всяком случае точно буду не рад. «You are not welcome here!»

Идти в сложные профессии можно и нужно только в одном случае — если вам это ДЕЙСТВИТЕЛЬНО ИНТЕРЕСНО.

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

А если же вам это действительно интересно, то вы в принципе не задаётесь и никогда не будете задаваться вопросом «стоит ли».

Вы пойдёте — независимо от текущего состояния рынка труда, от зарплат, от коньюнктуры, от того «заменит ли всех ИИ».

Вам всё это НЕ ВАЖНО, т.к. все эти факторы — преходящие и уходящие и идёте вы не за этим.

А если вам уже за 35, вы никогда не интересовались программированием и вдруг услышали рекламу «пройди наш 3-х-месячный курс и зарабатывай от 200k в месяц» — не верьте. Лучше не мучить ни себя, ни индустрию, ни будущих коллег...

Теги:
+14
Комментарии6

Obsidian Base16 Default Dark. Брутализм для глубокой работы

Создал инженерную тему для тех, кто относится к Obsidian как к IDE для мыслей.

Особенности:

  • Строгая визуальная иерархия

  • Семантическое цветокодирование

  • Чистая терминальная эстетика

Устанавливается из интерфейса Obsidian.
Рекомендованные шрифты и детали настройки – на GitHub.

Теги:
+1
Комментарии0

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

Предыдущая статья была вынесена в черновики мной, однако если вкратце, StingrayTV Alice - это попытка интегрировать ресиверы Триколора на базе платформы StingrayTV с сервисом "Дом с Алисой". Это позволяет управлять ресивером через Алису, и интегрировать его в общий умный дом. Проект пережил несколько доработок, и сейчас там используется Keycloak, Spring Boot 4, и другие самые современные технологии. Также было сделано множество улучшений кодовой базы, что позволило избавиться от лишнего кода, и улучшить стабильность и производительность данного гейтвея.

Keycloak: теперь нормальная аутентификация - это реальность

Изначально планировалась аутентификация по физическому присутствию пользователя за консолью сервера. Однако реализовать это достаточно было нетривиально, и поэтому принято решение использовать уже готовый сервер аутентификации - а именно Keycloak. Оно даёт более гибкий контроль за процессом аутентификации, а также является проверенным и готовым решением для реализации OAuth2.

Куча рефакторинга

Проект подвергся обширному рефакторингу - как те, которые я сделал на всех своих пет-проектах (в частности, перевод проектов на Spring Boot 4, а также улучшения по части CI/CD в проектах - теперь там реализован полноценный пайплайн, который обеспечивает высокий уровень консистентности всего цикла), так и постепенная работа над чисткой кода (при помощи самых разных линтинг-инструментов - начиная от встроенных инструментов OpenIDE, и заканчивая SonarQube for IDE и Explyt Spring). Это позволило обеспечить гораздо большую чистоту и сопровождаемость кода.

В частности:

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

  2. Убрали использование Preferences API для хранения нужных ключей для старого механизма аутентификации - Keycloak куда лучше во всём.

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

Итоги

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

Мой сайт-резюме
Мой GitHub

Теги:
+2
Комментарии0

Привет, Хабр! Тем, кому регулярно приходится заглядывать в etcd — будь то QA, поддержка или разработчики — хорошо знакома ситуация, когда нужно разобраться с неожиданным состоянием сервиса, проверить конфиги или найти застрявший лок. И каждый раз всё сводится к одному: копировать ключ, запускать etcdctl get, читать многострочный JSON в терминале, ошибаться в пути… и в какой-то момент понимаешь, что это однообразие выматывает больше, чем сама проблема.

Поэтому наш коллега из Хайстекс сделал небольшой TUI-инструмент, который заметно упрощает работу с etcd и делает её куда дружелюбнее для тех, кто каждый день копается в окружениях. Он снимает рутину etcdctl, даёт привычную “каталожную” навигацию, подсвечивает скрытые _-ключи, позволяет комфортно открывать большие конфиги и помогает разбираться с локами, которые любят появляться в самых неожиданных местах.

Если вы в QA, поддержке или просто часто работаете с etcd, этот инструмент легко сэкономит вам время и нервы.

Статью можно прочитать здесь.

Теги:
0
Комментарии0

📊 Multi‑LLM Orchestrator v0.6.0: метрики провайдеров и умный роутинг

На этой неделе на Хабре вышла статья про Multi-LLM Orchestrator — библиотеку для работы с российскими LLM через единый интерфейс. Сегодня релиз v0.6.0 добавляет метрики провайдеров и стратегию роутинга на основе health status.

Автоматический сбор метрик

Роутер отслеживает каждый запрос и собирает статистику по провайдерам. Latency, success rate, количество ошибок — всё фиксируется без дополнительной настройки.

from orchestrator import Router
from orchestrator.providers import GigaChatProvider, ProviderConfig

router = Router(strategy="best-available")
router.add_provider(GigaChatProvider(
    ProviderConfig(name="gigachat", api_key="...", model="GigaChat")
))

# После нескольких запросов
metrics = router.get_metrics()
print(f"{metrics['gigachat'].avg_latency_ms:.0f}ms")
print(f"Health: {metrics['gigachat'].health_status}")

Система отслеживает среднюю задержку и rolling average по последним 100 запросам. Если провайдер начинает деградировать, это видно сразу.

Health status провайдеров

Роутер классифицирует каждого провайдера автоматически:

  • healthy — error rate меньше 30%, стабильная latency

  • degraded — error rate 30-60% или задержки растут

  • unhealthy — error rate выше 60%

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

Стратегия best-available

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

router = Router(strategy="best-available")
router.add_provider(gigachat_provider)
router.add_provider(yandexgpt_provider)

# Роутер выбирает самого здорового и быстрого
response = await router.route("Вопрос")

Если GigaChat деградирует до 3 секунд, а YandexGPT стабильно отвечает за 500ms — роутер переключится на YandexGPT.

Тестирование на боевых API

Запущена серия тестов с реальными запросами к GigaChat и YandexGPT. Результаты подтверждают стабильность системы метрик.

Метрики провайдеров: GigaChat vs YandexGPT (fallback-тест)
Метрики провайдеров: GigaChat vs YandexGPT (fallback-тест)

Первый тест показал базовую работу: GigaChat отвечает за ~1.7 секунды со 100% success rate. Второй тест проверил fallback при ошибке авторизации — роутер переключился на YandexGPT без потери запроса. Третий тест подтвердил корректность метрик при streaming-запросах.

YandexGPT показал стабильные 500-700ms на серии из шести запросов. GigaChat медленнее (~1.7s), но это ожидаемо для более тяжёлой модели. Success rate обоих провайдеров — 100%.

Structured logging

Каждый запрос логируется в структурированном формате с полями provider, model, latency_ms, streaming, success. Интеграция с Prometheus или Grafana требует только парсинг JSON

# При успехе
logger.info("llm_request_completed", extra={
    "provider": "gigachat",
    "latency_ms": 1723
})

# При ошибке
logger.warning("llm_request_failed", extra={
    "provider": "yandexgpt",
    "error_type": "RateLimitError"
})

Ссылки

Следующий релиз (v0.7.0) добавит token-aware метрики: подсчёт токенов, расчёт tokens/s, cost estimation и экспорт в Prometheus.

Если используете российские LLM в production — буду рад обратной связи в комментариях.

Теги:
0
Комментарии0

Запуски 2025: программирование

В 2025 году мы запустили 25+ курсов и тарифов для ИТ-специалистов. В этой подборке собрали новые программы по разработке и архитектуре.

«Rust для действующих разработчиков» — 4 месяца
После курса сможете использовать Rust как основной стек и создавать отказоустойчивые системы с высоким уровнем безопасности.

«SRE — обеспечение надёжности систем» — 4 месяца
Освоите практики и подходы SRE, наладите мониторинг инфраструктуры и приложений, чтобы повышать стабильность и улучшать бизнес-процессы.

«DevSecOps: безопасная разработка и эксплуатация» — 3 месяца
Разберём, как выявлять и устранять уязвимости на ранних этапах и снижать риски на всём жизненном цикле приложения — от планирования до эксплуатации.

«MLOps для разработки и мониторинга» — 5 месяцев
Освоите принципы MLOps, чтобы ускорять и безопасно выводить ML-модели в продакшн, настраивать стабильную инфраструктуру и улучшать взаимодействие команд.

«Микросервисная архитектура» — 3 месяца
Научитесь проектировать и реализовывать масштабируемые и отказоустойчивые микросервисные системы. Разберёте паттерны SAGA и Transactional Outbox, подход DDD и другие инструменты.

«Мидл разработчик C++» — 4,5 месяца
Прокачаете владение современным C++: лучшие практики и идиомы языка, работа с диапазонами, библиотечными возможностями, асинхронностью и многопоточностью.

«Продвинутая разработка на C# и .NET» — 5 месяцев
Научитесь писать безопасный высокопроизводительный код, разбирать сложные продакшн-задачи, внедрять observability (логи, метрики, трейсы) и использовать современные возможности .NET.

Теги:
0
Комментарии0