Руководитель группы серверной разработки «Криптонита» Артём Корсаков ведёт проект Scalabook. Это уникальная русскоязычная база знаний по Scala.
И недавно он добавил туда новые страницы — делимся с вами. Отправляйте знакомым «скалистам»!
Руководитель группы серверной разработки «Криптонита» Артём Корсаков ведёт проект Scalabook. Это уникальная русскоязычная база знаний по Scala.
И недавно он добавил туда новые страницы — делимся с вами. Отправляйте знакомым «скалистам»!
Сравнивал разные методы сделать call-once (а точнее инициализацию синглтона) - далее небольшие заметки.
Итак, нам нужно создать какой-то объект, пусть даже что-то нетривиальное (для определённости можно представлять что мы хотим прочитать данные из файла/эмбеддет ресурсов и разложить эти данные в хеш-мапу для будущих чтений). Рассмотрим следующие варианты:
статичный глобальный объект, вычисление вызывается в конструкторе
статичный глобальный объект + std::call_once
статичный локальный объект в функции, вычисление вызывается в конструкторе
Чтобы в годболте видеть что и где вызывается - определяем тип имеющий только объявления методов (это заставит компилятор ставить явный call)
struct TSomeExternal {
TSomeExternal(int);
~TSomeExternal();
int DoCalc();
char Data[60]; // просто чтобы было проще увидеть
};
Особенности первого варианта:
вычисление "до main"
неконтролируемый порядок инициализации глобальных ответов => нельзя иметь зависимости между такими объектами
вычисление только в рамках 1 потока
уникальный плюс: нет проверок "на горячем цикле" - при обращении к объекту вызывающая функция может считать что данные уже готовы (но именно этот плюс стоит нам второго пункта)
В годболте можно посмотреть на func1 - видно что она скомпилилась просто в забор поинтера, и call
TSomeExternal GlobalValue(7);
int func1() {
int x = GlobalValue.DoCalc();
return x + 14;
}
Также видно что были порождены - указание что нужно разместить объект
GlobalValue:
.zero 60
А также _GLOBAL__sub_I_example.cpp - там видно, что вызывается конструктор, и регистрируется указатель на деструктор в пост-колбеках (__cxa_atexit)
Второй вариант (более правдоподобно было бы внести call_once внутрь глобального объекта, но для простоты сравнения сделал как ниже)
static std::once_flag flag;
static std::unique_ptr<TSomeExternal> ptr;
void init() {ptr = std::make_unique<TSomeExternal>(7);}
int func2() {
std::call_once(flag, init);
int x = ptr->DoCalc();
return x + 14;
}
настоящая инициализация случается только в момент первого использования (ленивая инициализация). Это может быть как плюс (позволяет не тратиться на инициализацию если данные не будут использоваться), так и минус (первые запросы/итерации будут медленными)
разные инициализаторы данных вполне могут работать одновременно из разных потоков
имхо - дорогой основной цикл (очень много инструкций до TSomeExternal::DoCalc) - это всё подготовка вызова call pthread_once, который будет выполнен каждый раз
Третий вариант годболт
int func3() {
static TSomeExternal prepared = {7};
int x = prepared.DoCalc();
return x + 14;
}
в рантайме есть проверка - но это проверка и так нужного нам указателя на null
push rbx
movzx eax, byte ptr [rip + guard variable for func3()::prepared]
test al, al
je .LBB0_1
если указатель не нулевой, то переход не будет выполнен и мы сразу переходим к
lea rdi, [rip + func3()::prepared]
call TSomeExternal::DoCalc()@PLT
в случае если указатель не готов, то подготовка случается под блокировкой (__cxa_guard_acquire)
конкретно в годболте не видно, но создаются слоты для guard variable for func3()::prepared и func3()::prepared (в этом смысле разницы со вторым вариантом мало)
Итого: ленивое вычисление, также возможна многопоточная инициализация, а также дешёвый success-path
Я бы назвал относительным неудобством третьего варианта, что указатель оказывается скрыт внутри функции, и надо сделать ещё доп действие чтобы использовать объект из нескольких функций. Пример такой обёртки Singleton.
Друзья, Digital Q.DataBase позволяет Вам не только сохранить прикладную логику СУБД Microsoft и Oracle.
🔹 В связке с другим нашим продуктом, предназначенным для замены SAP NetWeaver, Вы получаете возможность уйти от использования продуктов SAP без переписывания систем и без переписывания бизнес-логики.
Что это означает на практике:
🔹 ABAP-приложения продолжают работать на новой платформе
🔹 Данные и обработка переносятся в Digital Q.DataBase
🔹 Вся бизнес-логика сохраняется без изменений
🔹 Формируется импортонезависимый стек из отечественного ПО
🔹 В этом видео:
ABAP-код → сохранение → активация → преобразование в C++ → компиляция → установка на сервер → запуск
📎 Полезные ссылки
🔹 Отдельный лендинг по замене SAP: renovation.diasoft.ru
🔹 Бесплатное получение СУБД дистрибутива: database.diasoft.ru
🔹 Документация: доступна внутри дистрибутива
🔹 Telegram-сообщество Digital Q.DataBase: t.me/dqdatabase
Как стать Go-разработчиком?
Чтобы стать крутым разработчиком на Go, мало знать язык — нужно понимать как в целом устроена разработка, а еще осваивать инструменты, которые активно применяются на практике и облегчают повседневные задачи.
Ловите подборку таких инструментов — они идеально подойдут как для входа в профессию, так и для развития профессиональных навыков, а на Хабр Карьере уже полно актуальных учебных программ по каждому из них:
— SQL. Работаем с базами данных
— Git. Отслеживаем изменения в коде.
— CI/CD. Автоматизируем сборки, тестирование и развёртывание кода.
— Postman. Тестируем API через отправку HTTP-запросов и анализа ответов.
— Bash. Работаем с операционной системой и пишем скрипты.

Бесплатный тест для Java‑разработчиков: оцени уровень и найди точки роста
Хороший Java‑разработчик — это не просто тот, кто пишет код, а специалист, который держит в голове целую экосистему: от базовых принципов языка до архитектуры распределенных систем.
Чтобы осознанно развиваться дальше, важно понимать:
какие технические навыки уже прокачаны;
где есть пробелы в знаниях архитектуры и проектировании;
какие технологии стоит изучить подробнее;
насколько уверенно ты ориентируешься в современном Java‑стеке.
Мы сделали бесплатный пробный тест — он поможет оценить твой уровень как Java‑разработчика и подсветит зоны для развития.
За 20 минут ты получишь:
оценку текущего уровня;
карту компетенций — наглядную схему всех навыков, необходимых для развития в профессии;
подборку материалов для самостоятельного изучения.
Кстати, мы подробно разбирали ключевые навыки и компетенции Java-разработчика в отдельной статье — рекомендуем прочитать ее или перечитать, чтобы освежить и структурировать знания перед тестом.
🔹 Стоит ещё раз подчеркнуть важную мысль: переход на российскую СУБД не обязательно означает полное переписывание системы.
До сих пор многие не воспринимают это как реальную возможность.
Крупные системы на Oracle или Microsoft можно переводить иначе. Без многолетней переработки всего кода. Достаточно перенести данные и изменить настройки.
При этом важно понимать условие: такой подход работает, если выбранная СУБД изначально к этому подготовлена. В ней должны быть реализованы необходимые доработки для совместимости, включая клонирование функциональности систем Microsoft и Oracle.
Традиционный путь — это огромные команды разработчиков, длительная проверка корректности переписанного кода, принятие сложных архитектурных решений. Более того, в процессе такого переписывания зачастую приходится менять саму архитектуру системы и фактически перестраивать её заново.
🔹 Мы предлагаем другой подход. В нашем подходе меняется само представление о миграции: не обязательно адаптировать приложение под PostgreSQL. Можно пойти другим путём, реализовать в СУБД функциональность, совместимую с зарубежными системами.
🔹 Если бы такой подход начали применять раньше, страна могла бы сэкономить колоссальные ресурсы.
Речь идёт о миллиардах рублей, которые уже ушли и продолжают сегодня уходить на переписывание систем.
📎 Полезные ссылки
🔹 Бесплатное получение дистрибутива: database.diasoft.ru
🔹 Документация: доступна внутри дистрибутива
🔹 Telegram-сообщество Digital Q.DataBase: t.me/dqdatabase

Scala 3.8: три вещи, которые сломают ваш проект при апгрейде
Scala 3.8 вышла в январе 2026. Фич много, но сначала - про то, что может больно ударить при переезде.
1. JDK 17 - жёсткое требование
Никакого JDK 11, никакого JDK 8. Реализация lazy val переписана с sun.misc.Unsafe на VarHandles - потому что в JDK 26 Unsafe будет бросать исключения. Компилятор просто откажется работать на старой JVM.
Если ваша инфраструктура ещё на JDK 11 - апгрейд Scala придётся отложить до апгрейда JVM. Scala 3.3 LTS пока остаётся на JDK 8+.
2. stdlib скомпилирована Scala 3 - и один неочевидный момент
Бинарная совместимость сохранена, но явная передача ClassTag теперь требует using:
// ❌ Перестало компилироваться
Array.empty(reflect.ClassTag.Int)
// ✅
Array.empty(using reflect.ClassTag.Int)
// или просто
Array.empty[Int]
Хорошая новость: компилятор умеет чинить такие места автоматически. Запускаем на Scala 3.7.4 перед апгрейдом:
scalac -source:3.7-migration -rewrite YourFile.scala
3. betterFors меняет тип результата - молча
betterFors теперь включён по умолчанию. Новый дешугаринг убирает промежуточный map - это хорошо для производительности, но меняет поведение кода с Map и несколькими val-алиасами:
// Этот код даёт РАЗНЫЕ результаты в 3.7 и 3.8
val result: Iterable[(Int, Int)] =
for
(k, v) <- Map(1 -> 1, 2 -> 1, 3 -> 1)
x = k + v
(a, b) = f(x)
(y, z) <- Map(42 -> 27)
yield (a + y, b + z)
// 3.7 → List((43,29), (43,30), (43,31))
// 3.8 → Map(43 -> 31)
В 3.7 синтетический map превращал Map в Iterable. В 3.8 этого шага нет - Map остаётся Map, дубли ключей исчезают. 3.8.2 выдаёт предупреждение на такие места. Не игнорируйте.
Фикс простой - явно приводить к Seq:
(k, v) <- Map(1 -> 1, 2 -> 1, 3 -> 1).toSeq
В своем канале в Telegram и в канале Max о разработке в стартапах рассказываю ещё больше интересного и делюсь опытом, заходите, найдете полезные кейсы!
И ещё одно: не берите 3.8.0 и 3.8.1
Сразу после релиза в 3.8.0 нашли JVM linkage errors при использовании Scala 2.13 библиотек. 3.8.1 закрыл это, но оставил баг в for-comprehension. Стабильная версия — 3.8.2 (вышла 24 февраля 2026).
Обновляться - только на неё.
WT CDEK library v.1.3.0 - обновление PHP SDK для Joomla + CDEK.

Небольшая нативная PHP Joomla библиотека для работы с API v.2 службы доставки CDEK. Библиотека представляет собой клиент для авторизации в CDEK API по OAuth, работы с некоторыми методами API: получения ряда данных и расчета стоимости доставки. Поддерживается Joomla 4.2.7 и выше.
В пакет входят:
библиотека Webtolk/Cdekapi
системный плагин System — WT Cdek для хранения настроек и AJAX‑интеграций
task‑плагин Task — Update WT Cdek data для обновления локальных копий справочников CDEK по расписанию
web asset с официальным JavaScript‑виджетом СДЭК
👉 v.1.3.0. Что нового?
Полный рефакторинг библиотеки. Библиотека переработана в entity‑based API с фасадом Cdek и отдельным слоем запросов. Обратная совместимость не нарушена, поэтому версия библиотеки — 1.3.0.
Добавлена поддержка новых разделов API СДЭК. Добавлена поддержка новых разделов API СДЭК: webhooks, prealert, печатные формы, payment, passport, reverse, intakes и других сущностей.
Улучшена интеграция с Joomla. Улучшена интеграция с Joomla: installer script для layouts, новые поля Joomla Form для тарифов и обновлённые js виджета CDEK.
документация библиотеки. Все методы библиотеки подробно описаны, а так же текст документации собран в отдельной папке в git репозитории.
Пример запроса — запрос информации о городе.
<?php
use Webtolk\Cdekapi\Cdek;
\defined('_JEXEC') or die;
// Вариант 1: брать credentials из настроек плагина
$cdek = new Cdek();
// Вариант 2: передать credentials явно
$cdek = new Cdek(test_mode: true, client_id: 'your_client_id', client_secret: 'your_client_secret');
$result = $cdek->location()->getCities([
'postal_code' => '410012',
'city' => 'Саратов',
'size' => 1,
]);
Результат запроса:
Array
(
[0] => Array
(
[code] => 428
[city_uuid] => 7e54a0b3-76f0-41e2-92e0-f1e600ad84fd
[city] => Саратов
[fias_guid] => bf465fda-7834-47d5-986b-ccdb584a85a6
[country_code] => RU
[country] => Россия
[region] => Саратовская область
[region_code] => 47
[fias_region_guid] => df594e0e-a935-4664-9d26-0bae13f904fe
[sub_region] => городской округ Саратов
[longitude] => 46.034266
[latitude] => 51.533562
[time_zone] => Europe/Saratov
[payment_limit] => -1
)
)Библиотека эта нужна для разработчиков, создающих свои расширения для интеграции Joomla и курьерской службы CDEK.
Зелёные тесты ≠ хорошие тесты
Впервые в истории писать тесты стало легко и совсем не страшно. Вокруг теперь у всех покрытие 80%, 90%, а то и вовсе 100%. И вот тут начинается проблема: зелёные тесты ≠ хорошие тесты.
Проблема в метрике, которой мы все привыкли доверять. Code coverage считает строку протестированной, если она выполнилась во время теста. Всё. Не «поймает ли тест баг в этой строке», не «проверяет ли он правильность результата» — просто выполнилась. Можно написать тест без единого assert, и покрытие вырастет. 500 тестов, 90% coverage, а пользы ноль.
Мутационное тестирование — это совершенно другой путь. В простейшей реализации этот инструмент тупо берёт твой код и намеренно ломает его: меняет > на >=, + на ‑, True на False. Каждая такая поломка — мутант. Если после мутации все тесты по‑прежнему зелёные — значит они ничего не проверяют. Покрытие есть, защиты нет.
Почему это важно именно сейчас?
Потому что нейронка любит зелёненькое. Чем больше зелёных тестов — тем субъективно лучше. 100 тестов внушают больше доверия, чем 10, правда? А внутри там assert response.status_code == 200. assert result is not None. assert len(items) > 0. Тест проверяет, что функция вернула хоть что‑то — и радостно зеленеет. Поменяй логику условия, перепутай знак, сломай граничный случай — тест всё равно зелёный. Потому что он проверяет не правильность, а наличие.
Мутационное тестирование — единственный автоматический способ это поймать. Метрика называется mutation score: процент убитых мутантов. 60% — плохо. 90%+ — тесты реально что‑то защищают.
Кое‑какие инструменты для такого тестирования уже есть: mutmut и cosmic‑ray для Python, Stryker для JS/TS, PIT для Java. Медленно? Да, значительно медленнее обычного тест‑рана. Но запускать его не нужно на каждый коммит — достаточно на PR в критические модули.
Но есть нюансы. А где их нет, правда?
Первый: мутации рандомные. Замена > на >= — это не баг, который кто‑то реально допустит. Это синтетическая поломка. Половина мутантов генерирует код, который в реальности никогда не появится. Ты тратишь время на убийство мутантов, которые не имеют отношения к настоящим ошибкам. Это как тестировать замок, ковыряя его вилкой — формально проверка, по факту мимо.
Второй — ещё хуже. Чтобы убить мутанта, тест должен зафиксировать конкретное поведение. Каждую ветку, каждое значение, каждый edge case. Доведи mutation score до 100% — и ты прибил гвоздями каждую строчку кода. Буквально. Теперь попробуй отрефакторить. Переименовал внутренний метод — 40 тестов красные. Поменял порядок полей в ответе — ещё 20. Тесты превращаются из страховки в кандалы: код работает правильно, но тесты падают, потому что они проверяют не поведение, а реализацию.
Это реально ловушка. Слишком гонишься за mutation score — получаешь хрупкие тесты. Не гонишься — получаешь видимость тестирования.
Перемены — впереди!
И вот тут становится по‑настоящему интересно. Представь, что мутации генерирует не тупой набор правил «замени плюс на минус», а нейронка, которая понимает контекст. Которая знает, какие баги реально встречаются в таком коде. Которая мутирует не синтаксис, а логику: меняет порядок проверок, путает граничные условия, забывает обработать edge case — ровно так, как ошибается человек. Или другая нейронка.
Сейчас есть явный сдвиг в сторону таких инструментов, но всё еще ничего достойного не вышло. Но уже скоро точно появится. И это будет совсем другой уровень. Не «выжили ли тесты после рандомной поломки», а «выжили ли тесты после правдоподобной ошибки».
Парадокс в том, что мутационное тестирование было нишевым инструментом, пока тесты писали люди. Когда тесты пишет нейронка — идея становится обязательной. Правда инструменты пока не успели дозреть.
Ждём, когда мутанты станут умнее.
«Просто добавь кнопку» и недели работы
Однажды заказчик пришёл с задачей, которая звучала как пара часов работы «Просто добавь кнопку — нажал, выгрузил данные, всё». Я открыла код и поняла, что эта кнопка стоит не пару дней, а недель — и это если повезёт..
Сложнее всего оказалось не сделать, а объяснить так, чтобы услышали.
С технической стороны всё понятно: сервис писался под дедлайн, архитектура не предусматривала роста, и каждое новое изменение тянет за собой минимум три соседних. Но заказчик смотрит на задачу и видит один экран. Кнопки ещё нет, но она же просто кнопка. Что тут может быть сложного?
Архитектурное объяснение я попробовала и оно не зашло. Слои, связи, зависимости: всё правильно, всё мимо.
Что работает вместо «красивого кода»
Я перестала объяснять как устроено и начала объяснять что произойдёт. Не «тут монолитная структура без инверсии зависимостей», а конкретно: — эта кнопка затрагивает три модуля, которые никто не трогал два года — если что‑то сломается, то мы не узнаем сразу, потому что тестов нет — следующая фича после этой будет стоить столько же в лучшем случае.
Заказчик услышал третий пункт. Именно его.
Нетехнический человек воспринимает разработку примерно так: «нажал кнопку → произошла магия → получил результат». Это не незнание — просто другая роль. Заказчик и не должен думать об архитектуре, это моя работа. Значит, говорить на его языке — тоже моя.
Как я считаю стоимость следующей фичи
Со временем сложился свой фреймворк. Не из учебника, а из разговоров, где меня не понимали, пока я не поменяла подход.
Три вещи, которые я оцениваю перед тем, как называть сроки: 1. Базовая сложность: сколько займёт в идеальных условиях, на нормальной архитектуре. 2. Архитектурный коэффициент — во сколько раз реальность дороже идеала. Код без тестов, с жёсткими связями между модулями — это 2-4× к оценке. Не абстракция: вот здесь нельзя менять, не затронув вот это. Рисую буквально на бумаге. 3. Риск‑налог — что может пойти не так. Что сломается, насколько быстро заметят, сколько займёт починка. Не чтобы напугать, а чтобы показать, что «быстро» и «надёжно» здесь в противоречии.
Когда эти три числа стоят рядом — разговор меняется. Заказчик видит, что не «разработчик тормозит», а «вот цена, вот риск, вот выбор».
Именно тогда и появляется разговор про рефакторинг. Не потому что «код некрасивый», а потому что каждая следующая фича будет дороже предыдущей, если ничего не менять.
Что осталось в голове
Техдолг — это не технический вопрос. Это финансовый.
Пока объясняешь его как технический — тебя не услышат. Как только переводишь в деньги, сроки и риски — начинают слышать.
Самое сложное не методология. Самое сложное — поймать момент, когда ты всё ещё говоришь на своём языке, а не на их. У меня ушло время, чтобы это почувствовать.
А вы как объясняете техдолг тем, кому важен результат, а не архитектура? Есть формулировка, которая сработала лучше всего?
Валидация полей форм в Joomla 6
У каждого поля в форме Joomla есть поле type, но за валидацию значения отвечает атрибут validate.
Например: number — очевидно что значение должно быть числом.
<field
name="count"
type="number"
label="MOD_ARTICLES_FIELD_COUNT_LABEL"
description="MOD_ARTICLES_FIELD_COUNT_DESC"
default="5"
filter="integer"
min="0"
validate="number"
/>Или UserId — тут сложнее, значение должно быть реальным id сужествующего пользователя.
<field
name="default_value"
type="user"
label="PLG_FIELDS_USER_DEFAULT_VALUE_LABEL"
validate="UserId"
/>А в моём компоненте нужна валидация id компании.
Добавляем в поле атрибут validate="CompanyId":
<field
name="company_id"
type="text"
label="COM_WISHBOXBONUSSYSTEM_FIELD_COMPANY_LABEL"
required="true"
validate="CompanyId"
/>Добавляем класс правила (в моём случае по сути копия правила UserId):
<?php
/**
* @copyright (c) 2013-2026 Nekrasov Vitaliy <nekrasov_vitaliy@list.ru>
* @license GNU General Public License version 2 or later;
*/
namespace Joomla\Component\WishboxBonusSystem\Administrator\Form\Rule;
use Joomla\CMS\Form\Form;
use Joomla\CMS\Form\FormRule;
use Joomla\Database\DatabaseAwareInterface;
use Joomla\Database\DatabaseAwareTrait;
use Joomla\Database\ParameterType;
use Joomla\Registry\Registry;
use SimpleXMLElement;
use function defined;
// phpcs:disable PSR1.Files.SideEffects
defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Form Rule class for the Joomla Platform.
*
* @since 1.0.0
*/
class CompanyIdRule extends FormRule implements DatabaseAwareInterface
{
use DatabaseAwareTrait;
/**
* Method to test the validity of a Joomla User.
*
* @param SimpleXMLElement $element The SimpleXMLElement object representing the `<field>` tag for the form field object.
* @param mixed $value The form field value to validate.
* @param ?string $group The field name group control value. This acts as an array container for the field.
* For example if the field has name="foo" and the group value is set to "bar" then the
* full field name would end up being "bar[foo]".
* @param ?Registry $input An optional Registry object with the entire data set to validate against the entire form.
* @param ?Form $form The form object for which the field is being tested.
*
* @return boolean True if the value is valid, false otherwise.
*
* @since 1.0.0
*
* @noinspection PhpMissingReturnTypeInspection
*/
public function test(SimpleXMLElement $element, $value, $group = null, ?Registry $input = null, ?Form $form = null)
{
// Check if the field is required.
$required = ((string) $element['required'] === 'true' || (string) $element['required'] === 'required');
// If the value is empty, null or has the value 0 and the field is not required return true else return false
if (($value === '' || $value === null || (string) $value === '0')) {
return !$required;
}
// Get the database object and a new query object.
$db = $this->getDatabase();
$query = $db->createQuery();
// Build the query.
$query->select('COUNT(*)')
->from($db->qn('#__wishboxbonussystem_companies'))
->where($db->qn('id') . ' = :companyId')
->bind(':companyId', $value, ParameterType::INTEGER);
// Set and query the database.
return (bool) $db->setQuery($query)->loadResult();
}
}
И в модели нашей сущности (в моём случае OperationModel) подключаем префикс класса:
public function getForm($data = [], $loadData = true)
{
FormHelper::addRulePrefix("\\Joomla\\Component\\WishboxBonusSystem\\Administrator\\Form\\Rule");
return parent::getForm($data, $loadData);
}Два факта об int в Python
Один забавный факт привел меня к открытию другого :)
Читал Fluent Python и наткнулся на пример кода, который меня заинтересовал (помимо миллиона других, книга – топ). В главе про конкурентность и работу GIL была константа NUMBERS с необычным значением:
NUMBERS = 5_000_111_000_222_021Нижние подчеркивания
Если не встречали в работе или документации, то вряд ли знаете (как и я): в числах можно использовать _ для читаемости. Интерпретатор их игнорирует:
>>> x = 1_2
>>> y = 12
>>> x == y
True
>>> x is y
TrueОсобенно удобно в высокоразрядных числах. Согласитесь 5_000_111_000_222_021 куда проще читать, чем 5000111000222021
Кеш малых чисел
Примеры ниже разбирал на домашнем ноуте с Cpython 3.13.11 и 3.14.3.
Пока игрался, меня заинтересовал один прикол. Я попробовал тот же пример с большими числами:
>>> x = 100_500
>>> y = 100500
>>> x == y
True
>>> x is y
False # Но ведь в примере выше было True..Почему переменные больше не ссылаются на один объект?
В Cpython есть кеш для маленьких чисел, чтобы частые значения переменных не занимали много памяти и код был отзывчивее.
Ответ на вопрос: «где граница, до которой числа закешированы?» я решил не гуглить, проверил небольшим скриптом:
>>> x = 0
>>> y = 0
>>> for n in range(1000):
... print(f'If {x=} and {y=}, x is y: {x is y}')
... x += 1
... y += 1
# Пропустим часть строк
If x=254 and y=254, x is y: True
If x=255 and y=255, x is y: True
If x=256 and y=256, x is y: True
If x=257 and y=257, x is y: False # Вот и граница
If x=258 and y=258, x is y: False
...Сначала я сделал эмпирически вывод, что закеширован диапазон 0 – 256. Но после самопроверки с гуглом узнал, что также в амортизированный диапазон входят числа от -5 до -1. Итого : от -5 до 256 включительно.
UPD 15.03.2026. Добрый дядя в комментах принес ссылку на pr в Cpython 3.15, где кеш малых чисел увеличен до 1024 :). Ух, заживем..
Для присвоения переменным чисел вне диапазона, интерпретатор начнет выделять уже раздельные области памяти и is станет возвращать False.
Так то. В оптимизации пригодится вряд ли, но удивить друзей в баре сможете.
Неудобные вопросы про бэкап PostgreSQL: открытый разбор на вебинаре
Вокруг бэкапа PostgreSQL легко создать иллюзию, что все уже решено. Достаточно добавить в текст WAL, PITR, пару слов про консистентность и назвать агент «умным». Проблема в том, что в проде такие формулировки мало что гарантируют.
Можно ли вообще считать решение PostgreSQL-aware, если оно не живет внутри логики самой СУБД? Где проходит граница между нативными механизмами PostgreSQL и внешней платформой? Что происходит, если не доехал WAL-сегмент, не завершился post-script или восстанавливать нужно не весь инстанс, а один объект?
Из таких вопросов и вырос отдельный вебинар про PostgreSQL в Акуре, в формате открытого инженерного разбора: что здесь должна делать сама СУБД, что имеет смысл выносить во внешний слой, где начинаются реальные эксплуатационные проблемы и какие ограничения в таком подходе нельзя замалчивать.
План такой:
отдельно пройтись по WAL, PITR и консистентности;
обсудить, где файловый агент уместен, а где уже нет;
разобрать сценарии с ошибками pre/post-скриптов;
поговорить про восстановление в безопасную локацию и ручной recovery;
отдельно затронуть вопрос масштаба: почему на двух базах хватает shell-скриптов, а на пятидесяти уже начинается совсем другая жизнь.
26 марта 2026, 11:00 (МСК) Регистрация по ссылке. Приносите в комментарии вопросы, которые особенно хочется поднять в эфире.
ai_query() в StarRocks 4.1: вызываем LLM прямо из SQL. Разбор результатов тестов.
Зачем это нужно аналитику и как вписывается в архитектуру, я описал в своем Telegram-канале Selena (powered by StarRocks). Здесь — технические детали и результаты тестирования.

