Привет! Меня зовут Андрей, я технический директор в компании КЕДР Solutions. Мы занимаемся контрактной разработкой электроники и программного обеспечения. “Рисуем” платы и кодим уже больше 10 лет! 

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

  • Как мы организуем этапы разработки?

  • Какие методологии мы используем?

  • Как часто отчитываемся о ходе работы и по каким каналам?

  • Что делаем для минимизации рисков?

  • Как работаем с изменениями в проекте?

Как начинается работа

Сбор требований, анализ и оценка проекта

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

Разумеется, первое, о чем спрашивает клиент, – это сможем ли мы разработать нужную ему штуковину, сколько это будет стоить и сколько потребуется времени? Чтобы ответить на эти вопросы, нужно хотя бы в общих чертах понять, что задумал клиент. Так что первым делом мы собираем функциональные и нефункциональные требования. Для этого сейлз устраивает клиенту допрос с пристрастием и старается разузнать обо всех его хотелках – явных и неявных. 

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

Ключ успешного брифинга для нас в том, чтобы не бояться задавать “неудобные” вопросы на старте – чтобы потом не возникло “неудобных” ситуаций на финише. Наш бриф – это фильтр для фантазий и страховка от типичных граблей контрактной разработки.

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

Затем команда валидирует каждое требование. Мы умеем вытаскивать смысл через разные креативные техники. Чаще всего применяем простую технику “Пять почему”. Этот метод помогает выявить первопричину того или иного пожелания заказчика, чтобы предложить оптимальное решение. Работает здорово, быстро и помогает откинуть ненужное (и недешевое).

Пример. Когда проектировали робота-газонокосильщика, заказчику требовались инерциальная навигация и точное позиционирование – с картами местности, геолокацией, GPS. На наш взгляд, это явное удорожание и усложнение. Встала необходимость синхронизировать видение с клиентом. Пошли, докопались (а это не всегда комфортно клиенту, кстати). Оказалось, что у его конкурентов навигация работает с помощью электробарьера – нужно натягивать специальную проволоку, что не очень удобно. Для клиента более продвинутая система навигации – явное конкурентное преимущество.

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

Итак, мы собираем и уточняем требования к решению. Затем на их основе производим грубую оценку проекта – примерную продолжительность и стоимость работ. Здесь учитывается наш опыт работы с похожими задачами и степень знания предметной области. Также прикладываем портфолио подобных решений, созданных нами, со скриншотами UI/UX (если речь о приложении).

Правда, точность у такой оценки хромает, и результат может заказчика удивить: проект займет от трех месяцев до года; стоить будет от $5000 до $15 000. Брать будете? Но беспокоиться рано: это лишь примерный разброс параметров. Он помогает прикинуть бюджет и приоритизировать фичи.

Техническое задание и архитектура решения

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

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

  • универсальный контроллер, управляющий оборудованием заказчика; 

  • серверный модуль; 

  • мобильное приложение. 

Дальше проводится анализ, к каждому блоку примеряется его функциональность, между модулями появляются стрелочки (интерфейсы). Затем начинается процесс декомпозиции самих модулей на подмодули. Получается более низкоуровневая архитектура. Так продолжается до тех пор, пока не будет убрана вся неопределенность в функционировании будущего решения.

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

Работа с рисками

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

Риски оцениваются по степени их влияния на проект и вероятности наступления. Например, если проблему с новым чипом можно закостылить за 15 минут, риск можно проигнорировать. 

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

Наш любимый пример в этом месте – микроконтроллеры GD32. Производитель громко говорит об объеме флеш-памяти и тихо, в сторону (мелким шрифтом, под “звездочкой”) – о том, что она разделена на быструю и медленную, и разница в скорости – очень большая. При таком раскладе точно потребуется время на оптимизацию прошивки. 

Как принимаются технические решения

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

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

Если же в ходе проекта команда сталкивается с проблемой и никто не знает, как ее решать, тогда мы включаем режим брейншторма. Каждый вкидывает свои мысли, идеи, критику. Итоги выдаем руководителю проекта, который принимает окончательное решение после обсуждения вариантов с заказчиком. Другой вариант – кто-то из команды уходит в глубокое изучение вопроса и возвращается к команде с результатами.

