Промты, которые превращают ChatGPT в Go-сеньора: каналы, горутины, gRPC и pprof без боли
Go — язык, который прощает мало: забыл закрыть канал — получил deadlock, не ограничил параллелизм — словил OOM, не поставил таймаут на gRPC — сервис висит под нагрузкой. При этом LLM отлично генерируют Go-код, если правильно сформулировать задачу. Проблема в том, что большинство промтов вида «напиши код на Go» дают усреднённый результат: без context, без errgroup, без учёта GOMAXPROCS.
Ниже — 12 промтов, которые я собрал и обкатал на реальных задачах: от генерации идиоматичного кода до ревью горутин, профилирования через pprof и написания бенчмарков. Каждый промт — с описанием задачи, готовым текстом для LLM и примером. Формулировки проверены на актуальных версиях Go (1.21+) и стандартной библиотеке.
| Промт | Когда применять | Ключевая выгода |
|---|---|---|
| 1. Идиоматичный код | Пишете новый пакет | Соответствие Effective Go |
| 2. Ревью горутин | Проверка перед мержем | Ловля утечек и race |
| 3. Каналы и паттерны | Пайплайны, fan-in/out | Без deadlock |
| 4. gRPC-сервис | Новый микросервис | Полный boilerplate с interceptors |
| 5. Оптимизация pprof | Профилирование CPU/heap | Конкретные hotspots |
| 6. Бенчмарки | Замер производительности | testing.B с b.ReportAllocs |
| 7. Контекст и таймауты | Отмена операций | Правильные context.WithTimeout |
| 8. Синхронизация | Мьютексы, atomic | Выбор примитива под задачу |
| 9. Тесты с race detector | CI-проверки | go test -race сценарии |
| 10. Обработка ошибок | Обёртка ошибок | errors.Is/As, %w |
| 11. Пул воркеров | Ограничение параллелизма | Без утечек горутин |
| 12. Чтение чужого кода | Разбор legacy | Карта зависимостей |
1. Идиоматичный Go-код без «Java-акцента»
Задача: сгенерировать код, который не стыдно выложить в open source — с правильными именами, ранними возвратами и без избыточных абстракций.
Промт:
Ты — опытный Go-разработчик. Напиши пакет
[имя], который [задача]. Требования: следуй Effective Go (https://go.dev/doc/effective_go) и Code Review Comments (https://go.dev/wiki/CodeReviewComments). Используй короткие имена переменных, ранние возвраты, не создавай интерфейсы «на будущее». Обрабатывай ошибки черезfmt.Errorfс%w. Добавь doc-комментарии к экспортируемым символам. Не используй сторонние зависимости, только стандартную библиотеку. Покажи финальный код и поясни, почему выбрал именно такие имена и структуру.
Пример: «Напиши пакет retry, который повторяет операцию с экспоненциальной задержкой и учитывает context.Context». На выходе — функция Do(ctx, attempts, fn) с time.NewTimer и проверкой ctx.Err().
2. Ревью горутин и поиск утечек
Задача: перед мержем проверить, не остаются ли горутины висеть после завершения функции.
Промт:
Проанализируй код ниже как ревьюер Go. Найди: (1) горутины, которые не завершаются при отмене контекста; (2) каналы без закрытия или без читателя; (3) блокировки на
sync.Mutexбезdefer. Для каждой проблемы укажи строку, объясни сценарий утечки и предложи исправление. Используй терминологию из документацииruntime/pprofиnet/http/pprof.
Пример: код с go func() { ch <- result }() без буферизации — LLM укажет, что если читатель отменит контекст, горутина заблокируется навсегда.
3. Паттерны каналов: fan-in, fan-out, pipeline
Задача: построить конкурентный пайплайн без классических ошибок.
Промт:
Реализуй паттерн [fan-in / fan-out / pipeline] на Go. Требования: используй
sync.WaitGroupдля ожидания воркеров, закрывай каналы только на стороне отправителя, поддерживай отмену черезcontext.Context. Покажи схему потоков данных текстом, затем код. Сравни с альтернативой наgolang.org/x/sync/errgroupи объясни, когда что выбирать.
Пример: «Fan-out: 10 воркеров читают из jobs, пишут в results». LLM выдаст for i := 0; i < workers; i++ { go worker(ctx, jobs, results) } с корректным закрытием.
4. gRPC-сервис с interceptors
Задача: сгенерировать сервер и клиент gRPC с логированием, таймаутами и метриками.
Промт:
Сгенерируй gRPC-сервис на Go по proto-файлу ниже. Добавь: unary interceptor для логирования (с
request_id), interceptor для recovery от паник, таймаут по умолчанию 5 секунд черезcontext.WithTimeout. Используйgoogle.golang.org/grpcиgoogle.golang.org/grpc/credentials/insecureдля локального запуска. Покажи командуprotocдля генерации кода и структуру проекта.
Пример: сервис UserService с методом GetUser — на выходе server.go, client.go, interceptors.go и Makefile.
5. Профилирование через pprof
Задача: найти узкие места в сервисе под нагрузкой.
Промт:
Объясни, как профилировать Go-сервис через
net/http/pprof. Дай: (1) код подключения/debug/pprof/к HTTP-серверу; (2) командыgo tool pprofдля CPU и heap профилей; (3) как читать top, list, web. Затем проанализируй вывод профиля ниже и предложи 3 конкретные оптимизации с оценкой эффекта.
Пример: после go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30 видно, что 40% CPU уходит на encoding/json — LLM предложит заменить на jsoniter или перейти на protobuf.
6. Бенчмарки по всем правилам
Задача: написать бенчмарки, которые реально что-то измеряют.
Промт:
Напиши бенчмарки для функции
[имя]в файле_test.go. Используйtesting.B,b.ResetTimer(),b.ReportAllocs(). Добавь табличные кейсы сb.Run. Предупреди о типичных ошибках: компилятор может выбросить результат — используйb.ReportAllocsи глобальную переменнуюsink. Сравни сgo test -bench=. -benchmemи объясни, что означаетns/opиB/op.
Пример: бенчмарк для функции конкатенации строк — покажет разницу между +, strings.Builder и bytes.Buffer.
7. Контекст, таймауты и отмена
Задача: добавить корректную отмену во все операции.
Промт:
Отрефактори код ниже, добавив
context.Contextпервым аргументом во все функции, которые делают I/O. Используйcontext.WithTimeoutдля внешних вызовов,context.WithCancelдля ручной отмены. Не передавайnilконтекст — используйcontext.TODO()только как временную заглушку. Покажи, как пробрасыватьrequest_idчерезcontext.WithValueи почему это ок только для request-scoped данных.
Пример: HTTP-хендлер с вызовом БД — LLM добавит ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second) и defer cancel().
8. Мьютексы, atomic и выбор примитива
Задача: не блокировать горутины там, где можно обойтись atomic.
Промт:
У меня есть счётчик, который инкрементируется из 1000 горутин. Сравни три подхода:
sync.Mutex,sync/atomicиsync.RWMutex. Для каждого покажи код, объясни, когда он выигрывает по производительности, и приведи бенчмарк. Учти, чтоatomic.Int64доступен с Go 1.19.
Пример: для простого счётчика atomic.AddInt64 быстрее мьютекса в разы, но для группы связанных полей нужен мьютекс.
9. Тесты с race detector
Задача: поймать data race до продакшена.
Промт:
Напиши тест для функции ниже, который запускает её из нескольких горутин и проверяет отсутствие data race. Используй
go test -race. Покажи, как воспроизвести race намеренно (через общий слайс без мьютекса), и как его исправить. Объясни, что race detector не находит все гонки — только те, что реально произошли во время запуска.
Пример: тест с 100 горутинами, инкрементирующими общий счётчик — с -race упадёт, после исправления пройдёт.
10. Обработка ошибок по-гошному
Задача: уйти от if err != nil { return err } без контекста.
Промт:
Перепиши обработку ошибок в коде ниже. Используй
fmt.Errorfс%wдля обёртки,errors.Isиerrors.Asдля проверки. Создай sentinel-ошибки черезerrors.Newи кастомный тип ошибки с полями. Покажи, как не терять原始 ошибку и почемуerrors.Isработает через цепочку обёрток.
Пример: ошибка от БД оборачивается как fmt.Errorf("get user %d: %w", id, err), а наверху проверяется errors.Is(err, sql.ErrNoRows).
11. Пул воркеров с ограничением
Задача: обработать 100 000 задач, но не запустить 100 000 горутин.
Промт:
Реализуй пул воркеров на Go: N воркеров читают задачи из канала, результат пишут в другой канал. Добавь
context.Contextдля отмены,sync.WaitGroupдля ожидания, обработку паник в воркере. Покажи, как ограничить N черезruntime.NumCPU()и почему не стоит ставить N больше числа ядер для CPU-bound задач.
Пример: парсер 1 млн URL с 20 воркерами — потребление памяти стабильно, в отличие от go func() на каждую задачу.
12. Разбор чужого кода
Задача: быстро понять legacy-сервис и найти в нём проблемы.
Промт:
Проанализируй Go-код ниже. Составь карту зависимостей между функциями, найди циклические импорты, неиспользуемые экспортируемые символы, места без обработки ошибок. Оцени сложность каждой функции и предложи, что рефакторить в первую очередь. Формат ответа: таблица «функция — проблема — приоритет».
Пример: на входе 500 строк legacy — на выходе список из 10 точек, где нужно вмешаться.
Как использовать эти промты
Главное правило: LLM не знает ваш проект. Чем больше контекста вы дадите — версия Go, используемые библиотеки, ограничения по памяти — тем точнее ответ. Всегда проверяйте сгенерированный код через go vet, staticcheck и go test -race. Ни один промт не заменяет профилирование на реальной нагрузке: pprof покажет правду, которой нет в теории.
Начните с промтов 1–3, если пишете новый код, и с 2, 5, 12 — если разбираете существующий сервис. Сохраните подборку в закладки: в следующий раз, когда ChatGPT выдаст «Java-стиль» на Go, вы будете знать, как его поправить.
Комментарии