На схеме два потока данных между языковой моделью и базой данных:
Синий (вверху) — LLM → База через MCP (4.0). Пользователь задаёт вопрос на обычном языке. Агент сам формулирует SQL-запрос, отправляет его в StarRocks через MCP-протокол и возвращает ответ. Об этом я также подробно писал в нашем сообществе.
Зелёный (внизу) — База → LLM через ai_query() (4.1). Аналитик пишет SELECT с вызовом ai_query(). StarRocks на каждом сервере кластера отправляет запрос к языковой модели и возвращает её ответ как обычную текстовую колонку.
В версии 4.0 появилось первое направление, в 4.1 — второе. Полный цикл.
Что такое ai_query()
Функция принимает два аргумента: текстовый промпт и JSON с параметрами модели. Возвращает текстовую колонку — результат можно фильтровать, группировать и соединять с другими таблицами.
Обязательные параметры: model (название модели) и api_key (ключ доступа). Дополнительно можно указать адрес сервера модели, температуру, максимальную длину ответа и таймаут.
Функция работает с любым сервисом, совместимым с протоколом OpenAI: это и сам OpenAI, и локальные модели через Ollama, и DeepSeek, и vLLM.
Как тестировали:
Функция планируется к релизу в версии 4.1. Когда пришло время её проверить, привычный способ — развернуть готовый образ в Docker — не сработал. В образе обнаружился небольшой баг: функция была скомпилирована и лежала внутри сервера, но сервер о ней не знал. Исправление заняло одну строку в исходном коде. Но чтобы её применить, пришлось собирать BE из исходников.
Среда тестирования: виртуальная машина (8 CPU, 32 ГБ RAM), StarRocks 4.1.0-rc01 (собранный из исходников), языковая модель Ollama gemma3:1b (работает локально на процессоре). Тестовые данные — шесть отзывов о товарах.
Тест 1. Анализ тональности
Задача: определить, позитивный отзыв или негативный.
(SQL код по каждому тестированию я напишу в комментариях)
Вывод: четыре из шести точных.
Модель на один миллиард параметров делает бинарную классификацию — не различает нейтральные отзывы. Я, кстати, попробовал и с большими параметрами и с меньшим квантованием, насколько смог выдержать мой сервер, результат локальных моделей в этой задаче не очень.
Время: ~три секунды на шесть строк.
Не тот объем данных, чтобы экстраполировать на большие продакшн системы, но я тестировал не производительность, а работоспособность.
Тест 2. Суммаризация
Задача: сжать отзыв в одно предложение.
Вывод: адекватные резюме на русском языке. Длину ответа стоит контролировать параметром max_tokens.
Время: ~одна секунда на строку.
Тест 3. Извлечение характеристик
Задача: вытащить из текста ключевые свойства товара.
Вывод: характеристики извлекаются
Время: ~1 секунда на строку.
Тест 4. Классификация
Задача: определить категорию товара по тексту отзыва.
Вывод: категории определены верно. MacBook, монитор, наушники — «Электроника», мышь — «Периферия».
Время: ~0.5 секунды на строку.
Тест 5. Перевод
Задача: перевести отзыв с русского на английский.
Вывод: качественный перевод даже на модели в один миллиард параметров.
Время: ~1 секунда на строку.
Ограничения:
Нельзя задать роль модели (нет системного промпта) — только сообщение от пользователя
Нет повторных попыток при ошибке — если сервис модели вернул ошибку, это сразу ошибка SQL-запроса
Кеш хранится на каждом сервере отдельно и теряется при перезапуске
Итого:
ai_query() — простая обёртка над протоколом языковых моделей с кешем и дедупликацией. Не революция, но именно такие простые интеграции оказываются самыми полезными.
Функция появится в StarRocks 4.1.
JEP 524 в JDK 26 — второй preview PEM API
Наконец-то работа с PEM в Java становится похожа на API, а не на набор ручного парсинга, Base64 и странных телодвижений.
Справка: PEM или Privacy-Enhanced Mail - это текстовый контейнер для криптографических данных. Проще говоря – это способ хранить или передавать ключ, сертификат или другой crypto-объект не в бинарном виде, а в текстовом.
Раньше с PEM работали так:
String pem = "-----BEGIN PUBLIC KEY-----\n"
+ Base64.getMimeEncoder(64, "\n".getBytes())
.encodeToString(publicKey.getEncoded())
+ "\n-----END PUBLIC KEY-----";А в обратную сторону, но уже с ручной нормализацией PEM, Base64-декодированием и KeyFactory:
String normalized = pem
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replaceAll("\\s", "");
byte[] der = Base64.getDecoder().decode(normalized);
PublicKey key = KeyFactory.getInstance("EC")
.generatePublic(new X509EncodedKeySpec(der));По факту PEM в Java долгое время был не отдельным API, а набором низкоуровневых шагов, которые разработчик собирал руками.
А теперь это выглядит так:
var encoder = PEMEncoder.of();
String pem = encoder.encodeToString(keyPair);
var decoder = PEMDecoder.of();
KeyPair decoded = decoder.decode(pem, KeyPair.class);То есть ключевую пару можно закодировать в PEM и декодировать обратно буквально в несколько строк.
Во втором preview:
PEMRecord переименовали в PEM
добавили decode()
расширили поддержку KeyPair и PKCS8EncodedKeySpec
упростили шифрование через EncryptedPrivateKeyInfo
А так, как все это дело еще в preview, не забываем использовать --enable-preview.
❓ Минус еще один кусок криптографической копипасты из Java-кода. PEM в Java постепенно перестает быть унылым?