Пример. В одном проекте к микроконтроллеру надо было подключить внешнюю оперативную память. Из-за высокой частоты и большого количества проводников замакетировать это не получалось, поэтому решили проверить решение, изготовив прототипы на фабрике. Платы приехали, мы их протестировали – вроде, все ок. Продолжили писать прошивку, но в какой-то момент стали обнаруживать странные сбои, связанные с памятью. Сходу объяснений ни у кого не нашлось, поэтому техлид ушел ресерчить проблему. В результате выяснили, что память требует один из сигналов с небольшой задержкой относительно других, а микроконтроллер генерирует управляющие сигналы “одновременно”. Поставили буфер на проблемную линию – заработало! 

Распределение задач, синхронизация отделов 

В проектах, где задействованы хардовая и софтовая команды, очень важно синхронизировать их работу. Раньше, когда мы были молодыми и красивыми, мы делили проекты на HW и SW части. Харды делают плату, а кодеры пишут софт, и взаимодействовали они только при необходимости. Впрочем, быстро стало понятно, что такой подход проблематичен.

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

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

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

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

Как мы работаем над железом

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

Иногда еще на начальном этапе мы делаем макет! Это когда собираешь отдельный участок схемы “на соплях”, чтобы быстро проверить идею до прототипа. Если тест прошел – отлично, можно переносить в большую схему. Если нет – думаем дальше.

Тут могут случиться любые сюрпризы. Например, проектировали устройство на микроконтроллере nRF. Сначала проверили идею на макете. Взяли соответствующую макетку, поставили все, что нужно, – работает. Задизайнили плату, заказали на заводе, приехала, включаем – не работает. Вообще. Разработчик угробил кучу времени, чтобы понять, в чем причина. Оказалось, что nRF нужно два кварца. От одного работает все, а от другого – только часы. А поскольку часы в этом проекте были не нужны, разработчик этот кварц не поставил. Но вот именно у этой версии nRF второй кварц был обязательным!

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

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

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

Дальше – прототипы. Заказываем первую партию, получаем, тестируем… и, конечно, что-то не работает. С первого раза не заводится почти никогда – и это нормально! Бывает, что удается собрать рабочее устройство с одной итерации (что случается крайне редко), а бывает, что требуется больше пяти. Тут все зависит от очень многих факторов.

В команде ценится не только умение нажимать кнопки в софте, но и глубокое понимание физики процессов. Мы поддерживаем культуру въедливости: если инженер говорит “мне надо разобраться”, ему дают время. 

Как мы работаем над софтом

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

Допустим, мы написали код. Его надо применить к рабочей системе. Методология CI/CD может в автоматическом режиме проверить, насколько правильно он написан, работает ли он и не ломает ли решение в целом.

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

Здесь команде помогает GitLab. А еще целый ворох суперполезных инструментов. 

Автоматическая проверка кода

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

На некоторых проектах параллельно мы применяем ClangFormat. Этот инструмент проверяет форматирование: правильно ли расставлены отступы и переносы, все ли ровно и так далее. 

Поскольку мы в основном разрабатываем ПО на фреймворке Qt, то интерфейсы верстаем на QML. Для таких исходников существует похожий инструмент – QML formatter. Он подгоняет текст под правила форматирования. 

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

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

Развертывание 

Под развертыванием понимается размещение и запуск готовой версии программы на целевой платформе. 

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

Если программа собирается под Linux, то все еще проще. Собранное приложение (deb-пакет) заливается на apt-репозиторий Sonatype Nexus Repository. Это тоже хранилище файлов, но с ним можно взаимодействовать через стандартные средства установки приложений ОС Linux. Это позволяет заказчику установить или обновить приложение на своем компьютере или встроенном устройстве с помощью всего одной команды. Операционная система сама сходит в интернет, скачает и установит нужное приложение.

А если нам нужно залить разработанное решение в тот или иной магазин приложений, то мы используем Fastlane. Этот инструмент автоматически собирает приложения и публикует их в App Store, Google Play, RuStore и др.

