惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

云风的 BLOG
云风的 BLOG
P
Privacy International News Feed
Vercel News
Vercel News
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - 叶小钗
F
Fortinet All Blogs
Security Archives - TechRepublic
Security Archives - TechRepublic
L
LINUX DO - 最新话题
AWS News Blog
AWS News Blog
Engineering at Meta
Engineering at Meta
Attack and Defense Labs
Attack and Defense Labs
Recent Announcements
Recent Announcements
Recent Commits to openclaw:main
Recent Commits to openclaw:main
PCI Perspectives
PCI Perspectives
Cloudbric
Cloudbric
AI
AI
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
IT之家
IT之家
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
J
Java Code Geeks
M
MIT News - Artificial intelligence
Cisco Talos Blog
Cisco Talos Blog
V2EX - 技术
V2EX - 技术
Webroot Blog
Webroot Blog
Microsoft Security Blog
Microsoft Security Blog
Cyberwarzone
Cyberwarzone
博客园 - 聂微东
G
Google Developers Blog
W
WeLiveSecurity
罗磊的独立博客
P
Privacy & Cybersecurity Law Blog
阮一峰的网络日志
阮一峰的网络日志
A
About on SuperTechFans
WordPress大学
WordPress大学
The GitHub Blog
The GitHub Blog
T
Tailwind CSS Blog
V
Visual Studio Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
S
Secure Thoughts
Apple Machine Learning Research
Apple Machine Learning Research
Hugging Face - Blog
Hugging Face - Blog
Google DeepMind News
Google DeepMind News
Google DeepMind News
Google DeepMind News
雷峰网
雷峰网
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
F
Full Disclosure
Blog — PlanetScale
Blog — PlanetScale
The Last Watchdog
The Last Watchdog
P
Proofpoint News Feed

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
50% пишу и удаляю код, 40% объясняю, что таблица – это не эксель, 10% жду логи
Oladiy · 2026-04-16 · via Все публикации подряд на Хабре

В СИБУРе я отвечаю за разработку сервиса видеоаналитики. По сути, это система, которая в реальном времени обрабатывает видеопоток с камер на нефтехимических предприятиях. Камеры следят за технологическим процессом: считают продукцию на конвейере, определяют уровень жидкости в колбах, проверяют наличие касок на людях. Результат нужен в течение пяти секунд, потом уже поздно.

Про техническую сторону, как мы оптимизировали пайплайн и добились ускорения в 28 раз без GPU, я уже рассказывал на Хабре. Но есть вторая часть работы, и она занимает не меньше времени, чем код. Это коммуникация.

Наши заводы расположены от Твери до Амура. Серверы стоят в закрытом контуре, доступ к ним имеет ограниченное количество людей, и зачастую подключиться к машине напрямую из офиса не представляется возможным. Заказчики на производстве и мы, разработчики, говорим на разных профессиональных языках. Я сейчас не про английский и русский, а про то, что одно и то же слово может означать совершенно разные вещи для программиста и для человека на заводе. Если этот перевод не делать, технически нормальное решение просто не будет использоваться.

Кто с кем разговаривает

В нашей команде видеоаналитики три роли. Разработчики (нас двое, я как техлид и ещё один бэкенд-разработчик), инженеры-девопсы (три человека, каждый сопровождает свои кейсы на нескольких заводах) и ML-разработчик. 

Отдельно есть заказчики на производстве (это могут быть разные люди, от операторов до руководителей направлений).

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

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

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

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

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

Одно слово, два значения

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

Простой пример: слово «таблица». Для меня таблица это реализация сущности в базе данных. Строки, столбцы, у каждого столбца своё название на латинице, свой тип данных. Для человека с производства таблица это то, что он видит глазами: Excel-файл или форма на сайте с полями на русском языке.

Вот реальная ситуация. Допустим, в компании есть две системы, А и Б, они между собой не связаны. У бизнеса возникает потребность: когда менеджер вносит изменения в системе А, нужно, чтобы они подтянулись в систему Б. Мне, как разработчику, чтобы сделать такую интеграцию, нужно понять, как данные хранятся в базе. Я спрашиваю заказчика: «Как в базе данных называется вот это поле?» И он начинает говорить то, что написано у него на экране, по-русски, терминами из его предметной области.

Но это с базой данных вообще ничего общего не имеет. Потому что разработчики, которые создавали эту систему, могли назвать поля как угодно. Перевести термины на английский. Использовать сокращения. Написать транслитом, просто русское слово на латинице. И если это нигде не задокументировано, нужно найти человека, который помнит, почему поле называется именно так. А потом связать три слоя: то, что пользователь видит на экране; то, что имеет смысл с точки зрения бизнеса; и то, что реально лежит в базе данных. По сути, три разных названия одного и того же.

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

Два канала: приходят с болью и спрашиваю сам

У меня два способа узнать, что что-то не так.