Присоединяйтесь к русскоязычному сообществу разработчиков на Spring Boot в телеграм — Spring АйО, чтобы быть в курсе последних новостей из мира разработки на Spring Boot и всего, что с ним связано.
Что нас ждёт в StarRocks 4.1
В документации StarRocks появились release notes для 4.1 с пометкой RC (release candidate) — это предварительная версия перед финальным релизом. Посмотреть, куда движется проект, самое время. Я изучил release notes, связанные issues и PR, и выбрал четыре самых значимых изменения.
Ссылка на описание релиза: https://docs.starrocks.io/releasenotes/release-4.1/
Актуальные версии на сегодня: Stable — 3.5.14, Latest — 4.0.6.
1. Автоматическое управление распределением данных
Раньше при создании таблицы в shared-data кластере нужно было вручную выбирать ключ распределения и рассчитывать количество бакетов. Если ошибся — часть узлов перегружена, а часть простаивает, и исправление требует пересоздания таблицы.
В 4.1 для shared-data кластеров появляется range-based распределение: таблеты содержат метаданные диапазонов ключей, и система сама следит за их размером — автоматически разделяет слишком большие или объединяет недоиспользуемые. Без изменения схемы и без перезагрузки данных.
На практике: меньше ручной настройки при создании таблиц, меньше проблем с неравномерной нагрузкой. Issue #64986 (https://github.com/StarRocks/starrocks/issues/64986)
2. DELETE для Iceberg-таблиц
До 4.1 StarRocks мог только читать данные из Iceberg и добавлять новые (INSERT). Удалять было нельзя. А это серьёзное ограничение: удаление персональных данных по требованиям регуляторов, исправление ошибочных записей, очистка устаревших данных — всё приходилось делать через Spark или Trino.
Теперь DELETE FROM (механизм Iceberg position delete) работает напрямую из StarRocks. При этом delete-файлы совместимы с другими движками — Spark, Trino и Flink корректно их прочитают. StarRocks становится ещё более полноценным SQL-движком для Iceberg: SELECT + INSERT + DELETE. Issue #66944 (https://github.com/StarRocks/starrocks/issues/66944)
3. Рекурсивные CTE (WITH RECURSIVE)
Одна из самых запрашиваемых фич — сообщество просило с 2023 года. Рекурсивные CTE позволяют писать запросы, которые ссылаются сами на себя — это нужно для обхода иерархий (оргструктуры, категории товаров, вложенные комментарии), заполнения пропусков во временных рядах и графовых задач. Если вы мигрируете с PostgreSQL, MySQL или Trino — больше не нужно переписывать рекурсивные запросы. PR #65932 (https://github.com/StarRocks/starrocks/pull/65932)
4. Инкрементальное обновление Materialized Views на Iceberg
До 4.1 materialized views на Iceberg-таблицах обновлялись полным пересчётом — даже если в источнике добавилось несколько строк. Теперь StarRocks умеет обновлять MV инкрементально — обрабатывается только новая порция данных. Особенно заметно на append-heavy сценариях: логи, события, IoT-данные. Ограничение первой версии — работает только с таблицами, в которые данные добавляются, но не обновляются. Issue #61789 (https://github.com/StarRocks/starrocks/issues/61789)
Что ещё интересного:
Полнотекстовый поиск в shared-data кластерах (inverted index, beta)
Таблеты до 100 ГБ
Меньше мелких файлов, проще эксплуатация
Поддержка Iceberg V3 и тип VARIANT для полуструктурированных данных
ai_query()
вызов LLM-моделей прямо из SQL-запроса
sum_map() — нативная агрегация MAP по ключам
Мониторинг потоков FE через SQL без внешних инструментов
Больше постов про StarRocks и Lakehouse — в Telegram-канале @starrocks_selena
Разворачиваем приватную LLM в кластере Kubernetes в прямом эфире

