Самый частый вопрос про исполняемый стандарт: «это же просто SonarQube?». Нет. Но и не вместо. Эти инструменты живут на разных уровнях абстракции и дополняют друг друга, а не конкурируют.
Эта статья — таблица сравнения по 12 параметрам и разбор «когда что использовать».
Резюме за 30 секунд
- SonarQube / ESLint / Detekt — для багов уровня кода и стиля. Регексы, AST-анализ, безопасность.
- Code review тимлидом — для архитектуры и домена. Не масштабируется на команду 20+.
- Исполняемые правила агента с набором правил — для архитектуры и домена. Масштабируются. Дополняют линтеры, не заменяют.
Все три должны работать одновременно в зрелой команде. Линтер ловит NPE, исполняемое правило ловит «событие зарегистрировано не в корне агрегата», человек смотрит на «правильно ли вообще выбрана граница агрегата».
Полная таблица сравнения
| Параметр | SonarQube/ESLint | Тимлид-ревью | Исполняемые правила агента |
|---|---|---|---|
| Что проверяет | стиль, security-баги, complexity | архитектура, домен | архитектура, домен |
| Глубина понимания | синтаксис + AST | семантика + контекст | семантика + контекст (через набор правил) |
| Кто исполняет | статический анализатор | человек | LLM-агент |
| Кодифицировано | плагины + конфиг | в голове | markdown в репо |
| Ссылка в обзоре | да (java:S1234) | нет | да (AGG-4) |
| Масштабируется | да | нет (узкое место) | да |
| Задержка обзора | секунды (CI) | дни (тимлид reads) | минуты (claude run) |
| False positives | средне (~10–20%) | низко | средне (~10–15%) |
| Понимает домен | нет | да | да |
| Версионируется | релиз плагина | устные правила | git diff на наборе правил |
| Открытость | open-source движок | в голове | репо команды |
| Lifecycle правил | релизы плагинов | неформально | git, semantic diff |
Где они НЕ конкурируют
Возьмём один и тот же PR — handler оплаты заказа:
public void handle(PayOrder cmd) {
Order order = orders.findById(cmd.orderId()).get();
order.setStatus(OrderStatus.PAID);
order.setPaidAt(Instant.now());
orders.save(order);
events.publishEvent(new OrderPaid(order.id(), order.total()));
}
func (h *PayOrderHandler) Handle(ctx context.Context, cmd PayOrderCommand) error {
order, err := h.orders.FindByID(ctx, cmd.OrderID)
if err != nil {
return err
}
order.Status = OrderStatusPaid
order.PaidAt = time.Now()
if err := h.orders.Save(ctx, order); err != nil {
return err
}
return h.events.Publish(ctx, OrderPaidEvent{ID: order.ID, Total: order.Total})
}
async handle(cmd: PayOrderCommand): Promise<void> {
const order = await this.orders.findById(cmd.orderId);
order.status = OrderStatus.PAID;
order.paidAt = new Date();
await this.orders.save(order);
await this.events.publish(new OrderPaidEvent(order.id, order.total));
}
from datetime import datetime, timezone
def handle(self, cmd: PayOrderCommand) -> None:
order = self.orders.find_by_id(cmd.order_id)
order.status = OrderStatus.PAID
order.paid_at = datetime.now(tz=timezone.utc)
self.orders.save(order)
self.events.publish(OrderPaidEvent(id=order.id, total=order.total))
SonarQube скажет:
java:S3655—Optional.get()безisPresent()— может броситьjava:S1192— string literal "PAID" дублируется (если есть в других местах)
Исполняемое правило скажет:
AGG-4— событие зарегистрировано не в корне агрегатаENT-3— публичные сеттеры вместо бизнес-методаEVT-4—events.publishEvent()без транзакционных гарантий
Тимлид скажет:
- «Этот use case вообще должен быть
Order.pay()или application servicePaymentSaga? У нас же платёж асинхронный и приходит через webhook.»
Три уровня. Три инструмента. Все три нужны.
Когда что брать
Только линтер
- Команда < 5 инженеров.
- Простой CRUD-проект, не доменно-сложный.
- Нет архитектурных стандартов в голове техлида.
Линтер + тимлид-ревью
- Команда 5–10.
- Есть техлид с устоявшейся архитектурой.
- Архитектура держится на людях, не формализована.
Линтер + исполняемые правила + тимлид
- Команда 10+.
- Несколько сервисов, риск drift между ними.
- Архитектурные паттерны достаточно устоялись, чтобы их кодифицировать.
Линтер + тимлид (без исполняемых правил)
- Ранний прототип, всё меняется еженедельно.
- Команда не доверяет AI-обзорам.
- Архитектура ещё не устоялась.
Что общего у всех трёх
Все три — это инструменты обзора, а не замены ответственности. Линтер не пишет код за вас. Тимлид не подписывает PR за разработчика. Исполняемое правило не принимает архитектурные решения. Все они — усилители, не подменители.
Качество ревью на любом из трёх уровней зависит от качества исходных стандартов:
- ESLint без хорошей конфигурации — шум.
- Тимлид без устоявшихся принципов — субъективные капризы.
- Исполняемое правило без хорошего набора правил — обобщённый обзор уровня «улучши читаемость».
Качественный набор правил — то, что отличает зрелую команду от слепого копирования чужих практик.
Что исполняемые правила делают, чего не делают линтеры
Есть один класс правил, который традиционные линтеры принципиально не достанут:
- Правила про доменный слой. «Имя value object — существительное в единственном числе». Линтер не знает что такое value object и какое имя «доменное», а какое нет.
- Правила про границы. «Один use case изменяет один агрегат, остальные через события». Линтер не знает где проходят границы агрегатов в вашей доменной модели.
- Правила про намерение. «Имя класса соответствует доменному термину из ubiquitous language». Линтер не знает ubiquitous language.
- Правила про контекст. «В одних проектах маппинг идёт через отдельный mapper-класс, в других — через сгенерированный». Линтер не знает соглашения конкретного проекта и не выбирает правила в зависимости от них.
LLM умеет всё это потому что читает прозу правил вместе с прозой кода. Для LLM «доменный термин» и «не доменный» — это вопрос контекста, а не AST-узла.
Что линтеры делают, чего не делают исполняемые правила
И обратное:
- Скорость. Линтер за 3 секунды просмотрит 100K строк. Исполняемое правило — десятки секунд на PR.
- Детерминированность. Линтер на одном и том же коде даст один и тот же результат. LLM — с вариациями.
- Низкоуровневые баги. NPE, deadlock, off-by-one. Это работа AST-анализатора.
- Security-сканирование. SQL injection, XSS, hardcoded secrets — паттерны более чёткие.
Дальше
- Исполняемый стандарт — основная статья про подход