Первый: инженеры или ML-разработчики приходят сами. Обычно это значит, что проблема уже достаточно болезненная, чтобы человек оторвался от своих задач и пришёл с ней. Инженер говорит: «У меня конфиг разросся, я не могу в нём ориентироваться». Или: «Мы не можем использовать новое решение, потому что при изменении настроек одной камеры перезапускается весь сервис». ML-разработчик жалуется, что при каждом обновлении библиотек нужно переписывать тесты во всех репозиториях. Это конкретная боль, с которой можно работать.

Второй: я сам спрашиваю. Примерно раз в две-три недели обращаюсь к инженерам и ML-разработчикам: что неудобно в работе, чем не пользуетесь, что мешает, может быть нужен какой-то инструмент. Начал я это делать примерно через год после прихода в СИБУР, первый год ушёл на то, чтобы разобраться, как у нас всё устроено. И за полтора года запросы не заканчиваются: продукт живой, кейсы добавляются, заводы подключаются, люди меняются.

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

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

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

Четыре типа находок

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

Лишнее: фичи, которые жрут ресурсы, но никому не нужны

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

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

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

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

Отдельная сложность в том, что люди меняются. Человек, который пять лет назад попросил добавить какую-то штуку, уже не работает. Я вижу эту штуку в коде, кодовая база уже большая, прошло несколько лет, и единственный способ разобраться, нужна она кому-то или нет, это спросить.

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

Забытое: готовое решение, о котором никто не знает

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

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

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

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

Это особенно важно, потому что система работает круглосуточно, в том числе по выходным. Если что-то падает ночью, инженер получает сообщение и подключается. Разница между «подключился и за минуту разобрался по дашборду» и «полчаса копался в логах через три соединения» для человека, которого разбудили в три часа ночи, очень ощутима.

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

Рутина: инструмент, которого не хватает

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

Конфиг на 2000 строк

Наши сервисы настраиваются через конфигурационные файлы в формате YAML. Там описываются камеры (у каждой есть источник видеопотока, тип, опции, регионы на кадре, которые нас интересуют), детекторы, правила оповещения при эскалации инцидентов, и другое. Если завод небольшой и камер немного, конфиги компактные и читаемые. Но на одном из заводов камер было около двухсот. Структура описания некоторых модулей одинаковая, кроме одной строчки. Всё остальное копировалось. Один из конфигов разросся до 2000 строк.

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

notifiers:
  siiot_1:
    args:
      archive:
        after_sec: 5
        before_sec: 8
        host: http://127.0.0.1:12345/
        login: user
        password: '12345'
      kafka:
        bootstrap_servers:
        - s01.local
        - s02.local
        - s03.local
        ssl_cafile: ./certs/rootCA.crt
        ssl_certfile: ./certs/kafka.crt
        ssl_keyfile: ./certs/kafka.key
      kafka_topic: detection
      video_path:
        incident_dir: ./video/video_camera_1
        base_dir: /data/video/video_camera_1
        time_before: 5
        time_diff_limit: 60
    cls: siiot
  siiot_2:
    args:
      archive:
        after_sec: 5
        before_sec: 8
        host: http://127.0.0.1:12345/
        login: user
        password: '12345'
      kafka:
        bootstrap_servers:
        - s01.local
        - s02.local
        - s03.local
        ssl_cafile: ./certs/rootCA.crt
        ssl_certfile: ./certs/kafka.crt
        ssl_keyfile: ./certs/kafka.key
      kafka_topic: detection
      video_path:
        incident_dir: ./video/video_camera_2
        base_dir: /data/video/video_camera_2
        time_before: 5
        time_diff_limit: 60
    cls: siiot
  video_1:
    args:
      host: 127.0.0.1
      login: user
      password: '12345'
      path: /video_1
      port: 12346
      retries: 10
      time_correction: 300
      timeout: 30.0
      video_bitrate: 5000k
    cls: video
  video_2:
    args:
      host: 127.0.0.1
      login: user
      password: '12345'
      path: /video_2
      port: 12346
      retries: 10
      time_correction: 300
      timeout: 30.0
      video_bitrate: 5000k
    cls: video
version: 0

До: каждый модуль (siiot, video) дублировался под каждую камеру целиком. siiot_1 и siiot_2 — это одинаковые блоки по 20+ строк, у которых отличается только одна вложенная секция video_path (пути к файлам конкретной камеры). Тоже самое с video_1 и video_2 — идентичные блоки, разница в одной строке path. При 200 камерах это 200 копий каждого блока.

Я посмотрел и понял, что здесь можно применить наш инструмент для миграции конфигов: когда выпускается новая версия сервиса с изменённой структурой конфига, старый конфиг автоматически преобразуется в новый формат при запуске. Старая версия при этом сохраняется, если что, можно откатиться. Мы переработали структуру так, чтобы убрать дублирование: общие параметры описываются один раз, а отличающиеся параметры перемещены в другую секцию конфига, где описываются аргументы сообщения при нотификации. 2000 строк сократились до 900. 1100 строк копипасты просто исчезла.

