MidТеория7 min

Quality Gates для AI-генерированного кода

Контрольные точки качества при работе с AI-ассистентами

Что такое Quality Gates

Quality Gates (контрольные точки качества) -- это заранее определённые критерии, которые код должен пройти, прежде чем перейти на следующий этап разработки. Если хотя бы один критерий не выполнен -- продвижение блокируется.

В традиционной разработке Quality Gates существовали всегда: код-ревью, CI/CD проверки, ручное тестирование перед релизом. Но с появлением AI-ассистентов роль этих контрольных точек кардинально меняется.

Ключевой принцип: AI-генерированный код требует БОЛЬШЕ проверок, а не меньше. Недетерминированный выход требует детерминированной верификации.

Почему Quality Gates критичны именно для AI-кода

Между кодом, написанным человеком, и кодом, сгенерированным AI, есть принципиальная разница:

Характеристика Код человека AI-генерированный код
Предсказуемость Высокая (знаем стиль разработчика) Низкая (зависит от промпта и контекста)
Консистентность Стабильная в рамках навыков Может варьироваться от идеального до ужасного
Скрытые ошибки Типичные для уровня разработчика Могут выглядеть правильно, но содержать тонкие баги
Скорость генерации Медленная (есть время на обдумывание) Мгновенная (нет паузы на рефлексию)
Объём за единицу времени Умеренный Огромный

Высокая скорость генерации создаёт иллюзию продуктивности. Разработчик получает 200 строк кода за минуту вместо 20 за час -- и подсознательно снижает планку проверки. Именно здесь Quality Gates становятся спасением.

Quality Gate Framework для AI-assisted разработки

Gate 1: Pre-Generation Gate (Перед генерацией)

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

Критерии прохождения:

  • Задача чётко сформулирована (что делаем, зачем)
  • Определены acceptance criteria (как понять, что готово)
  • Известны ограничения (технологии, паттерны проекта, запреты)
  • Подготовлен контекст для AI (релевантные файлы, примеры кода)
  • Edge cases продуманы до генерации
# Example: pre-generation checklist in YAML
pre_generation_gate:
  task_definition:
    what: "REST API endpoint for creating orders"
    why: "Customers need to place orders via mobile app"
    acceptance_criteria:
      - "POST /api/v1/orders creates order and returns 201"
      - "Validates required fields: product_id, quantity, shipping_address"
      - "Returns 422 with field-level errors for invalid input"
      - "Idempotent: duplicate requests with same idempotency_key return existing order"
    constraints:
      - "Must use existing OrderRepository pattern"
      - "Must follow project naming conventions"
      - "No raw SQL — use Doctrine ORM only"
    edge_cases:
      - "Product out of stock"
      - "Invalid product_id"
      - "Quantity exceeds maximum (1000)"
      - "Concurrent creation with same idempotency_key"

Правило: Если вы не можете написать acceptance criteria -- вы не готовы генерировать код. Вернитесь к анализу требований.

Gate 2: Post-Generation Gate (Сразу после генерации)

Первичная проверка сгенерированного кода, которая занимает 2-5 минут, но предотвращает часы отладки.

Критерии прохождения:

  • Код компилируется / не содержит синтаксических ошибок
  • Код соответствует спецификации (все acceptance criteria покрыты)
  • Нет очевидных проблем (TODO, placeholder-ы, mock-данные)
  • Архитектура соответствует проекту (правильные слои, зависимости)
  • Именование следует конвенциям проекта
# Quick post-generation checks

# PHP: syntax + static analysis
php -l src/Controller/OrderController.php
vendor/bin/phpstan analyse src/Controller/OrderController.php --level=9

# Go: compile + vet
go build ./cmd/api/...
go vet ./internal/order/...

# TypeScript: type check
npx tsc --noEmit src/api/orders.ts

# Universal: check for placeholders and TODOs
grep -rn "TODO\|FIXME\|PLACEHOLDER\|CHANGEME\|xxx\|your-.*-here" src/

Частые проблемы на этом этапе:

  1. Галлюцинации API -- AI использует несуществующие методы библиотек
  2. Несовместимость версий -- код для старой версии фреймворка
  3. Placeholder-ы -- // TODO: implement this замаскированные под рабочий код
  4. Лишние зависимости -- AI добавляет пакеты, которые не нужны

Gate 3: Testing Gate (Тестирование)

AI может генерировать тесты -- но кто проверяет, что тесты правильные? Тестирование AI-кода требует особого внимания.

