Заметки: Работа

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

Показано: 0 · Страница

Номер года в литерале типа дата превышает 3999
28 марта 2026 ·

Про ошибку в названии заметки я уже как-то писал. Напомню суть: платформа не переваривает даты позже 3999 года (на практике начинает стрелять с 3999-12-01). Теоретически такие даты вообще нельзя записать, но если очень хочется из-за ошибок в прикладном коде (и в коде самой платформы) это всё-таки возможно и время от времени случается.

Обычные симптомы — перестают работать какие-то отчёты, не проводятся какие-то документы, падает пересчёт итогов. В общем, страдает любой код, который трогает записи с проблемными датами.

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

Thank You, Mario!

Желая решать проблему как-нибудь побыстрее, несколько лет назад я написал запрос для PostgreSQL. Общая идея:

  1. Ищем в базе данных все поля с датами.
  2. Строим мегазапрос к таким полям (ищем даты, которые выходят за лимит 1С).

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

Почему действуем на уровне СУБД? Ну, средствами 1С такой номер не отколоть — напомню, платформа падает при попытке потрогать проблемные записи. Это касается любого взаимодействия, включая чтение. Кроме того, в данном случае работать напрямую быстрее и удобнее.

На днях я переписал этот запрос для MS SQL. Вышло длиннее (в силу особенностей этой СУБД), но идея та же.

Если будете использовать, имейте в виду, что:

  • Поле _Fld626 в тексте запроса — разделитель для фреша. В вашей базе может называться по-другому или вообще отсутствовать.
  • Запрос написан для базы со сдвигом в 2000 лет. Если в вашей базе сдвига нет, нужно скорректировать условие (см. функцию DATEADD()).
  • Трюк с выводом в XML (см. FOR XML) я добавил, чтобы не дать SSMS обрезать текст мегазапроса (показалось быстрее, чем возиться с приведением типов). Побочный эффект: в получившемся мегазапросе нужно заменить > на >, прежде чем выполнять его.
С побочным эффектом
15 марта 2026 ·

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

В общем, запатчили через несколько разных подходов, один из них — подгрузка дополнительных данных в обработчике ПриПолученииДанныхНаСервере() (я про него недавно вспоминал, кстати). Написали удобный фреймворк вокруг фичи, внедрили, потестировали и... В общем, сидим с кислыми минами.

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

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

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

Блин, вот как можно было разработать такой замечательный концепт обработчика и настолько заруинить реализацию на уровне платформы?

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

Superpower

Вот и мы с разработчиками платформы в таком же диалоге. Вы можете разогнать динамические списки (но UI будет вызывать у пользователей бешенство). Вы можете автоматом слать двоичные данные в бакет S3 (но потеряете их в один неосторожный клик). Вы можете телепортироваться куда угодно (но тратите столько времени, словно шли пешком). Вы можете превращаться (но только в пожилого мопса). У вас пышные, шелковистые волосы (но на заднице).

Автосаммари
8 февраля 2026 ·

Записывать все встречи я, судя по моим же заметкам, начал ещё в 2020-м. Видео всегда точнее памяти, а ткнуть на «Start Recording» в OBS — самый дешёвый способ не потерять какую-нибудь ценную инфу.

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

Однако этот метод тоже не идеален. Создание конспекта (даже тезисного) отъедает фокус от самой встречи. Кроме того, про какие-нибудь забытые по ходу дела подробности можно узнать слишком поздно.

Забыл

Короче, пришёл к тому, что хороший вариант — просто извлекать из видео аудио разговора, превращать его в текст (нейронкой), а текст разговора — в детальное саммари по встрече (опять нейронка).

Аудио проще всего выдрать через ffmpeg (консольная утилита для работы с аудио и видео). Ниже пример вызова (один аудиоканал, дискретизация 16 кГц + нормализация громкости):

ffmpeg.exe -y -i "D:\video.mkv" -vn -ac 1 -ar 16000 -af loudnorm -c:a pcm_s16le "D:\audio.wav"

Что до извлечения текста — я экспериментировал с Vosk в связке с recasepunc, но про это без слёз не вспомнить. Даже комментировать не хочу. А вот Боромир Whisper (нейросеть от OpenAI, умеющая распознавать голос) ставится в фоне за 10 минут:

py -3.10 -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install setuptools wheel
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118
pip install openai-whisper

Пример вызова:

whisper "D:\audio.wav" --model small --language Russian --output_format txt

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

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

Скрипт ищет в своей папке первый попавшийся .mkv, прогоняет через ffmpeg + Whisper и сохраняет результат в ту же папку. Если будете использовать — обратите внимание, что он работает через CUDA (на CPU тоже можно, но будет сильно медленнее) + скачанные модели Whisper он сохраняет не в дефолтный кэш, а в папку с текущим Python-окружением.

(при желании можно прикрутить к нему вызов API или обращение к какой-нибудь модели в локальной LM Studio, но персонально для себя я решил — не, уже перебор)

Новый UI в блоге
17 января 2026 ·

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

Блог живёт на Github Pages, так что выбор решений небогат: либо слать запрос в гугл, либо делать свой статический индекс и отдавать в браузер пользователя (пусть сам в нём роется). Я пошёл по второму пути: быстрее, управляемее и можно самому покодить. При первом поиске файл индекса, правда, нужно скачать, но что такое 200 Кб в современном интернете? Смешно.

Ну а там как-то, знаете ли, пошло-поехало... Сначала не удавалось прикрутить к полю поиска Tachyons — разозлился и переделал всё на Tailwind (всё равно хотел попробовать, а случая всё никак не представлялось). Пока писал код для поиска — подумал, что логично сразу вкрутить в него теги, чтобы два раза не вставать. Очнулся с облаком тегов над заметками и сообразил, что тогда уж надо и с проектником то же самое сделать, только там не теги нужны, а стеки...

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

Управление бэкапами
6 декабря 2025 ·

В конце года выкатили для нашего внутреннего инструмента (я уже вскользь писал о нём) большой апдейт, дающий коллегам адекватный доступ к бэкапам пользовательских приложений. Бэкапы в SaaS-компании нужны всем и всегда — для разработки, для тестирования, для расследования проблем, да много для чего. Без адекватного учёта процесс превращается в зоопарк, когда три человека в один момент времени создают три запроса на практически одинаковые копии одной и той же базы. Задача, конечно, решается, но ресурсов прожрано в три раза больше, чем хотелось бы.

У нас уже было решение на базе UI Битрикса, но в силу, э-э, особенностей развития этого продукта оно приносило больше боли, чем пользы. Поэтому мы переосмыслили процесс и всё переписали. На фронте — 1C, на бэке — PostgREST, PostgreSQL, PowerShell и много чего ещё. Логика довольно сложная, но у пользователя — простой и дружелюбный UI, через который можно заказать бэкап буквально в два нажатия.

Выбрать можно один из трёх видов бэкапов:

  • облачный (копия реального приложения, развёрнутая в облаке и доступная, в том числе, через браузер);
  • файловый бэкап (обычный .dt-файл, который можно скачать и развернуть на локальной машине);
  • бэкап конфигурации и расширений (.cf + .cfe).

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

Ну и продолжаем хохмить в интерфейсе, конечно.

But Still!

Coffee First!

Протоптанные дорожки
29 ноября 2025 ·

Ладно, загадка Жана Фреско. У вас есть таблица, скажем, на 50 тысяч строк. Как прочитать из неё полмиллиарда?

Раз плюнуть, Nested Loops + Clustered Index Seek:

Полмиллиарда

От Clustered Index Seek тут одно название, конечно. Фактически оператор при каждом исполнении пробегает по всей таблице (всему кластерному индексу) и сверяет каждую запись с Predicates. И так — 10 730 раз для 51 391 записей. В итоге 551 425 430 строк прочитали, 13 343 вернули.

Ох

Короче, идеальный пример плохого плана запроса в вакууме, хоть сейчас тащи в палату мер и весов. Nested Loops, если кто позабыл, работает примерно так:

For Each Table1Row In Table1 Do
    For Each Table2Row In Table2 Do
        ...

Это ОК для мелких таблиц, но СУБД может его применить и для таблиц поболбше — например, если ей не хватает времени на построение плана.

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

Некоторые регистры и сами по себе были здоровенными, а виртуальные таблицы дополнительно поддали жару (каждая превращается в 2+ вложенных запроса). СУБД честно пыталась придумать эффективный алгоритм, но в какой-то момент решала, что хреновый план запроса всё же лучше, чем вообще никакого.

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