notifiers:
  siiot_1:
    args:
      archive:
        after_sec: 5
        before_sec: 8
        host: http://127.0.0.1:12345/
        login: user
        password: '12345'
      kafka:
        bootstrap_servers:
        - s01.local
        - s02.local
        - s03.local
        ssl_cafile: ./certs/rootCA.crt
        ssl_certfile: ./certs/kafka.crt
        ssl_keyfile: ./certs/kafka.key
      kafka_topic: detection
      time_before: 5
      time_diff_limit: 60
    cls: siiot
  video_1:
    args:
      host: 127.0.0.1
      login: user
      password: '12345'
      port: 12346
      retries: 10
      time_correction: 300
      timeout: 30.0
      video_bitrate: 5000k
    cls: video
version: 2

После: модуль описан один раз. Секция video_path вообще убрана из конфига нотификатора — параметры base_dir и incident_dir перемещены в другую секцию, где описываются аргументы сообщения при нотификации. . Тоже с path у video. А time_before и time_diff_limit, которые раньше были вложены внутри video_path, просто поднялись на уровень выше, в args напрямую.

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

Упрощённый продакшен для тестов

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

def test_raw_events_five_region1_and_one_outside(conf: HybridConfig):
    with (
        conf.getCamera("somecamera") as camera,
        # Инициализация детекторов вручную
        # Недостаток: привязка к структуре класса HybridConfig
        # Недостаток: нужно использовать контекстный менеджер или управлять контекстом вручную
        conf.getDetector("crop_helmet_colors_bytetrack_1") as detector_1,
        conf.getDetector("crop_helmet_colors_bytetrack_2") as detector_2,
    ):
        # Инициализация роутера кадров FramesRouter вручную.
        # Недостаток: привязка к внутренней архитектуре гибрида
        frames_router = FramesRouter(conf)
        # Получение маршрута кадра вручную для каждого детектора
        # Недостаток: невозможно обработать кадр в цепочке `камера -> детектор 1 -> ...`, только `камера -> детектор`
        route_1 = frames_router.get_route(detector=detector_1.name, camera=camera.name)
        route_2 = frames_router.get_route(detector=detector_2.name, camera=camera.name)

        # Задание общих параметров и настройка каждого детектора
        # Недостаток: общие параметры нужно дублировать для каждого детектора
        detector_1.return_raw_events = True
        detector_2.return_raw_events = True
        detector_1.return_counter_events = False
        detector_2.return_counter_events = False
        detector_1.object_min_status_count = 3

        with conf.get_camera("somecamera") as camera:
            frames = list(limited(camera, detector_1.object_min_status_count))

        for frame in frames:
            # Выполнение детекции по одному кадру
            # Недостаток: нет функциональности для получения артефактов и регионов, только ивенты
            events = detect_with_regions(conf, route_1, frame, detector_1)
            # Обработка результатов
            ...
            # Передача следующему в цепочке детектору
            # Недостаток: нужно вручную делать кропы кадров по ивентам с предыдущей детекции
            crops = make_crops(frame, events)
            for crop in crops:
                events = detect_with_regions(conf, route_2, crop, detector_2)
                # Обработка результатов
                ...

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

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

def test_raw_events_five_region1_and_one_outside(conf: HybridConfig):
    # Инициализация цепочки
    chain_tester = DetectorChainTester(conf, camera_name="somecamera", chain_name="chain_crop_helmet_colors_bytetrack")
    # Задание общих параметров и настройка каждого детектора
    chain_tester.set_return_raw_events(True)
    chain_tester.set_return_counter_events(False)
    chain_tester.detectors[0].object_min_status_count = 3

    # Подготовка кадров
    with conf.get_camera("somecamera") as camera:
        frames = list(limited(camera, chain_tester.detectors[0].object_min_status_count))

    # Прогон кадров по цепочке
    detection_result = chain_tester.execute_chain(frames)
    # Получение результатов детекции на каждой стадии прохождения кадра
    for stage_num, stage in enumerate(detection_result):
        name, events, artifacts_raw, artifacts_ndarray, regions, ignore_regions = stage
        # Обработка результатов
        ...

Тот же тест с DetectorChainTester: инициализация цепочки, настройка параметров, прогон кадров и получение результатов по каждой стадии — всё в нескольких строчках. Маршрутизация, кропы и управление контекстом спрятаны внутри инструмента

Архитектурные тормоза: старое мешает новому

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

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

Вот пример:

Мы извлекаем видеопоток из разных источников данных: IP-камеры, изображения и видео на диске, СТВН. У каждого источника есть свои особенности, поэтому под каждый мы разрабатываем свой плагин.

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

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

Три типа задач и зачем нужна уборка

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

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

Такое разбиение на три типа задач применяется ко всем разработчикам в команде. 

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

Задачи по работе с легаси я рассматриваю как разминку. С утра, когда ещё не включился в тяжёлую задачу, или вечером, когда мозги уже не соображают для сложного, можно взять что-то такое и сделать. Это вещи, которые просто нужно, чтобы кто-то сделал.

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

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

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

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

Вместо итогов

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

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

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

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

Подписывайтесь на наш тг-канал. Он полезен айтишникам, которые хотят понять, что реально происходит в промышленном ИТ.

Там мы рассказываем о цифровых технологиях для производства — от IIoT и аналитики до инженерных инструментов и ИИ. Делимся кейсами, экспериментами, новостями и выкладываем вакансии.