Начинаем через 30 минут, в 12:00 мск. Эксперты вместе с вами пройдут весь путь от подготовки кластера до готового интерфейса: настроят мониторинг, распределенные модели, интеграции и даже автоматизацию инфраструктуры.
Будет полезно DevOps- и SRE- и ML-инженерам, архитекторам и продакт-менеджерам.
О чем поговорим
— Почему клиенты все чаще выбирают приватные LLM.
— Как выделенные GPU-серверы решают задачи безопасности, производительности и стоимости.
— Сравним стоимость владения облаком и физическими серверами на реальных кейсах.
— Почему Kubernetes — идеальная платформа для управления LLM в проде.
— Воркшоп: разворачиваем приватную LLM от подготовки кластера до удобного интерфейса.
Подключайтесь к трансляции в VK и на YouTube.
Как проверить HTTP-запросы на Backend
Разрабатываете VK Mini Apps и хотите быть уверены, что HTTP-запросы на backend приходят именно из приложения VK, а не откуда угодно? Для этого нужна корректная проверка Init Data.
Наш backend-разработчик @dmvasiliev выложил Open Source-проект, который упрощает эту задачу. Это Python-пакет с готовыми алгоритмами проверки подлинности данных, передаваемых из VK Mini Apps. Он помогает быстро и безопасно настроить авторизацию и аутентификацию на backend-стороне приложения.
👉 Репозиторий
📘 Документация с примерами интеграции для Django и FastAPI.
Когда мы только начинали работать с VK Mini Apps, информации было немного: редкие кейсы, почти не у кого было спросить совета. За это время мы запустили несколько приложений, разобрались в нюансах платформы и накопили собственную экспертизу. Теперь делимся ею с сообществом через Open Source-проекты и вносим вклад в развитие технологии.
Репозиторий открыт — берите в работу, делитесь постом с коллегами.
А если вам нужен VK, Telegram Mini App или спецпроект на другой платформе — команда Doubletapp поможет пройти путь от идеи до работающего продукта.
Примеры наших проектов — на сайте.
22 открытых урока марта для бэкенд-разработчиков

