Меня зовут Артём, я занимаюсь коммерческой разработкой с 2019 года. Последние несколько лет я активно использовал Spring Boot для создания backend-сервисов на Java и Kotlin.

Но в какой-то момент захотелось попробовать что-то новое. Не потому что Spring надоел, а просто чтобы выйти из зоны комфорта и узнать, как чувствует себя проект на другом языке. Я решил: возьму уже начатый pet-проект, перепишу его на Go — и посмотрю, как изменится подход, скорость разработки, ощущения.

Это не туториал «как перепрыгнуть с Kotlin на Go». Это — эксперимент. Расширение границ и проверка своих навыков в новых условиях. Я не пытался доказать, что Go лучше, а просто дал себе возможность попробовать и сравнить.

Фронт у проекта был примитивный, построенный на HTML и JS без фреймворков. Углубляться в TypeScript и вылизывать интерфейсы я не планировал. Я бэкендер, и красить кнопки — точно не цель этого проекта (пока). Главное — чтобы всё работало стабильно, быстро и надёжно.

Так что сделал ставку на знакомые технологии, а всю новизну решил оставить для Go и инфраструктуры.

Заодно добавил интересный вызов: интегрировать GPT-4 через OpenAI API, сделать стриминг в реальном времени через WebSocket и всё это завернуть в CI/CD, который будет деплоить проект без лишних кликов.

Проект я писал около четырёх месяцев, в свободное время — вечерами и по выходным. Местами было сложно, но оно того стоило. В итоге родился полноценный AI-чат с авторизацией, конфигурацией, живыми ответами и гибкой архитектурой.

Важно: дальше в статье будет много текста и деталей. Это не обзор, а скорее история — по шагам и с погружением. Я хотел, чтобы на этом pet-проекте можно было попробовать много технологий, но не в ущерб здравому смыслу. Здесь есть и CI/CD, и WebSocket, и JWT, и взаимодействие с AI. И всё это с упором на:

  • безопасность,

  • отказоустойчивость,

  • асинхронность,

  • простоту масштабирования,

  • и, конечно, низкую стоимость эксплуатации.

Статья ориентирована на разработчиков, которые разбираются по чуть-чуть во всём — в backend-е, frontend-е, инфраструктуре и логике систем.

Эта статья — мой первый опыт публикации технического рассказа. И вышла она после майских праздников — символично: ощущаю это как свою маленькую победу.

Вот какие разделы вас ждут и зачем они нужны:


Почему Go + Gin, а не привычный Spring Boot

Разбираю, что подтолкнуло к смене стека и почему именно Go. Пишу честно: без фанатизма и без попытки продать Go как серебряную пулю. Просто сравнение ощущений и опыта.


CI/CD и деплой без кликов: GitHub Actions, Swagger, Docker и Portainer

Автоматизация — одна из главных целей. Я хотел, чтобы после каждого коммита новая версия приложения автоматически попадала на сервер: без SSH, без ручных рестартов и копипасты. В этом разделе расскажу, как получилось выстроить простой и надёжный пайплайн, который сам собирает образ, загружает его на сервер, обновляет прод и при этом ещё и Swagger держит в актуальном состоянии.


Интеграция с GPT-4 и адаптация под пользователя

Сердце проекта — AI. Рассказываю, как устроен prompt, как работают стриминговые ответы, какие параметры можно настроить и как GPT стал частью бизнес-логики. Будет и про фильтрацию, и про планы на мультимодальность (изображения, код и т.д.).


Реальное время: WebSocket, события, непрочитанные

Почему WebSocket, как устроена отправка сообщений и обновление счётчиков. Распишу архитектуру соединений, примеры событий и логику, которая делает чат «живым». Без диких подробностей — просто, понятно и с практическими выводами.


Архитектура проекта: как не перегрузить pet-проект

Разбор слоёв проекта: что за что отвечает, почему отказался от репозиториев. Пишу про GORM, интерфейсы, зависимости и почему важно не усложнять то, что можно сделать просто.


Безопасность: HTTPS, WSS, JWT и защита на всех уровнях

Описываю подход к авторизации, валидации токенов и работе с WebSocket. Зачем HTTPS, почему не ws://, как обрабатываются ошибки, и где можно упасть (но не стоит). Планы на разграничение ролей — тоже здесь.


Трудности разработки: от горутин до фронта

Раздел про грабли. Всё, что не получилось с первого раза: гонки в map, баги с каналами, «висящие» горутины. Как помог GPT-4 не сойти с ума, и почему писать UI без фреймворков — отдельный квест.


Итоги: что получилось, что не успел, и куда двигаться дальше

Заключение. Рефлексия. Что работает, что предстоит сделать. Какие выводы я сделал за эти четыре месяца, и чем хочу вдохновить других разработчиков.


Всё построено по принципу: от кода — к системе, от «почему так?» — к «как это поддерживать?».

Надеюсь, что этот рассказ окажется полезным не только как набор решений, но и как честная история с реальными трудностями, идеями и подходами. Поехали!

Почему Go + Gin, а не Kotlin + Spring Boot?

Первый вопрос, который, скорее всего, возник у многих:
зачем вообще менять проверенный стек?

Я сам долгое время писал на Kotlin с Spring Boot — с его аннотациями, DI, магией и встроенной экосистемой. Это реально удобно. Особенно когда проект большой, а времени — мало.

Но со временем начали ощущаться минусы:

  • громоздкость проекта даже на старте;

  • высокое потребление ресурсов;

  • долгое время запуска;

  • сложность отладки скрытых процессов (особенно, если не ты их писал).

Хотелось чего-то более простого, явного и быстрого:

  • лаконичный синтаксис, где почти нет «магии»;

  • мгновенная компиляция в один бинарник;

  • эффективная работа с памятью;

  • встроенная конкурентность (горутины — любовь с первого взгляда).

Я давно хотел подтянуть Go, и вместо учебных задач решил пойти ва-банк — взять живой проект, полностью его переписать, и посмотреть, как изменится разработка. Это был и вызов, и способ быстро прокачать скилл в боевых условиях.

Плюс ко всему, Go просто приятно писать. Ты чувствуешь, что контролируешь происходящее: от старта приложения до каждой строчки бизнес-логики. Это оказалось неожиданно приятно после многослойного Spring-приложения.

CI/CD и эксплуатация: GitHub Actions, Docker Swarm и Portainer

Когда я переписывал проект на Go, одной из целей было — автоматизировать всё, что можно. Хотелось просто писать код, пушить — и чтобы всё остальное происходило без моего участия. Так появилась связка: GitHub Actions + Docker + Portainer.


Что делает CI/CD пайплайн

После каждого пуша в main запускается пайплайн в GitHub Actions, который проходит по нескольким этапам:

  • Генерация Swagger-документации (OpenAPI)
    Использую библиотеку, которая пробегается по Go-контроллерам и моделям, генерируя swagger.yaml.
    Документация всегда остаётся актуальной — теперь её не нужно обновлять вручную.

  • Сборка Docker-образа
    Благодаря Go, итоговый бинарник получается лёгким и не требует JVM.
    Использую multi-stage Dockerfile:
    сначала golang, затем финальный слой на alpine.

  • Доставка и обновление
    Готовый .tar-архив отправляется через scp на сервер,
    загружается в локальный Docker Registry (localhost:5000),
    а затем выполняется docker service update — и сервис обновляется.


Почему Docker Swarm, а не Kubernetes?

Kubernetes — мощный инструмент, но для одного сервера — перебор.
Я выбрал Docker Swarm, потому что:

  • он встроен в Docker (не требует отдельной установки);

  • не нужно писать десятки YAML-файлов;

  • достаточно одной команды: docker stack deploy.

Если в будущем понадобится Kubernetes — архитектура проекта к этому готова. Миграция будет безболезненной.


Portainer — визуальный контроль и автоматизация

