Привет! Меня зовут Александр Сусиков, я тимлид команды, которая занимается платформой PaaS для СберМаркета. В этой статье я расскажу, как повысить скорость разработки с помощью кодогенерации.


Зачем нужна кодогенерация


Большинство работы программиста — рутина. Мы не придумываем каждый день по новому языку программирования или алгоритму. Одинаковые действия из проекта в проект отжирают время, и их банально скучно делать. Один из способов избавиться от рутины — кодогенерация.

Кодогенераторы создают нужный код одной командой. В случае с многословными языками вроде Go это может сэкономить до нескольких дней проекта.


Вот основные преимущества кодогенерации:


  • уменьшает ручной труд при разработке. Код пишется быстрее, а ошибок становится меньше;
  • стандартизирует код. Одну и ту же задачу можно решить разными способами, а кодогенерация предлагает конкретный вариант и задаёт таким образом стандарт;
  • улучшает производительность. Например, скорость работы приложения можно повысить, если заменить рефлексию в Go на кодогенерацию;
  • позволяет обходить ограничения языка. Например, до версии 1.18 в Golang отсутствовали дженерики — их компенсировали кодогенерацией.

Какие инструменты кодогенерации подойдут для каждого этапа разработки


Представьте, что вам поручили разработать простой сервис — небольшой прокси, который общается с внешним Rest API. Ответы API при этом просят кэшировать, чтобы повысить стабильность и производительность работы.


На всех этапах разработки можно использовать кодогенерацию. Вот конкретные инструменты, которые помогут в работе над прокси.


Создание проекта — генерация директорий и основных файлов. В этом помогут консольные утилиты:



Ещё вариант — создать свои шаблоны или генераторы кода. Это удобно, если в компании микросервисная архитектура — тогда сервисы будут похожи по структуре и используемым инструментам.


Создание веб-сервера и клиента по спецификации. Наш сервис из себя будет представлять веб-сервер, который будет ходить в кэш и если там не будет данных, то обращаться к внешнему API — для общения с ним понадобится клиент. В СберМаркете пропагандируют подход Contract-first: когда спецификация пишется до кода. С готовой спецификацией можно сразу сгенерировать и сервер, и клиент для описанного API — так удобнее.


Сгенерировать код по спецификации помогут:



Генерация метрик. Работу прокси из нашего примера можно оценивать по таким метрикам: сколько было попыток и сколько ответов нашлось в кэше.


С генерацией метрик поможет



Он использует паттерн-декоратор — создает враппер, который будет отправлять метрики в зависимости от наличия ошибки.


Создание моков. Вместо конкретной реализации в качестве зависимостей используют интерфейсы: это упрощает тестирование и уменьшает связность между модулями.


Моки — это реализация интерфейсов для тестов. Они позволяют указать, какие параметры должны быть получены и какие данные необходимо вернуть.


Моки сгенерируют:



Инициализация графа зависимостей. При инициализации приложений в конструкторы нужно передавать конкретную реализацию, а не интерфейсы. Тем самым мы получаем граф зависимостей. С его построением поможет



Разработчику достаточно зарегистрировать конструкторы структур и указать, какие интерфейсы они реализуют. Дальше 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)


  1. nin-jin
    21.07.2022 18:31
    +14

    я расскажу, как повысить скорость разработки

    Сменить язык на менее многословный? Пфф, нет, давайте писать кодогенераторы для кодогенераторов.


    1. Panov_Alexey
      21.07.2022 19:27

      Было бы интересно узнать Ваше мнение, чем можно заменить Go, не потеряв его "сильные стороны" и получив при этом выигрыш в уменьшении многословности.


      1. nin-jin
        21.07.2022 19:29
        +6

        О каких сильных сторонах идёт речь?


        1. nin-jin
          21.07.2022 20:11

          6 лет назад писал, например, такую статью, кстати:

          man!( Go => D ).concurrency


    1. da39a3ee5e6b
      21.07.2022 21:46
      +1

      Всегда так говорят как-будто такой уж большой выбор популярных немногословных языков, на которые сейчас можно сменить go. Kotlin ? TypeScript ?

      Кстати, генерация openapi или шаблонов сервисов актуальна и в других языках


    1. JekaMas
      22.07.2022 13:51
      +2

      Основное время в программировании уходит на набор букв у вас? Точно?


      1. nin-jin
        22.07.2022 14:49

        У меня - нет. У гоферов - да.


        1. JekaMas
          22.07.2022 14:54
          +1

          Много лет работали на го?

          Говорят, если основное время на набор кода уходит, то надо не программистов, а машинисток.


          1. nin-jin
            22.07.2022 15:21

            Я ж не машинистка, чтобы на го работать.


        1. JekaMas
          22.07.2022 20:47
          +3

          В D всё сообщество такое, как его представляете вы? Это бы ответило на вопрос, почему язык так на рынке и не растёт.


  1. JordanCpp
    21.07.2022 23:17
    +2

    Мы сгенерировали генератор, что бы ты используя генератор, мог сгенерировать свой кастомный генератор, генерирующий генераторы для конкретной задачи генерации. Фух:)


    1. raamid
      22.07.2022 00:20
      +2

      Слова настоящего генератора :)


    1. Tuxman
      23.07.2022 19:21

      Главное потом такое генератор натренировать на большом объёме данных, например, на всем GitHub, то получится.. GitHub Copilot


  1. gohrytt
    23.07.2022 01:41
    +4

    Если ваша задача отличается от CRUD хотя бы на одну букву - обходите генераторы openapi, они хороши для прототипирования, но поддерживать это после - ж*** сгорает мгновенно.