Привет, Хабр. Делимся подборкой бесплатных демо-уроков, которые проведут в марте преподаватели Otus. Это не предзаписанные, а живые онлайн-встречи — сможете узнать больше о формате обучения и задать вопросы экспертам. Выбирайте свою тему и присоединяйтесь!
11 марта 20:00
Сообщения, которые не теряются: Брокеры против хаоса в Java
Открытый вебинар курса «Разработчик на Java. Профессиональный уровень»
12 марта 20:00
Системный дизайн: как устроены реальные ИТ-проекты — от архитектуры до интеграции машинного обучения
Открытый вебинар курса «MLOps» (Эксплуатация и сопровождение моделей машинного обучения)
12 марта 20:00
Типовая архитектура современной ИТ-системы
Открытый вебинар курса «Системный аналитик. Руководитель»
12 марта 20:00
Практикум проектирования приложения с помощью предметно-ориентированное проектирование (DDD)
Открытый вебинар курса «Архитектура и шаблоны проектирования»
16 марта 20:00
Списывай, но только точь-в-точь: копирование базы данных на примере MySQL
Открытый вебинар курса «Администрирование Linux. Профессиональный уровень»
16 марта 20:00
PostgreSQL и секционирование: «разделяй и властвуй!»
Открытый вебинар курса «PostgreSQL для администраторов баз данных и разработчиков»
17 марта 20:00
Как перейти на ClickHouse, когда PostgreSQL уже не хватает?
Открытый вебинар курса «ClickHouse для инженеров и архитекторов баз данных»
17 марта 20:00
Работа с данными и сетями в Docker
Открытый вебинар курса «DevOps практики и инструменты»
18 марта 18:00
API — учим модель общаться с внешним миром
Открытый вебинар курса «MLOps» (Эксплуатация и сопровождение моделей машинного обучения)
18 марта 20:00
API Gateway и не только: шаги к идеальной архитектуре внешних API
Открытый вебинар курса «Предметно-ориентированное проектирование (DDD)и асинхронная архитектура»
19 марта 20:00
Кафка — работа с сообщениями в форматах Avro и Protobuf
Открытый вебинар курса «Разработчик на Java. Профессиональный уровень»
19 марта 20:00
Основы многопоточности в Java. Асинхронные методы.
Открытый вебинар курса «Инженер по автоматизации тестирования на Java. Профессиональный уровень»
23 марта 19:00
SSH: безопасный доступ и автоматизация в Linux
Открытый вебинар курса «Администрирование Linux. Базовый уровень»
24 марта 20:00
Технология контейнеризации. Введение в Docker
Открытый вебинар курса «DevOps практики и инструменты»
25 марта 20:00
Кракозябры против Java: как победить кодировки и стать Гуру Unicode?
Открытый вебинар курса «Разработчик на Java»
25 марта 20:00
Инструменты профилирования и оптимизации в Go
Открытый вебинар курса «Разработчик на Golang. Профессиональный уровень»
25 марта 20:00
Механизмы блокировок в PostgreSQL
Открытый вебинар курса «PostgreSQL для администраторов баз данных и разработчиков»
26 марта 20:00
Путешествие запроса в PostgreSQL: от парсера до executor'а
Открытый вебинар курса «Администрирование PostgreSQL. Экспертный уровень»
26 марта 20:00
NestJS и архитектура масштабируемых серверных приложений на Node.js
Открытый вебинар курса «Разработчик на Node.js»
26 марта 20:00
Проектирование RAG систем
Открытый вебинар курса «ИИ-архитектор»
30 марта 20:00
Spring Boot Actuator: основы мониторинга и управления приложением
Открытый вебинар курса «Разработчик на Spring»
31 марта 20:00
SQL: Оконные функции — когда GROUP BY уже не хватает
Открытый вебинар курса «SQL для разработчиков и аналитиков»
Полный список бесплатных уроков марта по всем ИТ-направлениям (разработка, тестирование, архитиктура, инфраструктура, аналитика, ИИ, управление) смотрите в дайджесте.
Для начинающих рекомендуем обратить внимание на короткие видеокурсы, которые помогут закрепить основы, сейчас всего за 10 рублей:
Учимся писать промпты правильно
Я когда начинал погружаться в агентскую разработку, постоянно испытывал легкое чувство стыда, за то, что делаю все как колхозник, а не как "настоящий инженер", который значит продумал задачу, разложил на шаги, обсудил с ии, дальше итеративно, задав кучу условий и ограничений начал реализовывать.
Мой подход "надо сделать вот это" и дальше уже куда поведет его и куда поведет меня. И это действительно было проблемой, когда я работал в агентах без режима планирования. Доходило до смешного, ты хотел с ним поговорить, он начинает херачить, ты ему "стоять нахер", он там уже наломал дров. В итоге постоянно откатывался сам либо просил откатывать его. В целом, это тоже хороший опыт, понимания того как вообще такие системы работают, где у них границы, как лучше формулировать и так далее.
Но в любом случае, даже это не приучило меня как-то сидеть и расписывать, потому что ты никогда точно не знаешь, куда поведет агента на чем он запнется. Тебе мерещится одно, а он начинает тупить ваще в неожиданном месте.
А потом появился режим планирования. И в этот момент, все эти обучалки и принципы того как надо заранее хорошо подумать над задачей превратились в тыкву. Даже если вы начинаете мычать что-то в терминале, современные агенты сами направят, сами зададут вопросы и подсветят важные моменты. Главное в целом понимать что вам нужно и каким будет финальный результат. В этот момент мне перестало быть стыдно.
Вдруг меня осенило, слишком сильное обдумывание до начала работы в агентской разработке (последние полгода), стало чем-то сродни преждевременной оптимизации. Поработав с большим числом моделей и агентов, могу сказать, что если вы будете сами все продумывать, то заставите работать более менее нормально даже самые тупые модели, но вы никогда не поймете границы их возможностей, где они могут еще больше взять на себя освободив вас от рутины. А модели и агенты постоянно развиваются, там где раньше надо было подсказывать, теперь они могут сами. Плюс если вы работаете над AGENTS.md, скилами и mcp, то и тут мы ловим буст.
Поэтому сейчас я иду по такому пути. Включаю режим планирования и дальше даю короткое описание задачи и подробный контекст.
Для упавшего ci это выглядит так:
Поправь тесты
Прошу посмотреть последний запущенный билд на github actions
Баг в браузере:
прошу открыть страницу (он это делает через mcp chrome) и самому изучить
Для рефакторинга:
Переводим вот это на это
Вот эталон (тут ссылка на файл)
Для фичи:
Нужно реализовать такую фичу
Даю пример или говорю с помощью какого инструмента
И все. Самые продвинутые модели типа opus 4.6 или codex 5.3 справятся самостоятельно с большинством острых углов. Сами спросят объем, посмотрят разные варианты, изучат доку и так далее. Модели по тупее, не сделают глубокого анализа и ничего особо не спросят, но именно тут вы поймете где их надо вести и включать свой мозг чаще. Хотя хорошие модели настолько сильно помогают, что я физически не могу использовать более простые для задач планирования. Они хороши в авторежиме только для работы по аналогии, рефакторинг, написание тестов и так далее.
Даже если вы что-то забудете или пропустите сразу, все это можно будет доуточнить, попросить показать примеры кода, сходить открыть браузер. Если в процессе появляются вещи, которые агент делает из раза в раз, например, пытается выяснить где что-то лежит, как работать с какой-то частью системы, то постепенно это выносится в AGENTS.md и с определенного размера и уровни сложности (когда уже нужны скрипты например и команды) выносится в skill.