Промты для Rust и Go: как в одиночку тянуть высоконагруженный бэкенд, пока команда из трёх разработчиков тонет в созвонах

Промты для Rust и Go: как в одиночку тянуть высоконагруженный бэкенд, пока команда из трёх разработчиков тонет в созвонах

В сентябре 2026-го я закрывал проект, на который по нормальным прикидкам нужна была команда из трёх человек: gRPC-фасад, два микросервиса на Go, один latency-критичный воркер на Rust, плюс профилирование и ревью. Со мной был только один союзник — LLM. И это не история про «ИИ написал за меня весь код». Это история про то, как правильно сформулированный промт заменяет тимлида, который держит в голове контекст всей системы.

Разница между «ИИ пишет мусор» и «ИИ экономит недели» — почти всегда в постановке задачи. Если попросить «сделай мне сервис на Go», получишь учебный пример из туториала. Если дать модели точный контракт: версию Go, протокол, бюджет по памяти, требования к отмене контекста — получишь рабочий скелет, который останется только довести. Ниже — 10 промтов, которые я реально использовал в этом проекте, с примерами кода и разбором, почему формулировка работает.

1. Промт для старта: спроектируй gRPC-сервис из спецификации

Первый и главный промт. Он превращает .proto-файл в структуру проекта, а не в кашу из файлов.

Промт:

«Ты — Go-разработчик уровня senior. Ниже .proto-контракт. Спроектируй структуру сервиса на Go 1.24: слои (transport / service / repository), где живут интерсепторы, как организуется graceful shutdown. Не пиши бизнес-логику — только скелет с TODO и обоснованием каждого файла. Верни дерево каталогов и содержимое ключевых файлов.»

Пример контракта:

syntax = "proto3";
package orders.v1;

service OrderService {
  rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse);
  rpc GetOrder(GetOrderRequest) returns (GetOrderResponse);
}

Модель выдаёт дерево вида cmd/server, internal/order, internal/transport/grpc, internal/repo/postgres и отдельно — файл с graceful shutdown через signal.NotifyContext. Ключевой момент: просьба обосновать каждый файл заставляет модель не выдумывать лишние абстракции. Один и тот же промт стабильно даёт структуру, которую не стыдно показать на код-ревью.

2. Промт для конкурентности: семафоры и errgroup без гонок

Асинхронное программирование — место, где LLM чаще всего врёт. Лечится явным требованием указать модель памяти и способ синхронизации.

Промт:

«Напиши на Go функцию, которая параллельно дёргает N внешних HTTP-вызовов с лимитом конкурентности K. Обязательно: errgroup.Group с SetLimit, отмена через context.WithCancel, сбор ошибок без потери первой. После кода — объясни, почему здесь достаточно errgroup, и в каком случае понадобился бы sync.Mutex или канал-семафор.»

func fanOut(ctx context.Context, urls []string, k int) ([]string, error) {
    g, ctx := errgroup.WithContext(ctx)
    g.SetLimit(k)
    out := make([]string, len(urls))
    for i, u := range urls {
        i, u := i, u
        g.Go(func() error {
            req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
            if err != nil {
                return err
            }
            resp, err := http.DefaultClient.Do(req)
            if err != nil {
                return err
            }
            defer resp.Body.Close()
            b, err := io.ReadAll(resp.Body)
            if err != nil {
                return err
            }
            out[i] = string(b)
            return nil
        })
    }
    return out, g.Wait()
}

Требование «объясни, когда нужен Mutex» — это бесплатное мини-ревью. Модель почти всегда добавляет предупреждение про захват переменной цикла (актуально до Go 1.22, где семантика изменилась) и про то, что errgroup.SetLimit появился в Go 1.20.

3. Промт для Rust: Send + Sync и почему компилятор ругается

Rust-воркер у меня держал соединения к брокеру и считал агрегаты. Первое, что делает LLM без подсказки, — пихает Arc<Mutex<...>> везде, где видит шаринг. Промт это лечит.

Промт:

«Ниже структура на Rust с tokio. Объясни, почему компилятор требует Send + Sync для задач в tokio::spawn. Перепиши так, чтобы избежать Arc, если состояние можно сделать иммутабельным. Покажи оба варианта и назови trade-off по contention.»

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

use std::sync::Arc;
use tokio::sync::mpsc;