Короче, по поводу виртуальных таблиц в динамических списках. В английском есть выражение «desire path», «протоптанная дорожка». Часто прицепить виртуальную таблицу к основной — и в самом деле самый простой, быстрый и привычный способ решить задачу. Но он не эффективен.

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

Шаманство
5 октября 2025 ·

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

  • Создаем новое приложение из шаблона 35-го релиза нашей ERP.
  • Запускаем его и ждем, когда закончится инициализация.
  • Заменяем конфигурацию приложения на 36-й релиз (конкретно, версию 28537 из хранилища разработки) и снова запускаем.

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

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

Welcome To Dipshit Central

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

  • Перечисление было и в 35-м релизе, но было переименовано в 36-м;
  • Методов в 35-м релизе вообще не было (в 36-м их как раз разработали).

То есть не первого, не вторых в кэше 35-го релиза не существовало, а платформа, по какой-то причине, пытается рыться в именно в нём.

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

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

Шаманство, конечно, но работает ¯\_(ツ)_/¯

Медленное удаление областей Фреша
3 августа 2025 ·

Коллега заметил, что на одном из инстансов нашего фреша удаление областей стало идти прямо-таки трагически долго. Что в метриках? Вот это:

DELETE FROM T1
FROM _DataHistoryMetadata T1
WHERE 
    T1._MetadataId = ?
    AND T1._IsActual = 0x00 
    AND NOT (
        T1._MetadataVersionNumber IN (
            SELECT T2._MetadataVersionNumber AS MetadataVersionNumber_
            FROM _DataHistoryVersions T2
            WHERE T2._HistoryDataId IN (
                SELECT DataHistoryLatestVersions1.DataHistoryLatestVersions._HistoryDataId AS HistoryDataId_
                FROM DataHistoryLatestVersions1.DataHistoryLatestVersions T3
                WHERE DataHistoryLatestVersions1.DataHistoryLatestVersions._MetadataId = ?
            )
        )
    )

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

Чему ты удивлён?

В общем, теряем от тридцати секунд до получаса на действие. Хотелось бы побыстрее. Решение:

CREATE NONCLUSTERED INDEX IX_DataHistoryLatestVersions1_MetadataId
  ON dbo._DataHistoryLatestVersions1 (_MetadataId)
  INCLUDE (_HistoryDataId)
  WITH (DROP_EXISTING = OFF, ONLINE = OFF);

CREATE NONCLUSTERED INDEX IX_DataHistoryVersions_MetadataVersionNumber
  ON dbo._DataHistoryVersions (_MetadataVersionNumber)
  WITH (DROP_EXISTING = OFF, ONLINE = OFF);

Стоимость удаления области ожидаемо просела на ~99%.

Если будете повторять у себя:

  1. Формально такой трюк нарушает лицензионное соглашение, you've been warned и всё такое.
  2. Есть риск, что платформа при будущих реструктуризациях будет спотыкаться о новые индексы (особенно при работе по «новой» схеме). Лучше иметь под рукой готовый скрипт, который сможет их грохнуть, а потом (после реструктуризации) — вернуть обратно.
Нет
11 июня 2025 ·

Нет

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

Платформа, причем, установлена с единственным языковым пакетом (английским) и даже запущена с жестким указанием на «Ven VLen», но откуда-то всё равно прорастают родные березки. Возможно, сишная библиотека, откуда стреляет сообщение (DataExchangeTcpClientImpl.cpp) в моем случае смотрит на язык ОС (русский), сложно сказать.

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

No

Пасхалки
6 апреля 2025 ·

Thank you, Mario!

Awaiting Signal

Пара пасхалок, которые можно найти внутри FirstBit ERP. Спрятаны для разработчиков: пользователи в первом случае видят на форме заголовок и текст оповещения, а во втором — стандартное waiting for connection.

Люблю время от времени делать такие штуки: это весело, а ещё — помогает не скучать, даже если задача не слишком увлекает или просто устал. Если не читали заметку «Дизайн без стресса» Быстроновского — прочитайте, она клёвая (а я все равно не сформулирую лучше).

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

Nice try, Marty!

Разработка ERP на английском
19 января 2025 ·

Ведущий разработчик из нашей команды немного рассказал о нашей работе — конкретно, о разработке ERP на 1С для ОАЭ и, с недавних пор, Саудовской Аравии. Встречу захостила онлайн-школа английского языка Across, так что аудитория получилась соответствующая — русскоговорящие ребята, интересующиеся карьерой и рынками за рубежом.

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

Немного хирургии
17 декабря 2024 ·

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

А сегодня с коллегой ремонтировали кластер 1С с базами на 1cFresh, которому после миграции на соседний сервер внезапно поплохело (если вкратце, при попытке распечатать документ клиентское приложение умирало в муках). Пока возились, мелькнула мысль: это, конечно, не так жутко выглядит, как ремонтировать софт, от которого вот прямо сейчас зависит чья-то жизнь, но... Если посчитать всех клиентов, которые вот прямо сейчас сидят на нервах из-за того, что у них бизнес стоит — ещё неизвестно, где уровень стресса будет выше.

P.S. Технические заморочки для тех, кому интересно. Вышло так, что при миграции кластера права на папку с серверным кэшем переехали некорректно и это привело к любопытному эффекту: в ней оседали только логи, а данные сеансов — нет. В итоге при открытии печатной формы документа конфигурация пыталась положить её в хранилище; rphost, в свою очередь, пытался засунуть её в серверный кэш сеанса.

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

Пересобрали права, перезапустили кластер, проблема ушла.

End of Report

Остальные гипотезы (буйство менеджера кластера, недостаток аппаратных ресурсов, программные ошибки конфигурации, ошибки клиентского процесса, кривая отработка security profile, проблемы c сетью между клиентом и сервером) отбросили по ходу диагностики.

Сводка в Slack
10 ноября 2024 ·

Попался под руку скрипт, который я пару лет назад накатал для рабочего GitLab'а. Вкратце: мы ежедневно прогоняем репозиторий разработки через прорву тестов на Ванессе, в результате чего получаем симпатичный отчет — сколько тестов выполнено, какие провалились, причины провалов и так далее.

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

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

Отчет

Как бонус, стало проще отвечать на философский вопрос «кто все сломал». Чаще всего — автор первого коммита, на котором метрика Scenarios Failed на скриншоте выше пробила потолок :)

Про PDF
6 сентября 2024 ·

Получаю жалобу: клиент не может войти на наш портал. Смотрю в базе — учетная запись в порядке. Что не так-то? Проверяю логин и вижу чудную картину:

PDF

Выше то, что вводит пользователь, а ниже — то, что в базе. Первая мысль: откуда, черт подери, в строке взялся документ? :D

Опущу дальнейший разбор. Главное тут — задавать правильные вопросы (иначе и гугл, и ИИ будут рассуждать о популярном формате, а не о символе). Короче, PDF в контексте Юникода означает Pop Directional Formatting! Это символ, который управляет направлением текста; он нужен, например, чтобы корректно рендерить арабский язык (где может встречаться как текст слева направо, так и справа налево). Пользователь, очевидно, вводит логин в режиме RTL (или копирует откуда-то), а портал этого нюанса не понимает.

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

Сингапурская кукла
22 июня 2024 ·

Можно ли любить валюту своей страны сильнее, чем жители Саудовской Аравии? Вопрос риторический: уверен, что нет.

Разглядываю сайт их центрального банка. Валюта страны — саудовский риал, и курсы других валют ЦБ устанавливает по отношению к ней. То есть, нет смысла спрашивать с банка курс самого риала. Однако сайт невозмутимо предлагает выбрать его дважды:

Форма выбора

Для справки: первый вариант ломает интерфейс, а второй педантично выдает единицу на любую дату.

Другая забавная деталь: коллеги, кажется, хранят названия валют как строки длиной в 14 символов. Иначе сложно объяснить, почему по их данным в Канаде используется не канадский доллар, а CANADIAN DOLLA, а в Румынии — загадочный NEW ROMANIAN L вместо леи. Впрочем, этим двоим ещё повезло: Сингапур, например, ведёт расчёты в SINGAPORE DOLL.

Скриншот со звуком
20 мая 2024 ·

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

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

Cut My Life Into Pieces

Заголовок вышел со звуком. Ладно, думаю, смешно, но как тогда назвать? Во, пусть будет Distributed Internal ERP. Сокращенно... DIE?

Оставил первый вариант. Long live Papa Roach :)

Таймшит для Обсидиана
12 мая 2024 ·

Написал ещё один плагин к Obsidian, на этот раз — для ежедневных заметок. Рисует симпатичный отчет: над какими задачами работал, что сделал, сколько времени потратил. Я постарался описать в репозитории, как это работает; буду рад, если кому-то ещё пригодится!

Забавный момент: для примеров в README я использовал номера задач FBI-1, FBI-2 и так далее. Это не отсылка к X-Files или Twin Peaks — просто первое, что пришло мне в голову. Дело в том, что наш внутренний проект по разработке FirstBit ERP называется First Bit Internal, сокращенно — FBI. Основной пул задач, над которыми мы работаем, живёт именно в нём.

Мы-то уже привыкли, но коллег вне компании наши скриншоты из JIRA или SonarQube неизменно веселят. Представили, что вы — агент Купер? А мне и представлять не надо :)

Do? Do Not?
14 января 2024 ·

На одном из проектов у нас есть две общающиеся друг с другом системы: ERP и CRM. Обмен данными сделан по-взрослому: поднят push'n'pull сервер, прописаны подписки на события, подняты REST API и много другой интересной технической обвязки, но я сейчас не про неё.

Среди прочей логики, там так: если в CRM появляется новый контрагент — его данные отправляются в ERP. На днях с этим возникла проблема: один из контрагентов не отправлялся из CRM ну вот вообще никак, сколько его не записывай. Полезли разбираться, подозревая худшее: CRM написана на PHP (ничего личного, просто это не наш технический стек) и там много разного легаси. Выстрелить себе в ногу проще, чем высморкаться.

Однако особенно долго копаться не понадобилось. Открыли страницу контрагента в CRM и увидели, что у него стоит галка «Do Not Export To ERP», которая, собственно, блокирует отправку. Короче, очевидная ошибка какого-то менеджера.

Убираем галку, закрываем тикет?

Well yes, but actually no

Это решит проблему с этим конкретным контрагентом, но не причину, по которой она возникла. А она в интерфейсе, конкретно — в названии опции: используется «do not», которого желательно избегать из-за того, что пользователям сложнее правильно считать формулировку. К простому «do» это, кстати, тоже относится.

Программистам часто непросто понять, почему так: мы привыкли мгновенно рассчитывать в уме булевые выражения и вариации в духе «не (не истина)» для нас — обычное дело. А вот люди с другим бэкграундом могут путаться. Совсем чуточку, но иногда и этого достаточно, чтобы в горячке дня воспринять «do not export» как «do export», ткнуть опцию и побежать дальше.

Отсюда выводим правильное решение: переименовать галку. Подойдёт «Disable Export» или «Stop Export». В голову ещё приходит «Prohibit Export», но это скорее про межличностные отношения и вообще, запрет что-то делать не означает, что это что-то не будет сделано :)

Бытовой героизм
24 сентября 2023 ·

Поднимал какое-то время назад Swagger для внутренней API-шки. Пока возился — стало понятно, что часть функционала в документацию включать не нужно. Поискал способ, как это сделать без костылей — наткнулся на забавный вопрос на GitHub'е.

Чем забавный? Ну, невольно вспомнил про Мисту. В среде 1С-разработчиков это синоним слова "токсичность": мол, спросишь там что-нибудь — получишь ушат помоев за шиворот вместо ответа. Здесь, конечно, не так всё запущено, но ребята, с завидным упорством ссылающиеся на 14-и страничный мануал, здорово рассмешили.

Одно радует: к концу треда всё же нашёлся отважный повстанец, который просто взял и запостил нужный параметр для декоратора.

Не все герои носят плащи, ей-богу.

Румынская фича
30 августа 2023 ·

Делаю для нашего клиентского портала функцию восстановления пароля через SMS. Добрался до документации Twilio о поддержке буквенно-цифрового ID отправителя в разных странах; эта фича позволяет отправлять сообщения так, чтобы получатель видел не номер отправителя, а что-то осмысленное (название компании, например).

При этом фича везде зарегулирована по-своему: где-то просто работает, где-то нужна регистрация.

Читаю:

Скрин

🤔

  • Португалия: да
  • Пуэрто-Рико: нет
  • Катар: да (с регистрацией)
  • Реюньон: да
  • Румыния: да (с регистрацией) (но бойтесь Дракулы)

Не знаю, как ещё объяснить это кладбище.

UPD: Нашёл разгадку. Могильные кресты означают, что за регистрацию нужно заплатить 700 баксов.

Объяснение про Дракулу мне нравилось больше.