Допустим, у нас iOS-приложение. Как только в GitLab заливаются исходники, Fastlane скачивает их, собирает и получившийся файл отправляет в Apple TestFlight. Это официальный сервис бета-тестирования приложений для яблочных устройств. С него владелец аккаунта (заказчик) может пригласить тестировщиков опробовать новый продукт и оставить отзывы. Если все хорошо, приложение можно публиковать в магазине App Store, где оно становится доступно любым пользователям. 

Наконец, при работе с “Малинками” (одноплатными компьютерами Raspberry Pi) команда использует инструмент Pi-Gen. Это скрипт, который генерирует готовые образы операционных систем под Raspberry Pi. Он оказывается незаменим, если заказчику нужна не стандартная версия ОС, а что-то кастомное – например, без фона рабочего стола, без менюшки, без курсора (так называемый kiosk mode). Или когда требования к настройкам ОС уточняются по ходу проекта. 

Образ системы можно настроить и вручную, но тогда надо запускать образ ОС на Raspberry Pi, устанавливать или обновлять приложение вручную, подчищать за собой, а потом, опять же, вручную собирать образ. Pi-Gen же автоматически собирает образ ОС нужной конфигурации с последней версией приложения (если оно загружено в репозиторий, например, в Nexus). Затем получившийся ISO-файл можно кинуть на ownCloud, откуда его заберет заказчик и развернет на своих устройствах. 

Отчетность

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

Ежедневные или еженедельные отчеты

Формируются в письменном виде и отражают, сколько рабочих часов команда потратила на те или иные задачи. Мы в компании разработали собственное программное решение, которое среди прочих решает задачи отчетности. Отчет о выполненных работах агрегирует данные из Jira (ворклоги) и автоматически формирует их в виде таблицы, в которую входят: наименование задачи, описание работ, сколько часов потрачено и сумма за неделю. В отдельном блоке – сумма часов по всему проекту и сколько часов бюджета осталось.

Пример отчета клиенту
Пример отчета клиенту

Периодические отчеты

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

Демо и отгрузки билдов

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

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

Работа с граблями

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

  • Заказчик просит новую фичу

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

Например, в одном из проектов заказчик собирался сделать разграничение прав пользователей в системе, и мы заложили эту задачу в первоначальную оценку. Но тогда фича показалась слишком дорогой, поэтому от нее отказались. Прошло полтора года, проект движется к завершению, и тут клиента “осеняет”, что фичу надо непременно вернуть. Мы: “Все ОК!” Делаем переоценку проекта. Рассчитываем, сколько времени это займет и сколько будет стоить. Выкатываем заказчику. Тот бледнеет, но соглашается. Подписываем документы, берем в работу новый план. 

  • Команда ошибается в оценке или планировании

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

  • Недопонимания

Наученные опытом, мы, как военные, стараемся все повторять по два разá. А еще записывать. Но как ни старайся, случаются казусы. 

Делали проект для ребят из Аргентины. Разрабатывали софт под их железяку. В какой-то момент клиент выкатил дополнительные требования, которых изначально в ТЗ не было. Мы сказали, что это задачи сверх оценки (out of scope). Точно делать? “Делайте”, – сказал заказчик. Когда в конце проекта клиент увидел счет за услуги, он сильно удивился. 

Оказалось, заказчик плохо прочитал договор, в котором написано, что все, что “out of scope”, оплачивается отдельно. А мы не подсветили это дополнительно. С тех пор мы всегда явно обозначаем стоимость, сроки и другие параметры.

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

Как мы с этим работаем?

Максимально стараемся выяснить все дополнительные потребности на старте. Но, если требование появилось в процессе работы, мы:

1. Фиксируем, что это именно дополнительное требование, или “out of scope” задача.

2. Сообщаем клиенту. Решаем, что делать дальше. В 90% случаев клиент просит ее оценить.

3. Оцениваем работу в часах (включая смежные работы, такие как актуализация документации, дополнительное тестирование и т.д.). Прикидываем, на сколько сдвинутся сроки этапов.

4. Согласовываем все с клиентом.

Далее варианта три: а) берем в работу; б) кладем в бэклог; в) отказываемся от реализации.

Подытожим 

Контрактная разработка электроники и ПО – это длительный итеративный процесс, в котором встречается много сложностей и подводных камней. Для успешного создания продукта требуются как технические знания, так и умение работать с клиентом. Внутренняя кухня нашей команды выработалась на базе многолетнего опыта. И даже текущие процессы мы продолжаем адаптировать под постоянно меняющиеся реалии. Буду рад почитать в комментах, как устроена работа в других командах.

Комментарии (10)


  1. AlexXYZ
    16.07.2025 05:22

    Вроде как прописные истины, всё по делу. Чтоб все так работали, очевидные же вещи!?


  1. Flammmable
    16.07.2025 05:22

    Откуда берёте узкую экспертизу? Отдаёте на аутсорс (что происходит, если субподрядчик не справляется?), развиваете свои компетенции (за счёт заказчика) или держите оверквалифицированных узких спецов "на всякий случай"?

    Как реализуете тестирование железной части? Рассматриваете компоненты как блоки с гарантированными паспортными характеристиками и идеальными связями между блоками; доверяете результатам симуляции и моделям компонентов (что делаете, если у производителя того или иного компонента отсутствуют модели для симуляции?) или собираете тестовый стенд, способный, например, свипировать параметры? Если последнее, какое самое дорогое измерительное оборудование у вас есть в собственности?


    1. Indemsys
      16.07.2025 05:22

      Судя по перечню проектов на их сайте это в основном переигрывание опенсорсных проектов. Недаром там в основном фигурируют nRF, STM32, малина и проч.
      Нет ни одного проекта для какого-нибудь станка или промышленной установки. Максимум датчики. Из математики максимум PID и Калман из опенсорсных проектов, OpenCV там где-то. Симуляцию систем, SIL и HIL не применяют похоже в принципе.

      Т.е. экспертиза здесь только на этапе проверки есть или нет что-то подобное на алике или в апнотах 3-4 известных производителей. Оборудования для таких проектов почти не надо. Но и ценник адекватный. Я бы даже сказал удивительно низкий.


      1. Flammmable
        16.07.2025 05:22

        Вот да. Всякий раз, когда вижу словосочетание "контрактная разработка электроники", сначала в воображении появляется романтический образ по типу "Команды А" из оригинального сериала :) Такие, фулстек электронщики у которых всё всегда получается, а если и нет - то в ход идёт находчивость и смекалка.

        А потом я вспоминаю, что у производителей ИБП, узкоспециализированных на своей тематике, измерительно-тестовое оборудование составляет примерно x100 к стоимости единицы продукции. А также, что наряду с софтом и железом есть полноценное подразделение, осуществляющее сплошное покрытие тестами.


    1. andreysolovev Автор
      16.07.2025 05:22

      Когда нужна узкая экспертиза, возможны варианты. Если такого опыта нет, честно говорим заказчику: мы не умеем, но можем попробовать и будем учиться за ваш счет. Если клиенту норм, то работаем так. Аутсорс – тоже вариант. Мы на постоянке работаем с проверенными спецами, с ними проблем не возникало.

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


      1. Flammmable
        16.07.2025 05:22

        Какой самый дорогой измерительный прибор/комплекс у вас есть в собственности?


        1. andreysolovev Автор
          16.07.2025 05:22

          Это довольно интимный вопрос ))


          1. Flammmable
            16.07.2025 05:22

            А что тут интимного?

            Вон по ссылке выше компания-разработчик ИБП вполне спокойно сообщает: "продукцию тестируем на GW Instek PEL‑500xC и на GW Instek APS‑7000".

            В чём здесь может быть проблема?


  1. sepulkary
    16.07.2025 05:22

    Команда ошибается в оценке или планировании

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

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

    Остальное — огонь!


  1. Valeriy_Paranichev
    16.07.2025 05:22

    Можно только порадоваться успехам коллег по цеху. (Отличные, кстати иллюстрации к статье).