pub struct Worker {
    tx: mpsc::Sender<Job>,
}

impl Worker {
    pub fn spawn(buffer: usize) -> Arc<Self> {
        let (tx, mut rx) = mpsc::channel::<Job>(buffer);
        tokio::spawn(async move {
            while let Some(job) = rx.recv().await {
                process(job).await;
            }
        });
        Arc::new(Self { tx })
    }
}

Главная ценность промта — не код, а раздел «trade-off». Модель объясняет, что канал-очередь снижает contention по сравнению с Mutex на горячем пути, но добавляет аллокации и backpressure. Это ровно тот разговор, который вы бы вели с сеньором на архитектурной сессии.

4. Промт для профилирования: pprof и flamegraph по симптомам

«Сервис тормозит» — бесполезный запрос. Работает такой:

Промт:

«Go-сервис на 12k RPS показывает p99 = 340ms при p50 = 8ms. Подозреваю GC или блокировки на мьютексе. Составь пошаговый план профилирования через net/http/pprof и go tool pprof: какие именно профили снимать (cpu, heap, mutex, block), что искать в каждом, какие команды запускать. Дай список из 5 гипотез с признаком в профиле для каждой.»

Модель выдаёт конкретику: go tool pprof -http=:8080 http://localhost:6060/debug/pprof/profile?seconds=30, затем go tool pprof -top, поиск runtime.gcBgMarkWorker в CPU-профиле как признак GC-давления, sync.(*Mutex).Lock в block-профиле как признак contention. Отдельно — гипотезу про json.Marshal на горячем пути. Это не «магия ИИ», это сжатый конспект официальной документации по pprof, применённый к моему симптому.

5. Промт для бенчмарков: сравни две реализации честно

Когда у меня было два варианта парсера, я не стал писать бенчмарк руками.

Промт:

«Напиши бенчмарк на Go для двух реализаций функции Parse. Используй testing.B, sub-benchmarks, b.ReportAllocs() и b.ResetTimer(). Обе реализации должны быть в одном файле. После кода объясни, почему b.N не фиксируется и как избежать оптимизации компилятором через sink-переменную.»

var sink any

func BenchmarkParse(b *testing.B) {
    for _, tc := range []struct{ name string; fn func([]byte) (any, error) }{
        {"stdlib", ParseStdlib},
        {"custom", ParseCustom},
    } {
        b.Run(tc.name, func(b *testing.B) {
            data := []byte(`{"order_id":"42","amount":1990}`)
            b.ReportAllocs()
            b.ResetTimer()
            for i := 0; i < b.N; i++ {
                v, err := tc.fn(data)
                if err != nil { b.Fatal(err) }
                sink = v
            }
        })
    }
}

b.ReportAllocs() и sink-переменная — это то, что отличает настоящий бенчмарк от игрушки. Модель знает про это только если вы явно попросите.

6. Промт для ревью: найди data race до продакшена

Этот промт я гонял на каждом PR. Он дешёвый и ловит то, что глазами не видно.

Промт:

«Проведи ревью кода ниже как инженер, который ищет только три класса проблем: data race, утечки горутин, необработанные ошибки. Для каждой находки укажи строку, почему это проблема и как исправить. Если проблем нет — так и скажи, не выдумывай.»

Последняя фраза критична. Без неё модель «находит» проблемы там, где их нет, — классический sycophancy. С ней ревью становится пригодным: из десяти PR она реально указывала на забытый defer resp.Body.Close() и на горутину, которая писала в канал без закрытия.

7. Промт для отмены контекста: сквозной context.Context

В микросервисах отмена запроса — источник утечек. Промт для генерации сквозного контекста:

Промт:

«Покажи, как правильно протянуть context.Context от gRPC-хендлера до SQL-запроса в слое repository. Что произойдёт с запросом в Postgres, если клиент отменит вызов? Как проверить это в тесте? Дай код хендлера, репозитория и тест с context.WithTimeout.»

Модель объясняет, что драйвер pgx поддерживает отмену через ctx, и что без проброса контекста запрос продолжит выполняться на сервере даже после разрыва соединения. Тест с context.WithTimeout(ctx, 10*time.Millisecond) и проверкой errors.Is(err, context.DeadlineExceeded) закрывает вопрос.

8. Промт для gRPC-интерсепторов: метрики и трейсинг без боли

Промт:

«Напиши unary-интерсептор на Go, который логирует метод, длительность и код ошибки, и добавляет trace-id из metadata. Покажи, как подключить его в grpc.NewServer. Отдельно — как сделать то же самое в Go 1.24 через новый пакет log/slog, без сторонних логгеров.»

func UnaryLogger(log *slog.Logger) grpc.UnaryServerInterceptor {
    return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, h grpc.UnaryHandler) (any, error) {
        start := time.Now()
        resp, err := h(ctx, req)
        code := status.Code(err)
        log.Info("rpc",
            slog.String("method", info.FullMethod),
            slog.Duration("dur", time.Since(start)),
            slog.String("code", code.String()),
        )
        return resp, err
    }
}

Промт ценен тем, что заставляет модель использовать актуальный log/slog (появился в Go 1.21) вместо устаревших логгеров, и не тащить лишние зависимости.

9. Промт для Rust: async без блокирующих вызовов

Классическая ошибка — вызвать блокирующий код внутри async fn. Промт:

Промт:

«Найди в коде ниже блокирующие вызовы внутри async-функций tokio. Для каждого предложи замену: spawn_blocking или асинхронный аналог. Объясни, почему блокировка runtime-потока опаснее в tokio, чем в потоках ОС. Покажи до и после.»

Модель находит std::fs::read_to_string внутри async fn и меняет на tokio::fs::read_to_string, а для CPU-тяжёлой работы — на tokio::task::spawn_blocking. Объяснение про work-stealing планировщик и starvation — точное и без воды.

10. Промт для документации: ADR из diff'а

Последний, но не по важности. Архитектурные решения забываются через месяц.

Промт:

«Ниже git diff за последнюю неделю. Составь Architecture Decision Record в формате: контекст, решение, альтернативы, последствия. Язык — сухой, без маркетинга. Если из diff'а решение не выводится однозначно — задай мне уточняющий вопрос.»

Последнее предложение превращает промт в диалог. Модель спрашивает: «Почему вы выбрали pgx вместо database/sql — производительность или фичи?» — и это ровно тот вопрос, который я бы забыл записать в ADR.

Что реально даёт этот подход

Задача Без промтов С промтами
Скелет gRPC-сервиса 1–2 дня 2–3 часа
Поиск data race в ревью по остаточному принципу на каждом PR
Профилирование p99 полдня на гипотезы 1 час по чек-листу
ADR по изменениям «потом напишу» генерируется из diff

Цифры — из моего проекта, не универсальная статистика. Но паттерн устойчивый: LLM не заменяет инженера, она заменяет того второго и третьего разработчика, которых вы бы позвали на созвон, чтобы обсудить структуру пакетов. Разница только в том, что созвон стоит час всем, а промт — минуту вам.

Если хотите попробовать — начните с промта №1 на своём реальном .proto, а не на учебном. Именно на реальном контракте модель показывает, умеет ли она думать в вашем контексте, или просто пересказывает туториал. И держите под рукой официальные источники: Go Concurrency Patterns, Rust Async Book, gRPC Go docs — модель иногда ошибается, и проверять её по первоисточникам полезно и вам, и ей.

← Все статьи

Комментарии

Читайте также

Английский язык с AI: как нейросети меняют изучение языка в 2026 году и почему я выбрал этот курс

13 сентября 2026

Data Science для бизнеса: как освоить A/B-тестирование, продуктовые метрики и оптимизацию конверсии в 2026 году

13 сентября 2026

Flutter vs React Native: 12 промтов, которые превращают хаос нативных багов в работающий MVP за выходные

13 сентября 2026

Создание MCP-серверов: как подключить ИИ-агентов к реальным API — и почему этот навык востребован

13 сентября 2026

Промпт-инъекции и защита LLM: как курс «Промпт-инжиниринг ПРО (Prompt Engineering Pro)» учит строить безопасные AI-приложения

13 сентября 2026

RAG-системы с нуля: обзор курса и прогнозы развития технологии до 2027 года

13 сентября 2026

System Design Interview: как подготовиться к FAANG в 2026 году и какие тренды учитывать

12 сентября 2026

Flutter и React Native в 2026: 12 промтов, которые реально экономят часы разработки

12 сентября 2026

Промты для Python-разработчика: 10 сценариев LeetCode и Computer Science на техническом собеседовании 2026

12 сентября 2026