Критерии прохождения:

  • Unit-тесты проходят (все зелёные)
  • Integration-тесты проходят
  • Edge cases покрыты тестами
  • Тесты проверяют поведение, а не реализацию
  • Нет "тавтологических" тестов (тест проверяет то же, что и код)
  • Code coverage для нового кода >= 80%
<?php

declare(strict_types=1);

// ANTI-PATTERN: tautological test generated by AI
// This test just mirrors the implementation — it proves nothing
final class BadOrderTest extends TestCase
{
    public function testCreateOrder(): void
    {
        $order = new Order(
            productId: 1,
            quantity: 5,
            shippingAddress: '123 Main St'
        );

        // These assertions just verify the constructor works
        // They don't test any business logic
        $this->assertEquals(1, $order->getProductId());
        $this->assertEquals(5, $order->getQuantity());
    }
}

// GOOD PATTERN: behavior-focused test
final class GoodOrderTest extends TestCase
{
    public function testRejectsQuantityAboveMaximum(): void
    {
        $this->expectException(InvalidQuantityException::class);
        $this->expectExceptionMessage('Quantity cannot exceed 1000');

        new Order(
            productId: 1,
            quantity: 1001,
            shippingAddress: '123 Main St'
        );
    }

    public function testIdempotentCreation(): void
    {
        $service = new OrderService($this->orderRepository);
        $idempotencyKey = 'unique-key-123';

        $first = $service->create($this->validData, $idempotencyKey);
        $second = $service->create($this->validData, $idempotencyKey);

        $this->assertSame($first->getId(), $second->getId());
        $this->assertCount(1, $this->orderRepository->findAll());
    }

    /**
     * @dataProvider invalidInputProvider
     */
    public function testRejectsInvalidInput(array $data, string $expectedError): void
    {
        $this->expectException(ValidationException::class);
        $this->expectExceptionMessage($expectedError);

        $this->service->create($data);
    }

    public static function invalidInputProvider(): array
    {
        return [
            'missing product_id' => [
                ['quantity' => 1, 'shipping_address' => 'addr'],
                'product_id is required',
            ],
            'zero quantity' => [
                ['product_id' => 1, 'quantity' => 0, 'shipping_address' => 'addr'],
                'Quantity must be at least 1',
            ],
            'empty address' => [
                ['product_id' => 1, 'quantity' => 1, 'shipping_address' => ''],
                'shipping_address is required',
            ],
        ];
    }
}

Gate 4: Security Gate (Безопасность)

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

Критерии прохождения:

  • Нет SQL-инъекций (все запросы параметризованы)
  • Нет XSS (все данные экранированы на выходе)
  • Нет хардкоженных секретов
  • Валидация всех входных данных
  • Проверка авторизации на каждом эндпоинте
  • Нет path traversal в работе с файлами
  • Пройден SAST-скан
# Automated security checks

# PHP: security-focused static analysis
vendor/bin/phpstan analyse --level=9 -c phpstan-security.neon src/

# Go: security scanner
gosec ./...

# Universal: secret scanning
gitleaks detect --source=. --no-git

# SAST with Semgrep (supports PHP, Go, JS, Python, etc.)
semgrep scan --config=auto --config=p/owasp-top-ten src/

# Dependency vulnerability check
composer audit          # PHP
go vuln check ./...     # Go (govulncheck)
npm audit               # JavaScript

Gate 5: Performance Gate (Производительность)

AI оптимизирует на уровне строки, но часто упускает системные проблемы: N+1 запросы, отсутствие индексов, утечки памяти.

Критерии прохождения:

  • Нет N+1 запросов к базе данных
  • Запросы используют индексы (EXPLAIN проверен)
  • Время ответа API < SLA (например, < 200ms для p95)
  • Потребление памяти в допустимых пределах
  • Нет блокирующих операций в hot path
-- Check for missing indexes on foreign keys
SELECT
    tc.table_name,
    tc.constraint_name,
    kcu.column_name
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu
    ON tc.constraint_name = kcu.constraint_name
LEFT JOIN pg_indexes pi
    ON pi.tablename = tc.table_name
    AND pi.indexdef LIKE '%' || kcu.column_name || '%'
WHERE tc.constraint_type = 'FOREIGN KEY'
    AND pi.indexname IS NULL;
# Performance benchmarks
# API endpoint benchmarking with k6
k6 run --vus 50 --duration 30s loadtest.js

# Go benchmarks
go test -bench=. -benchmem ./internal/order/...

# PHP profiling with Xdebug/Blackfire
# Check for N+1 with Symfony Profiler doctrine panel

Gate 6: Review Gate (Код-ревью)

Финальная проверка человеком. Даже если все автоматические Gates пройдены, человеческий ревью незаменим.

