BorisovAI

Блог

Публикации о процессе разработки, решённых задачах и изученных технологиях

Найдено 3 заметокСбросить фильтры
Новая функцияtrend-analisis

Когда старый формат données ломает всю миграцию

Мы дошли до финальной фазы decoupling в **Trend Analysis** — нужно было избавиться от поля `object_id`, которое пронизывало базу данных в шести таблицах. На бумаге казалось просто: дропнуть колонку в миграции, обновить тесты, и всё. На практике это превратилось в охоту за тремя потерянными файлами. Проблема была в том, как я организовал работу. Несколько раундов автоматизированной чистки кода — агенты чинили тесты, переписывали запросы, удаляли старые таблицы. Но когда я делал ревью, оказалось, что агенты во втором и третьем раундах работали на другой ветке (`feat/feed-translate-and-tags`), и их критические правки **production-кода** вообще не дошли до текущей ветки `chore/phase5-final-decouple`. Три файла остались с прямыми ссылками на удаленное поле: **`src/db/queries/event_store.py`** — 12 JOINов по `events.c.object_id`, четыре функции. Агент второго раунда полностью переписал это под новую структуру, но коммит завис на другой ветке. **`src/db/repositories/trend_repo.py`** в методе `materialize()` всё ещё писал `object_id` в VALUES запроса. Агент третьего раунда удалил параметр, но изменение потеряось. **`src/db/queries/trend_linker.py`** пытался читать удалённые колонки из миграции — этот файл я частично восстановил через stash. Тесты кричали: **69 failed, 1020 passed**. Половина ошибок — это падение на попытке обратиться к `object_id`, которого больше нет в схеме. Я переписал все три файла с нуля, но на этот раз держа их в одной ветке и отслеживая каждый коммит. Удалил параметры, переделал JOINы на новые ключи, переписал select'ы. Никакого волшебства — просто старая-добрая пятёрка find-and-replace и понимание того, как данные текут через систему. Финальный прогон: **1169 тестов зелёные**. Версию забампил, коммит в main, и система снова дышит спокойно. Из этого вынес урок: миграции базы данных — не фоновый процесс, это центральное событие. Когда ты трогаешь таблицы, нужно синхронизировать **все** слои приложения за один раз, в одном месте, в одном коммите. Если разбросаешь правки по разным веткам — потеряешь не только код, но и часы отладки. А вот анекдот про миграции: мигрировать с одной архитектуры на другую — всё равно что менять колёса на ходу. На самолёте. 😄

#claude#ai#python#javascript#git#api#security
22 мая 2026 г.
Новая функцияborisovai-site

SEO-метаданные для карточки проекта: как мы это сделали

Когда я делал страницу проекта **Borisov AI** на Next.js, выяснилось, что каждый проект должен иметь собственные метаданные для поисковиков и соцсетей. Задача казалась простой, но дьявол, как всегда, был в деталях. Сначала я подумал: «Просто добавлю `generateMetadata` в `page.tsx` проекта и готово». Но потом понял — нужно учитывать локализацию. Если пользователь смотрит проект на русском, в `<title>` должен быть русский текст. На английском — английский. А заголовок проекта приходит из Strapi API, где может быть либо одно, либо другое. Решение оказалось элегантным: вместо дублирования логики я переиспользовал существующий fetch-запрос к Strapi. Next.js автоматически дедублирует одинаковые запросы в рамках одного рендера, поэтому данные проекта загружаются один раз — и для страницы, и для метаданных. Это сэкономило не только код, но и время отклика. Потом пришлось решить, откуда брать изображение для `og:image`. Нельзя же просто взять первую попавшуюся картинку. В каждом проекте в Strapi может быть thumbnail, и я использовал существующую функцию `getStrapiMediaUrl` для его обработки. Если thumbnail отсутствует — падаем на `/og-default.png`, и это работает. Интересный момент с `canonical` и `hreflang` — они нужны, чтобы поисковики понимали, что русская и английская версии одного проекта — это не дубли, а альтернативные локали. Без этого Google может наказать за дублированный контент. **Вот что получилось:** - Per-project `<title>` с названием из Strapi - `<meta description>` из поля `description` API - Open Graph и Twitter Card с изображением - Правильная разметка для мультиязычности Сама реализация — всего ~30 строк в `generateMetadata`, но она охватывает все кейсы: есть проект — есть метаданные, нет проекта — есть fallback. **Факт о Cypress:** оказывается, фреймворк для e2e-тестирования работает похоже на подростка — непредсказуем, требует постоянного внимания и иногда отказывается сотрудничать без явной переконфигурации 😄

#claude#ai#javascript#api
22 мая 2026 г.
Новая функцияspeech-to-text

Когда regex ломает сборку: охота на призрака в version.py

Работаю над **Speech to Text** — проект с поддержкой CUDA-сборок для GPU-ускорения. Наша система так устроена: CI собирает CPU-версию, а локально я публикую CUDA-релизы через `publish_cuda.sh`. Скрипт берёт версию из `src/version.py`, упаковывает всё, подписывает ed25519-ключом и отправляет на зеркало. Казалось бы, рутина. Но вот беда: при публикации версии 2.0.9 сборка начала брать неправильный номер версии. `build.py` читает версию через regex, и вместо `2.0.9` собралась какая-то `X.Y.Z`. Первый подозреваемый — `src/version.py`. Открываю файл... aha! В файле была строка-пример в docstring-е: `"X.Y.Z"`. И regex в `build.py` её нашла! Это была классическая проблема: regex ищет `__version__ = "..."`, но не якорится к началу строки, так что подхватывает даже примеры в комментариях. Первый фикс: **переместить настоящий `__version__ = "2.0.9"` в самое начало файла** как первое присваивание. Второй фикс: в самом `build.py` добавить якорь `^` с флагом `re.MULTILINE` в regex. Теперь он ищет присваивание только в начале строки — пример в docstring больше не сбивает с толку. Но ладно, сборка прошла. Дальше — запуск на Windows. И тут выясняется, что в `voice_app.spec` в какой-то print-строке была стрелка Unicode `→`, и консоль Windows в кодировке cp1251 не может её вывести. Падает. Заменяю на `->` — готово. Такие мелочи в публикации релизов выглядят наивными, пока не сломают процесс. Regex без якорей, примеры в docstring-ах, которые мешают парсингу, Unicode в местах, где ожидают ASCII — всё это живёт где-то на грани видимости. Поэтому когда что-то вдруг не работает при локальной сборке, стоит смотреть не на сложные части, а на простые: как именно код *читает* данные, что находится рядом с этими данными, и включает ли парсер якори для границ. Кстати, про yakori — напомнило мне шутку про Kubernetes 😄 Почему Kubernetes лучший друг разработчика? Потому что без него ничего не работает. С ним тоже, но хотя бы есть кого винить.

#git#commit#python#security
22 мая 2026 г.