Чтобы не прыгать по ssh и docker ps, я подключил Portainer — лёгкую и удобную веб-панель.

На скриншоте — интерфейс Portainer с минимальным стеком для pet-проекта: ai_chat (бэкенд и фронт в одном сервисе), PostgreSQL для хранения данных и NGINX как SSL-прокси через Let's Encrypt.
На скриншоте — интерфейс Portainer с минимальным стеком для pet-проекта: ai_chat (бэкенд и фронт в одном сервисе), PostgreSQL для хранения данных и NGINX как SSL-прокси через Let's Encrypt.

С его помощью можно:

  • видеть, какие контейнеры запущены и как работают;

  • быстро перезапускать сервисы;

  • прокидывать переменные окружения без выхода в консоль;

  • смотреть логи, даже если под рукой только браузер.

Portainer отлично дополняет CI/CD-процесс и даёт контроль «одним глазом».


Мониторинг и планы на будущее

Пока я полагаюсь на логи (docker logs, Portainer UI) и статусы контейнеров

Но в планах:

  • подключить Prometheus + Grafana для метрик:

    • количество WebSocket-соединений;

    • время отклика от GPT;

    • загрузка CPU и памяти.

  • настроить Alertmanager — чтобы оповещения приходили автоматически (Telegram, Email и т.д.).


Что ещё хочется внедрить

  • нагрузочное тестирование;

  • CI-пайплайн для фронта и его сборка отдельно от бэка;

  • отдельные контейнеры для фоновых задач (модерация, чистка логов и др.);

  • ролевая модель доступа на уровне инфраструктуры.


Вывод

Pet-проекту не нужен Kubernetes — пока. Но если придёт время — я готов.
Проект уже контейнеризирован, CI работает, инфраструктура гибкая.

А пока связка GitHub Actions + Docker Swarm + Portainer даёт всё, что нужно:

  • быстрое развёртывание;

  • простое обновление;

  • уверенность, что всё под контролем.

Интеграция GPT-4: как устроен AI-чат

Когда базовая инфраструктура была готова, я приступил к самому интересному — внедрению AI.

На скриншоте показана статистика использования OpenAI API в проекте ai-chat: выполнено 439 запросов к Chat Completions с общим объёмом 82.7K входных токенов.
На скриншоте показана статистика использования OpenAI API в проекте ai-chat: выполнено 439 запросов к Chat Completions с общим объёмом 82.7K входных токенов.

Модель GPT-4 от OpenAI уже тогда казалась чем-то волшебным. Возможность организовать чат, в котором ответы приходят от мощного языкового движка, — звучало как магия. И стало техническим вызовом: как всё это обернуть в потоковое, гибко настраиваемое решение?

Как всё работает внутри

Когда пользователь пишет сообщение, сервер формирует prompt, в который входит:

  • system message: описание правил — например, "Ты ассистент в этом приложении, не обсуждай запрещённые темы, отвечай развёрнуто, но сдержанно."

  • user message: последнее сообщение пользователя.

  • Дополнительный контекст: email, настройки, история сообщений.

Этот prompt уходит в OpenAI, а в ответ приходит результат — с учётом всех настроек.

Уже сейчас backend поддерживает передачу email, temperature, depth и даже пользовательские подсказки к каждому чату — всё это настраивается из UI.


Потоковый режим и WebSocket

Сначала я пробовал обычные REST-запросы. Но быстро понял, что хочется "эффекта живого общения": чтобы бот печатал ответ в реальном времени, а не присылал всё одним куском.

Для этого я включил режим stream в OpenAI API: модель начинает слать ответ токенами, и можно сразу транслировать их на клиент.
На стороне Go-сервера для каждого подключения запускается горутина, которая читает поток и пересылает данные в WebSocket.

Фронт ловит эти фрагменты и выводит как "печатающийся" текст — результат выглядит живо и даже эмоционально. Такое общение цепляет.


Почему не REST?

REST — это хорошо для запросов с мгновенным результатом. Но для AI-ответов в реальном времени он не подходит:

  • нельзя "частями" возвращать текст;

  • frontend не может показать постепенное появление символов;

  • пользователь не понимает, работает ли бот или "завис".

С WebSocket всё иначе: соединение живое, и каждый токен от GPT немедленно уходит клиенту. И именно это делает чат "живым".


Настройки для гибкости

Пользователь может сам выбрать:

  • temperature — насколько креативным будет AI;

  • depth — сколько сообщений учитывать в истории;

  • дополнительный контекст — например, "ты общаешься с клиентом по вопросам доставки".

    Скриншот показывает реализацию потоковой генерации ответа от GPT через OpenAI API в Go: формируется POST-запрос с флагом stream: true, авторизация идёт по API-ключу, а результат передаётся по чанкам в обработчик.
    Скриншот показывает реализацию потоковой генерации ответа от GPT через OpenAI API в Go: формируется POST-запрос с флагом stream: true, авторизация идёт по API-ключу, а результат передаётся по чанкам в обработчик.

Это уже реализовано в настройках чата, и сохраняется в базе для каждого пользователя.


Фильтрация и адаптация

Иногда GPT может вернуть что-то вроде:

{ "chat_id": "123", "user_id": "456" }

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

  • фильтрует "пустые" JSON-ответы;

  • добавляет system-сообщения вроде: "В чате участвует user@example.com", чтобы AI понимал контекст;

  • проверяет стиль общения — например, избегает неприемлемых тем.

    На скриншоте показан адаптер, подготавливающий сообщения для GPT: слева — логика фильтрации, добавления email и генерации prompt, справа — системные инструкции (system prompts), автоматически добавляемые в начало переписки. Такой подход помогает модели лучше ориентироваться в диалоге, персонализировать ответы и соблюдать правила поведения в чате.
    На скриншоте показан адаптер, подготавливающий сообщения для GPT: слева — логика фильтрации, добавления email и генерации prompt, справа — системные инструкции (system prompts), автоматически добавляемые в начало переписки. Такой подход помогает модели лучше ориентироваться в диалоге, персонализировать ответы и соблюдать правила поведения в чате.

Покупатель пишет продавцу
Покупатель пишет продавцу
У продавца появилась подсказка
У продавца появилась подсказка

Планы по AI: дальше — больше

Уже сейчас GPT-4 — ядро всей интеллектуальной логики приложения. Но хочется пойти дальше:

  • Подключить другие модели для задач: GPT-4o, Vision, Whisper (голос), парсинг изображений.

  • Обработка вложений: например, пользователь присылает фото, а модель анализирует его и формирует ответ.

  • Настроить бизнес-роли: консультант, продавец, помощник поддержки — чтобы ответы были не просто текстом, а соответствовали цели чата.

  • Добавить параметры вроде max_tokens, stop_sequence, интеграцию с внешними источниками — например, чтобы бот "смотрел в интернет" при необходимости.


Работая над этим модулем, я понял главное: AI — это не "просто ответ", это диалог, который можно настраивать и развивать. И это, честно, затягивает.

Реальное время на WebSocket: события и непрочитанное

Когда появилась первая интеграция с AI, стало ясно: чтобы чат был по-настоящему живым, нужно уходить от классического подхода с REST-запросами.
Пользователь не должен ждать, пока кто-то нажмёт F5. Он должен сразу видеть новое сообщение, обновлённый счётчик, или приход уведомления.

Так в проекте появился WebSocket — и с ним, по сути, вторая жизнь приложения.


Почему именно WebSocket?

У REST есть альтернатива — long polling или SSE (Server-Sent Events), но у всех них есть ограничения:

  • REST — хорош для запроса и ответа, но не годится для "пуша" от сервера;

  • polling грузит сервер и не даёт мгновенности;

  • SSE работает в одну сторону (только сервер → клиент).

А WebSocket — это полноценное двустороннее соединение:

  • сервер может слать данные в любой момент;

  • клиент может слушать и отправлять одновременно;

  • всё это работает поверх одного TCP-соединения — эффективно и быстро.


Какие события мы отправляем?

Сервер в любой момент может прислать:

  • chat_message — новое сообщение в чате;

  • notification — событие вроде "тебя добавили в друзья";

  • unread_count — обновление количества непрочитанных.

Каждое событие имеет свой формат, чтобы клиент понимал, как его обработать. Например, chat_message содержит ID чата, отправителя и текст.


Архитектура соединений внутри

Когда клиент подключается к WebSocket:

  1. Он передаёт JWT (через query или заголовок).

  2. Сервер валидирует токен.

  3. Создаётся Session: WebSocket + userId + список чатов.

  4. Session кладётся в map[userId]Session.

С этой map работают горутины через каналы, чтобы не было гонок и проблем с конкурентностью.


Как работает рассылка

Допустим, пользователь отправляет сообщение в чат:

  • оно сохраняется в базу данных;

  • формируется событие chat_message;

  • сервер ищет всех участников чата;

  • у кого открыт WebSocket — тем сразу отправляется сообщение;

  • если пользователь офлайн — сообщение считается непрочитанным.


Подсчёт непрочитанных

У каждого пользователя:

  • в базе хранится количество непрочитанных сообщений;

  • в памяти есть актуальные данные для быстрого доступа;

  • в UI — отображается badge со счётчиком.

Когда пользователь открывает чат:

  • счётчик сбрасывается;

  • отправляется событие unread_count, чтобы UI обновился.


Работа уведомлений в реальном времени
Работа уведомлений в реальном времени

Пример обработки в реальном времени

У каждого пользователя есть своя горутина и канал сообщений:

type Session struct {
    conn *websocket.Conn
    send chan Event
}

Сервер пишет события в канал, а воркер читает их и шлёт в WebSocket.
Так можно безопасно и быстро отправлять сообщения, не блокируя основную логику.


Что ещё работает через WebSocket

  • Ответы от GPT — прямо по мере генерации;

  • Уведомления — новые друзья, приглашения;

  • Ошибки — если что-то пошло не так, клиент узнаёт мгновенно;

  • Статусы — кто в онлайне, кто набирает текст (в планах).


Это изображение демонстрирует структуру проекта на Go и фрагмент ключевой функции ProcessStreamingResponse, отвечающей за обработку потоковых AI-ответов от OpenAI в реальном времени. Слева — структура проекта с модулями, а справа — код, который читает входящий поток построчно, парсит JSON-чанки и передаёт их в WebSocket-клиент. Этот механизм лежит в основе стриминга ответов от GPT-4, обеспечивая эффект «печатающегося» текста прямо в чате.
Это изображение демонстрирует структуру проекта на Go и фрагмент ключевой функции ProcessStreamingResponse, отвечающей за обработку потоковых AI-ответов от OpenAI в реальном времени. Слева — структура проекта с модулями, а справа — код, который читает входящий поток построчно, парсит JSON-чанки и передаёт их в WebSocket-клиент. Этот механизм лежит в основе стриминга ответов от GPT-4, обеспечивая эффект «печатающегося» текста прямо в чате.

В результате чат стал ощущаться живым. Нет задержек, нет пустых экранов, нет ощущения "ничего не происходит".
И всё это — на одном компактном соединении. Легко, быстро и эффективно.

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

Архитектура проекта: минимум слоёв, максимум простоты

Переписывая проект на Go, я не стал изобретать архитектуру с нуля. Вместо этого — взял лучшее из привычного мира Spring и адаптировал к философии Go.
Результат получился простым, но мощным — как раз то, что нужно для быстрого старта и лёгкого развития.


? Структура проекта

Я разбил код по директориям:

  • routes — настройка маршрутов и привязка к обработчикам;

  • controllers — обработка запросов, валидация, вызов сервисов;

  • services — бизнес-логика, работа с БД, GPT и т.д.;

  • models — все структуры: модели БД, DTO и вспомогательные типы.

Такой подход делает проект понятным с первого взгляда.
Заходишь в services — и сразу видишь, где живёт основная логика. Всё читаемо и без лишней "магии".


Почему я отказался от репозиториев

В Java-мире я привык к схеме controller → service → repository. Но в Go она не всегда нужна.

Вот мои причины отказаться от repository:

  1. GORM уже предоставляет удобные методы: db.First, db.Where, db.Create — и так далее;

  2. Интерфейсы Go и так дают нужную гибкость для тестов;

  3. Чем меньше абстракций — тем проще и быстрее писать код.

Если в будущем захочу подменить БД или вынести слой доступа — легко добавлю интерфейс и внедрю его. Но пока всё работает и без этого.


В итоге получился проект с минимальным количеством слоёв, но с максимальной ясностью.
Go помогает писать просто, и эта архитектура отражает именно такой подход: никаких фабрик, DI-контейнеров и обёрток без необходимости.

Код — как открытая книга. И это даёт огромное удовольствие от работы.

Безопасность: HTTPS, WSS, JWT и единый подход для API и WebSocket

Когда строишь даже pet-проект, особенно связанный с личными сообщениями и авторизацией, хочется быть уверенным: данные пользователей защищены. Поэтому я сразу закладывал продакшен-подход — без компромиссов.


HTTPS и WSS: шифруем всё

Чат обменивается чувствительной информацией:

  • AI-ответы могут содержать личные вопросы,

  • WebSocket передаёт сообщения в реальном времени,

  • Авторизация построена на токенах.

Всё это должно передаваться только в зашифрованном виде.

Поэтому:

  • Сервер работает через HTTPS,

  • WebSocket подключается только по WSS,

  • Сертификат TLS — от Let's Encrypt, с автопродлением через certbot.

? Почему это важно?
Потому что большинство браузеров не разрешают незащищённые WebSocket-соединения, если страница загружена по HTTPS. А ещё — потому что безопасность по умолчанию экономит нервы.


JWT: единый способ авторизации

Для всех защищённых API — и REST, и WebSocket — я использую JWT.

Что внутри токена:

  • userId,

  • exp (время жизни),

  • (в будущем — role).

Как это работает:

  1. После логина клиент получает JWT.

  2. Для REST-запросов он кладёт токен в заголовок Authorization.

  3. Для WebSocket — передаёт в query-параметре или header.

Сервер на каждом шаге проверяет:

  • подпись токена,

  • срок действия,

  • и если всё ок — прокидывает userId в контекст.

Если токен просрочен или подделан — пользователь получает 401 Unauthorized и дальше не проходит.


Централизованная обработка ошибок

Для REST API — ошибки всегда возвращаются в виде JSON:

{
  "code": "auth.invalidToken",
  "message": "Token expired"
}

Для WebSocket — соединение закрывается, но перед этим клиент получает финальное сообщение:

{
  "code": "webSocket.closed",
  "message": "WebSocket для уведомлений закрыт, повторное подключение..."
}

Такой подход делает поведение приложения предсказуемым — и удобным для фронтенда.


Recovery и устойчивость

Чтобы не уронить сервер при панике (например, из-за nil-указателя), я подключил middleware Recovery. Также пишутся логи всех запросов и ошибок.

На скриншоте — фрагмент логов из GIN-сервера: видно, как обрабатываются HTTP и WebSocket-запросы, включая информацию о статусах, времени ответа, маршрутах, а также предупреждение об отключении WebSocket — пример того, как работает middleware Recovery и централизованное логирование.
На скриншоте — фрагмент логов из GIN-сервера: видно, как обрабатываются HTTP и WebSocket-запросы, включая информацию о статусах, времени ответа, маршрутах, а также предупреждение об отключении WebSocket — пример того, как работает middleware Recovery и централизованное логирование.

Это помогает быстрее находить баги и не бояться нестандартных ситуаций в продакшене.

Хеширование сообщений в базе

Кроме защиты канала передачи данных, я позаботился и о хранении. Все сообщения, которые отправляются через чат — и от пользователя, и от AI — сохраняются в базу в хешированном виде (через SHA-256).

Зачем это нужно:

  • Дополнительный уровень защиты — даже если кто-то получит доступ к БД, он не сможет прочитать переписку.

  • Соблюдение приватности — сообщения могут быть чувствительными, и даже на pet-проекте хочется спать спокойно.

  • Прозрачная архитектура — хранение текстов без явного содержания упрощает юридические и этические аспекты при дальнейшем развитии проекта.

Важно: хеширование применяется только к сохранённой копии в базе, а не к передаваемым сообщениям в реальном времени. Таким образом, можно реализовать аналитику, поиск и другие фичи, если потребуется — без ущерба безопасности.

поле content не хранит открытый текст — сообщения сохраняются в зашифрованном (или хешированном) виде. Это сделано для защиты личной переписки пользователей от утечек даже в случае доступа к базе данных.
поле content не хранит открытый текст — сообщения сохраняются в зашифрованном (или хешированном) виде. Это сделано для защиты личной переписки пользователей от утечек даже в случае доступа к базе данных.

В будущем — роли и права

Сейчас все пользователи равны. Но JWT уже содержит поле role, и я планирую:

  • admin — доступ к модерации,

  • support — аналитика и техподдержка,

  • bot — автоответы и системные события.

Роли будут проверяться на уровне middleware, а значит, основную логику менять не придётся.


Вывод:
Безопасность — это не второстепенная задача, а фундамент всей системы. Я заложил продакшен-подход с самого начала: от HTTPS и WSS до JWT и централизованной обработки ошибок. А дополнительное хеширование сообщений в базе — это страховка от любых утечек. Теперь я точно знаю: пользовательские данные под защитой, а архитектура готова к масштабированию, ролям и новым требованиям.

Трудности разработки: горутины, фронтенд и помощь GPT

Go, WebSocket и OpenAI — это не только про новые технологии, но и про множество граблей, которые на них лежат. И про то, как с этими граблями можно танцевать — особенно если рядом есть GPT.


Горутины и конкурентность

Go отлично справляется с многопоточностью, но он не прощает невнимательности. Я столкнулся с классической ошибкой:

  • Хранил все активные WebSocket-соединения в map[userId]session,

  • Писал в неё из нескольких горутин,

  • Не поставил mutex.

Итог — гонка данных, падения, баги.

Race Detector сразу подсветил проблему, и я добавил sync.Mutex вокруг доступа к карте. Таких мелочей было много: где-то забытый defer close(), где-то лишний канал, который блокировал всю систему.

GPT оказался здесь не просто полезным — он стал настоящим напарником:

  • Подсказывал, как правильно синхронизировать доступ,

  • Объяснял поведение горутин,

  • Помогал проектировать очереди и worker'ы.


Трудности с полнотекстовым поиском

Ещё один открытый вопрос — это реализация полнотекстового поиска по сообщениям. Поскольку содержимое сообщений хранится в базе в хешированном виде, обычный поиск по тексту невозможен.
Хочется дать пользователю возможность искать по истории, но:

  • Простой LIKE не сработает (контент не в открытом виде),

  • PostgreSQL Full Text Search тоже не применим к хешам,

  • Расшифровка сообщений на лету — небезопасна и затратна.

? Пока у меня нет идеального решения. Возможно, нужно будет хранить отдельный индекс расшифрованных сообщений в защищённой таблице, использовать внешнюю систему (например, Elasticsearch) или проектировать поиск как отдельный сервис с доступом только по JWT и ролям.


Фронтенд — как отдельный квест

Я по натуре бэкендер. И когда пришло время сделать UI, начал с чистого JavaScript — без React, Vue и фреймворков.

На скриншоте показана структура фронтенда проекта и часть кода notification.js, который подключается к WebSocket для получения уведомлений и отслеживает непрочитанные сообщения. Очень много логов для отладки.
На скриншоте показана структура фронтенда проекта и часть кода notification.js, который подключается к WebSocket для получения уведомлений и отслеживает непрочитанные сообщения. Очень много логов для отладки.

Хотелось:

  • отображать сообщения в реальном времени,

  • видеть, как AI «печатает» ответ,

  • корректно подсчитывать непрочитанное,

  • и просто... чтобы всё выглядело прилично.

Проблем хватало:

  • браузер не успевал отрисовывать символы, если GPT присылал их слишком быстро,

  • scroll вниз не срабатывал на всех браузерах одинаково,

  • токен терялся при перезагрузке страницы.


С нуля — значит по-новому

Нечто похожее на UI в проекте появилось только на этом этапе. Это дало свободу:

  • переписать HTML под API-first подход,

  • сделать WebSocket-логику централизованной,

  • перенести авторизацию и state-менеджмент на JS.

В какой-то момент приложение из скрипта превратилось в живой чат, с динамикой, всплывающими уведомлениями и ощущением настоящего мессенджера.


GPT — как тиммейт

Было чувство, что работаю в паре:

  • я описывал задачу,

  • GPT давал реализацию или подсказывал подход.

Он помогал не тратить часы на Stack Overflow, а сразу идти к сути.


Вывод:
Трудности были — но именно они помогли прокачаться.
Я стал лучше понимать не только Go и JS, но и архитектуру, DevOps, и сам процесс разработки. А GPT помогал пройти этот путь быстрее и увереннее.

Итоги и планы: что получилось и куда двигаться дальше

Этот проект оказался для меня не просто попыткой попробовать Go — это было настоящее приключение. Смена стека, интеграция с GPT-4, борьба с горутинами и JavaScript, настройка DevOps и CI/CD — всё это превратилось в цепочку вызовов, преодоление которых дало невероятное ощущение роста.


Что получилось:

  • Я собрал полноценный AI-чат с WebSocket и GPT-4;

  • Настроил CI/CD на GitHub Actions с автообновлением через Portainer;

  • Развернул прод через Docker Swarm без боли;

  • Реализовал стриминг сообщений и работу в реальном времени;

  • Погрузился в Go и его конкурентную модель;

  • Написал свою первую большую статью


Что дальше?

Проект только начинается. Вот что планирую делать:

  • Развивать авторизацию и роли — добавить разграничение доступа, например, для поддержки, аналитики или автоматизации;

  • Прокачать AI — дать пользователю больше настроек, выбор ассистента, возможность использовать другие модели;

  • Обновить фронт — сделать его адаптивным и дружелюбным для мобильных;

  • Запустить нагрузочное тестирование — чтобы понять пределы;

  • Добавить централизованное логирование и мониторинг;

  • Подключить внешние API — например:

    • API погоды, чтобы бот мог отвечать на бытовые вопросы;

    • YouTube или Spotify API — для рекомендаций и поиска контента;

    • OCR/анализ изображений (например, через Vision API) — чтобы распознавать текст или предметы на картинках;

    • сервисы по верификации пользователей (email, телефон);

    • или даже интеграция с CRM-системой — чтобы AI мог подсказывать по клиентским данным.


Немного личного

Эту статью я заканчиваю в майские праздники — когда вся страна празднует День Победы. И пусть это совсем другая история, но для меня эти дни стали символом победы над собой: над усталостью, над страхом перед незнакомым, над отсутствием времени и над собственными сомнениями.


Спасибо, что дочитали до конца
Если вы задумываетесь о переходе с Kotlin/Java на Go — возможно, мой опыт вдохновит вас на первый шаг.
Или хотя бы напомнит: любой pet-проект — это уже шаг вперёд.

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

Комментарии (1)


  1. Krokochik
    16.05.2025 18:26

    В итоге про Go только 1% статьи, остальное про CI/CD, Docker, GPT (?) - в общем, про вещи с заявленной темой не связанные...