Критерии прохождения:

  • Код понятен другому разработчику без объяснений
  • Архитектурные решения обоснованы
  • Нет over-engineering (AI склонен к избыточным абстракциям)
  • Naming конвенции соблюдены
  • Документация обновлена (если нужно)
  • Нет дублирования с существующим кодом

Важно: При ревью AI-кода обращайте особое внимание на "слишком красивый" код. AI часто создаёт элегантные абстракции, которые никогда не понадобятся (YAGNI violation). Простое решение почти всегда лучше.

Автоматизация Quality Gates

Pre-commit hooks

Первая линия обороны. Ловит проблемы ещё до того, как код попадёт в репозиторий.

#!/bin/bash
# .git/hooks/pre-commit

set -e

echo "Running pre-commit quality gates..."

# Gate: Formatting
echo "==> Checking code formatting..."
php-cs-fixer fix --dry-run --diff src/
prettier --check "src/**/*.{ts,vue}"

# Gate: Static Analysis
echo "==> Running static analysis..."
vendor/bin/phpstan analyse src/ --level=9 --no-progress
npx tsc --noEmit

# Gate: Security
echo "==> Scanning for secrets..."
gitleaks detect --staged --no-banner

# Gate: No debug artifacts
echo "==> Checking for debug statements..."
grep -rn "dd(\|dump(\|var_dump(\|console\.log(" src/ && {
    echo "ERROR: Debug statements found!"
    exit 1
} || true

echo "All pre-commit gates passed!"

CI/CD Pipeline

Полноценная проверка в изолированной среде:

# .github/workflows/quality-gates.yml
name: Quality Gates

on: [pull_request]

jobs:
  gate-compile:
    name: "Gate: Compilation"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: PHP Syntax Check
        run: find src/ -name "*.php" -exec php -l {} \;
      - name: TypeScript Check
        run: npx tsc --noEmit

  gate-tests:
    name: "Gate: Tests"
    needs: gate-compile
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Unit Tests
        run: vendor/bin/phpunit --testsuite=unit
      - name: Integration Tests
        run: vendor/bin/phpunit --testsuite=integration
      - name: Coverage Check
        run: |
          vendor/bin/phpunit --coverage-text --coverage-clover=coverage.xml
          # Fail if coverage below 80% for new code
          python scripts/check-coverage.py coverage.xml --min=80

  gate-security:
    name: "Gate: Security"
    needs: gate-compile
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: SAST Scan
        run: semgrep scan --config=auto --config=p/owasp-top-ten src/
      - name: Dependency Audit
        run: composer audit --no-dev
      - name: Secret Scan
        run: gitleaks detect --source=.

  gate-quality:
    name: "Gate: Code Quality"
    needs: gate-compile
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Static Analysis
        run: vendor/bin/phpstan analyse src/ --level=9
      - name: Code Style
        run: vendor/bin/php-cs-fixer fix --dry-run --diff src/
      - name: Architecture Rules
        run: vendor/bin/deptrac analyse

  gate-performance:
    name: "Gate: Performance"
    needs: [gate-tests]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Load Test
        run: k6 run --vus 10 --duration 10s tests/loadtest.js
      - name: Query Analysis
        run: php bin/console app:analyze-queries --max-time=100ms

Предотвращение Gate Fatigue

Gate Fatigue -- это состояние, когда разработчик начинает игнорировать или обходить Quality Gates, потому что их слишком много, они слишком медленные или генерируют ложные срабатывания.

Стратегии борьбы с Gate Fatigue

1. Приоритизация Gates:

Критичные (блокируют всегда):
  - Компиляция
  - Тесты
  - Security scan

Важные (блокируют в CI):
  - Static analysis
  - Code coverage
  - Performance benchmarks

Рекомендательные (не блокируют):
  - Code style suggestions
  - Complexity warnings
  - Documentation coverage

2. Быстрый feedback loop:

Gate Максимальное время Где запускать
Lint / Format < 5 секунд Pre-commit hook
Compile / Type check < 15 секунд Pre-commit hook
Unit tests < 30 секунд Pre-push hook
Static analysis < 1 минута CI pipeline
Integration tests < 5 минут CI pipeline
Security scan < 3 минуты CI pipeline
Performance tests < 10 минут CI pipeline (nightly)

3. Инкрементальные проверки:

Проверяйте только изменённые файлы, а не весь проект:

# Only analyze changed files
CHANGED_FILES=$(git diff --name-only HEAD~1 -- '*.php')
if [ -n "$CHANGED_FILES" ]; then
    vendor/bin/phpstan analyse $CHANGED_FILES --level=9
fi

Метрики эффективности Quality Gates

Чтобы понять, работают ли ваши Quality Gates, отслеживайте эти метрики:

Ключевые метрики

Метрика Описание Целевое значение
Pass Rate % прохождений с первой попытки > 70%
Time-to-Pass Среднее время от первого запуска до полного прохождения < 15 минут
Defect Escape Rate Баги, пропущенные Gates и найденные в production < 5%
False Positive Rate Ложные срабатывания < 10%
Gate Execution Time Общее время прохождения всех Gates < 15 минут

Пример дашборда

Quality Gates Dashboard — March 2026
=====================================

Overall Pass Rate:         78% (target: >70%) [OK]
Avg Time-to-Pass:         12 min (target: <15 min) [OK]
Defect Escape Rate:        3% (target: <5%) [OK]
False Positive Rate:       14% (target: <10%) [NEEDS ATTENTION]

By Gate:
  Pre-Generation:    92% pass rate | avg 2 min
  Post-Generation:   85% pass rate | avg 3 min
  Testing:           71% pass rate | avg 8 min
  Security:          88% pass rate | avg 4 min  | 12% false positives
  Performance:       95% pass rate | avg 6 min
  Review:            65% pass rate | avg 45 min

Top Failure Reasons:
  1. Missing edge case tests (23%)
  2. PHPStan level 9 violations (18%)
  3. N+1 query detection (12%)
  4. Incomplete input validation (11%)
  5. Naming convention violations (9%)

Практический пример: полный цикл с Quality Gates

Рассмотрим реальный сценарий -- разработка эндпоинта с помощью AI-ассистента:

Шаг 1: Pre-Generation Gate

## Task: Create Order API Endpoint

**What:** POST /api/v1/orders -- creates a new order
**Why:** Mobile app needs order placement functionality
**Who:** Authenticated customers (ROLE_USER)

### Acceptance Criteria:
1. Creates order with product_id, quantity, shipping_address
2. Returns 201 with order details on success
3. Returns 422 with validation errors on invalid input
4. Idempotent via X-Idempotency-Key header
5. Checks product stock availability
6. Dispatches OrderCreated event

### Constraints:
- Use existing CreateOrderCommand + handler pattern
- Doctrine ORM, no raw SQL
- Parameterized queries only
- All money values in cents (integer)

### Edge Cases:
- Product out of stock → 409 Conflict
- Duplicate idempotency key → return existing order
- Quantity > 1000 → 422 validation error
- Non-existent product → 404

Чеклист пройден: задача сформулирована, критерии определены, ограничения указаны, edge cases продуманы. Gate PASSED.

Шаг 2: Генерация и Post-Generation Gate

AI сгенерировал код. Проверяем:

# Does it compile?
php -l src/Controller/Api/V1/OrderController.php    # OK
php -l src/Command/CreateOrderCommand.php             # OK
php -l src/Handler/CreateOrderHandler.php             # OK

# Static analysis
vendor/bin/phpstan analyse src/Controller/Api/V1/OrderController.php --level=9
# OK: no errors

# Check for placeholders
grep -rn "TODO\|FIXME\|PLACEHOLDER" src/Controller/Api/V1/ src/Command/ src/Handler/
# No matches found

# Check architecture (handler in correct namespace, no circular deps)
vendor/bin/deptrac analyse --config-file=deptrac.yaml
# OK: no violations

Компилируется, нет placeholder-ов, архитектура соответствует. Gate PASSED.

Шаг 3-6: Остальные Gates

Каждый последующий Gate строится по тому же принципу: определённые критерии, автоматизированные проверки, чёткий результат pass/fail.

Ключевые выводы

"Работа с AI-агентами требует БОЛЬШЕ строгости, БОЛЬШЕ структуры, БОЛЬШЕ качества кода -- а не меньше."

  1. Quality Gates -- это не бюрократия, а страховка. Скорость AI-генерации делает Gates единственным надёжным механизмом контроля качества.

  2. Автоматизируйте максимально. Каждый Gate, который можно проверить автоматически, должен быть автоматизирован. Человеческий ревью -- для того, что автоматизировать невозможно.

  3. Быстрый feedback -- ключ к adoption. Если Gates работают медленно, разработчики будут их обходить.

  4. Pre-Generation Gate -- самый важный. Чем лучше спецификация, тем выше качество генерации, тем меньше нагрузка на последующие Gates.

  5. Измеряйте эффективность Gates. Без метрик вы не знаете, работают ли ваши контрольные точки.

  6. Итерируйте. Quality Gates -- не статичный процесс. Анализируйте defect escape rate и добавляйте новые проверки для типов ошибок, которые проскальзывают.