Добро пожаловать!

vmkteam labs – ресурс, посвященный программированию на Go и около.

У нас есть Github, куда мы выкладываем проекты https://github.com/vmkteam/ и философия, которую мы используем при разработке.

Простая архитектура

В разделе про архитектуру вы найдете описание “простой архитектуры”, которая подойдет многим (исключая бигтех).

Работа с API

В разделе про API вы найдете хорошие практики.

Onboarding

В данном разделе мы попробуем пройти создание проекта с чистого листа и его модификации в будущем. Познакомимся с нашими инструментами.

Тулинг

JSON-RPC 2.0

  • zenrpc – JSON-RPC 2.0 сервер через go generate
    • zenrpc-middleware – полезные мидлвари для zenrpc
    • rpcgen – генератор клиентов на различных языках (Go/Dart/PHP/TypeScript/Swift)
    • smdbox – UI для zenrpc
  • rpcdiff – дифф для CI между разными схемами OpenRPC (через rpcgen)
  • brokersrv – сервис для асинхронного взаимодействия через JSON-RPC 2.0

Библиотеки

  • embedlog – обертка над slog c плюшками
  • cron - реализация крона с поддержкой middleware & ui
  • vfs – библиотека/сервис для работы с файлами самым простым образом

Инструменты

genc4: визуализация архитектуры
genc4: визуализация архитектуры В прошлой заметке про архитектурный резпозиторий мы говорили о том, как хранить и актуализировать документацию. Логичным продолжением стал вопрос о тулинге, который мог бы автоматизировать две ключевые задачи: Построение карты сервисов на основе метаданных appkit Визуализацию межсервисного взаимодействия через HTTP, также на основе appkit Это отличный повод попробовать навайбкодить этот тулинг, а заодно проверить гипотезу: нужно ли уметь программировать при вайбкодинге или нет? Дисклеймер: к вайбкодингу и нестабильной генерации кода я отношусь крайне скептически.
Заметки: Архитектурный репозиторий
Заметки: Архитектурный репозиторий На этапе зарождения нового проекта будет хорошо, если в какой-либо базе знаний сохранится описание примерной архитектуры, и куда мы с ней должны прийти. Но практика показывает, что на это обычно не хватает времени. В данной статье представлена попытка описания минимального архитектурного репозитория (Architecture as Code). Или как жить без выделенного архитектора в команде. Инструменты git – основной принцип AaC. Writerside – система документации от JetBrains. PlantUML – поддержка диаграмм. C4 – архитектурная нотация. Mermaid – поддержка простых диаграмм. Скрипты и тулпинг Публикация (github/gitlab pages) – доступность в виде опубликованного сайта. Ядро системы: Writerside Данный бесплатный инструмент дает возможность просто организовать любой портал с документацией. Основные преимущества для архитектурного репозитория:
Философия разработки от vmkteam
Философия разработки от vmkteam Лучший код тот, который не написан. Если нужно все-же писать код, то попробуйте сначала его сгенерировать (все еще без LLM). Если сгенерировать не удалось – пишите максимально простой понятный код. Пишите код для того, кто посмотрит его через полгода и все поймет. Через полгода – это про Вас (из будущего). Пишите код так, чтобы никто не понял, кто его написал (речь про общий стиль). Связывайте код с задачами (номер задачи в начале commit message, через полгода скажете себе спасибо). Не пишите тот код, которого не просили (а то это еще нужно будет поддерживать). Экономьте время тех, кто будет использовать ваш код (понятный контракт и документация). Ставьте себя на место QA, когда тестируете свой код. Ставьте себя на место другого разработчика, когда используете свой код. Проектируйте на внезапное падение (не найдетесь, что ваш сервис всегда завершится корректно). Проверяйте все, что пришло извне (пользовательский ввод особенно опасен). Деплойте в пятницу (если научились; если нет – то учитесь). В программировании все еще две проблемы, избегайте их до последнего (кешируйте от безысходности). Прочитайте книжку по постгресу (сэкономите себе время при разработке). Используйте чаще мозг, чем LLM (LLM отупляет). Не забывайте программировать для души (но не тащите это в продакшн, см пункт 3). Неудачное планирование – запланированная неудача. Берите только отгрумленные и расписанные задачи в спринт. Не берите не отгрумленные задачи в спринт (еще раз для закрепления материала). Спрашивайте у PO, что будет через полгода и год при обсуждении задачи (можно заложить фундамент, но в разумных пределах). Программирование в “ворде” все еще дешевле (распишите в текстовом виде нижний и верхний слой).
Работа с API
Работа с API Предпочтительный формат В своей практике мы используем JSON-RPC 2.0 как при внешнем, так и при внутреннем взаимодействии. Современные приложения и сервисы работают в RPC парадигме, нежели в ресурсной. REST используется там, где JSON-RPC 2.0 не сильно подходит: например, работа с файлами (скачивание и загрузка). Внешние системы так же реализуют входящие вебхуки, которые нужно обработать (обычно это REST). JSON-RPC 2.0 Схема: http://open-rpc.org/ Преимущества: Стандарт со спецификацией У каждого запроса есть идентификатор Режим нотификаций позволяет не ждать ответа от сервера Поддержка батч запросов (отправить несколько запросов в одном) Можно использовать любой транспорт, например сервера очередей (не только HTTP) Единая точка входа JSON формат Недостатки:
Claude review for colgen.go
Code Review: colgen.go Overall Assessment The code is generally well-structured and follows many Go idioms. However, there are several areas where improvements can be made for better readability, maintainability, and adherence to Go best practices. Documentation Several functions are missing documentation. Here are examples of missing documentation: // generateFile processes colgen lines and generates a new file with the generated code. // It initializes a generator, parses rules, loads packages, generates code, formats it, // and saves the result to a file with "_colgen.go" suffix. func generateFile(cl colgenLines, filename string) { // ... } // replaceFile processes injection lines and replaces content in the original file. // It initializes a replacer, loads packages, generates replacements, and applies them // to the original file content. func replaceFile(cl colgenLines, filename string) { // ... } // colgenLines holds the parsed lines from a source file that contain colgen directives. type colgenLines struct { lines []string // Regular colgen directive lines injection []string // Injection directive lines assistant []string // Assistant directive lines pkgName string // Package name of the source file } Error Handling The error handling is generally good with the use of exitOnErr, but there are some inconsistencies:
DeepSeek review for colgen.go
Here’s my code review with recommendations for improvements: General Observations The code is well-structured and follows many Go idioms. However, there are some areas that could be improved for better readability, maintainability, and idiomatic Go practices. Documentation Improvements Several exported functions and types are missing documentation. Here are the recommended additions: // Config represents the configuration for the colgen tool including API keys for different AI assistants. type Config struct { DeepSeekKey string // API key for DeepSeek assistant ClaudeKey string // API key for Claude assistant } // fillByAssistName sets the API key for the specified assistant name. // Returns error if config is nil or assistant name is unknown. func (cfg *Config) fillByAssistName(name colgen.AssistantName, key string) error // keyByName returns the API key for the specified assistant name. // Returns empty string if assistant name is unknown. func (cfg *Config) keyByName(name colgen.AssistantName) string // exitOnErr logs the error and exits the program if error is not nil. // Should be used for fatal errors that prevent the program from continuing. func exitOnErr(err error) // assistFile processes a file using the specified AI assistant based on the configuration. // It handles both normal generation cases and test generation cases differently. func assistFile(cfg Config, assistPrompt, filename string) // extractAIPrompts extracts AI mode and name from the assistant prompt string. // Returns mode, assistant name, and error if parsing fails. // Defaults to "deepseek" assistant if not specified. func extractAIPrompts(aiPrompt string) (mode colgen.AssistMode, name colgen.AssistantName, err error) // replaceFile replaces content in the target file based on injection rules. func replaceFile(cl colgenLines, filename string) // generateFile generates new code based on colgen rules and writes it to a new file. func generateFile(cl colgenLines, filename string) // readFile parses a file line by line and returns all colgen-related lines. // It extracts package name, assistant instructions, injection rules, and normal colgen rules. func readFile(filename string) (result colgenLines, err error) // baseName returns the base name from path without extension. func baseName(path string) string // appVersion returns the application version from VCS info. // Returns "devel" if version information cannot be determined. func appVersion() string // writeConfig creates or updates the configuration file in the user's home directory. // It preserves existing keys while updating the specified assistant's key. func writeConfig(key string, name colgen.AssistantName) error // configPath gets the path to the configuration file in the user's home directory. func configPath() (string, error) // readConfig reads the configuration from the default location in the user's home directory. // Returns an empty config if the file doesn't exist. func readConfig() (Config, error) Code Improvements Error Handling: Consider using fmt.Errorf with %w for wrapping errors consistently Example improvement: // Current if err != nil { return fmt.Errorf("create config failed: %w", err) } // Suggested if err != nil { return fmt.Errorf("failed to create config: %w", err) } String Handling: Use strings.TrimSpace() when processing user input Example: // Current aiPrompt = strings.ReplaceAll(aiPrompt, " ", "") // Suggested aiPrompt = strings.TrimSpace(aiPrompt) File Operations: Consider using os.OpenFile with proper flags instead of os.WriteFile when you need more control Example improvement: // Current err = os.WriteFile(filename, content, os.ModePerm) // Suggested err = os.WriteFile(filename, content, 0644) // More standard permission Constants: Consider grouping related constants: const ( configFile = ".colgen" defaultAssistant = colgen.AssistantDeepSeek ) Early Returns: