Привет! Меня зовут Александр Сусиков, я тимлид команды, которая занимается платформой PaaS для СберМаркета. В этой статье я расскажу, как повысить скорость разработки с помощью кодогенерации.
Зачем нужна кодогенерация
Большинство работы программиста — рутина. Мы не придумываем каждый день по новому языку программирования или алгоритму. Одинаковые действия из проекта в проект отжирают время, и их банально скучно делать. Один из способов избавиться от рутины — кодогенерация.
Кодогенераторы создают нужный код одной командой. В случае с многословными языками вроде Go это может сэкономить до нескольких дней проекта.
Вот основные преимущества кодогенерации:
- уменьшает ручной труд при разработке. Код пишется быстрее, а ошибок становится меньше;
- стандартизирует код. Одну и ту же задачу можно решить разными способами, а кодогенерация предлагает конкретный вариант и задаёт таким образом стандарт;
- улучшает производительность. Например, скорость работы приложения можно повысить, если заменить рефлексию в Go на кодогенерацию;
- позволяет обходить ограничения языка. Например, до версии 1.18 в Golang отсутствовали дженерики — их компенсировали кодогенерацией.
Какие инструменты кодогенерации подойдут для каждого этапа разработки
Представьте, что вам поручили разработать простой сервис — небольшой прокси, который общается с внешним Rest API. Ответы API при этом просят кэшировать, чтобы повысить стабильность и производительность работы.
На всех этапах разработки можно использовать кодогенерацию. Вот конкретные инструменты, которые помогут в работе над прокси.
Создание проекта — генерация директорий и основных файлов. В этом помогут консольные утилиты:
Ещё вариант — создать свои шаблоны или генераторы кода. Это удобно, если в компании микросервисная архитектура — тогда сервисы будут похожи по структуре и используемым инструментам.
Создание веб-сервера и клиента по спецификации. Наш сервис из себя будет представлять веб-сервер, который будет ходить в кэш и если там не будет данных, то обращаться к внешнему API — для общения с ним понадобится клиент. В СберМаркете пропагандируют подход Contract-first: когда спецификация пишется до кода. С готовой спецификацией можно сразу сгенерировать и сервер, и клиент для описанного API — так удобнее.
Сгенерировать код по спецификации помогут:
- swag (OpenAPI 2.0),
- go-swagger (OpenAPI 2.0),
- oapi-codegen (OpenAPI 3.0),
- openapi-generator (мультиязыковый, для всех версий OpenAPI).
Генерация метрик. Работу прокси из нашего примера можно оценивать по таким метрикам: сколько было попыток и сколько ответов нашлось в кэше.
С генерацией метрик поможет
Он использует паттерн-декоратор — создает враппер, который будет отправлять метрики в зависимости от наличия ошибки.
Создание моков. Вместо конкретной реализации в качестве зависимостей используют интерфейсы: это упрощает тестирование и уменьшает связность между модулями.
Моки — это реализация интерфейсов для тестов. Они позволяют указать, какие параметры должны быть получены и какие данные необходимо вернуть.
Моки сгенерируют:
Инициализация графа зависимостей. При инициализации приложений в конструкторы нужно передавать конкретную реализацию, а не интерфейсы. Тем самым мы получаем граф зависимостей. С его построением поможет
- wire.
Разработчику достаточно зарегистрировать конструкторы структур и указать, какие интерфейсы они реализуют. Дальше wire сам разберётся, какие нужны зависимости и в каком порядке их инициализировать.
На любом этапе удобно использовать генераторы в связке со встроенной в Go командой go generate — она позволяет запускать все генераторы одной строкой. Команда включает поиск специального комментария, начинающегося с go:generate. Затем происходит выполнение инструкций, которые написаны после этого текста.
//go:generate mockery --name Cache
type Cache interface {
Get(key string) string
}
В комментарии после go:generate нужно указать команду для вызова генератора с необходимыми параметрами. Например, имя структуры, интерфейс и пакет.
Подробнее о go generate можно почитать в официальном блоге Go. Перевод на русский — в статье на Хабре.
Как платформа СберМаркета использует кодогенерацию для работы с инфраструктурой
Платформа СберМаркета PaaS помогает разработчикам решать вопросы, связанные с инфраструктурой, это ускоряет доставку фич на прод. Вот как мы используем для этого кодогенерацию.
Алерты
Для добавления алертов разработчики СберМаркета создают yaml-файл с несколькими полями, где прописывают выражения для алерта, его имя, runbook и другие параметры. На основе этого файла генерируются Prometheus Alerting Rules, которые попадают в Alert Manager.
import-alerts-worker-not-run: &import-alerts-worker-not-run
name: Import alerts workers have not run
expr: sum(increase(workers_job_executes{queue="ImportAlerts", DEFAULT_LABELS}[5m])) == 0
for: lm
description: "Import workers have not run during 5 minutes"
runbook: ""
dashboard: https://grafana.sbmt.io/d/ueja7ew/odin
labels:
app: "paas-odin"
Инфраструктурные зависимости и зависимости от сервисов
При локальной разработке возникает проблема инфраструктурных зависимостей и зависимостей от других сервисов. Эту задачу отлично решает Docker, но для него нужно подготавливать docker-compose-файлы и описывать, какие контейнеры необходимы для работы.
У сервисов платформы СберМаркета есть манифест, в котором разработчики указывают зависимости сервиса. Эту информацию мы используем для генерации docker-compose-файла. В итоге локально запустить сервис можно всего в одну строчку.
sbm-cli service up
Все инструменты одним списком
- Для создания проекта: buffalo, beego.
- Для создания веб-сервера и клиента по спецификации: swag, go-swagger, oapi-codegen, openapi-generator.
- Для генерации метрик: gowrap.
- Для создания моков: mockery, gomock.
- Для инициализации графа зависимостей: wire.
- Для запуска генераторов одной строкой: go generate.
Мы завели соцсети с новостями и анонсами Tech-команды. Если хотите узнать, что под капотом высоконагруженного e-commerce, следите за нами там, где вам удобнее всего: Telegram, VK.
Комментарии (14)
JordanCpp
21.07.2022 23:17+2Мы сгенерировали генератор, что бы ты используя генератор, мог сгенерировать свой кастомный генератор, генерирующий генераторы для конкретной задачи генерации. Фух:)
Tuxman
23.07.2022 19:21Главное потом такое генератор натренировать на большом объёме данных, например, на всем GitHub, то получится.. GitHub Copilot
gohrytt
23.07.2022 01:41+4Если ваша задача отличается от CRUD хотя бы на одну букву - обходите генераторы openapi, они хороши для прототипирования, но поддерживать это после - ж*** сгорает мгновенно.
nin-jin
Сменить язык на менее многословный? Пфф, нет, давайте писать кодогенераторы для кодогенераторов.
Panov_Alexey
Было бы интересно узнать Ваше мнение, чем можно заменить Go, не потеряв его "сильные стороны" и получив при этом выигрыш в уменьшении многословности.
nin-jin
О каких сильных сторонах идёт речь?
nin-jin
6 лет назад писал, например, такую статью, кстати:
da39a3ee5e6b
Всегда так говорят как-будто такой уж большой выбор популярных немногословных языков, на которые сейчас можно сменить go. Kotlin ? TypeScript ?
Кстати, генерация openapi или шаблонов сервисов актуальна и в других языках
JekaMas
Основное время в программировании уходит на набор букв у вас? Точно?
nin-jin
У меня - нет. У гоферов - да.
JekaMas
Много лет работали на го?
Говорят, если основное время на набор кода уходит, то надо не программистов, а машинисток.
nin-jin
Я ж не машинистка, чтобы на го работать.
JekaMas
В D всё сообщество такое, как его представляете вы? Это бы ответило на вопрос, почему язык так на рынке и не растёт.