Библиотека Go-разработчика | Golang
24K subscribers
2.6K photos
48 videos
88 files
5.14K links
Все самое полезное для Go-разработчика в одном канале.

По рекламе: @proglib_adv

Учиться у нас: https://proglib.io/w/32d20779

Для обратной связи: @proglibrary_feeedback_bot

РКН: https://gosuslugi.ru/snet/67a4a8c24689c2151c752af0

#WXSSA
Download Telegram
🦾 Почему ваши AI-продукты на базе LLM ломаются (и как это чинить)?

Выкатили ИИ-фичу в прод, а она галлюцинирует, падает или выдает мусор? Приглашаем на открытый вебинар, где разберем реальную боль внедрения LLM-агентов и научимся делать так, чтобы «всё работало».

🗓️ Когда: 14 мая в 19:00 МСК
⏱️ Формат: 60 минут мяса + 30 минут ответов на ваши вопросы

🧑🏻‍💻 Кто вещает: Эмиль Сатаев — Backend Platform Developer (8+ лет в разработке). Человек, который своими руками внедряет LLM и агентные системы в реальные коммерческие сервисы.

🎁 Главный бонус для онлайна:
Только участникам прямого эфира подарим уникальный промокод на скидку 10.000 ₽ на большой курс AgentOps.

👉 Занять место на вебинаре
🥱5👍1
🔄 oapi-codegen v2.7.0

Вышел релиз oapi-codegen v2.7.0. Это Go-инструмент, который генерирует серверный и клиентский код из OpenAPI-спецификаций. Версия исправляет проблемы с allOf/anyOf/oneOf, внешними $ref, перечислениями и обходом схем.

Три изменения, которые затронут существующий код

1. Strict-server с внешними $ref на ответы

Если strict-server использует внешний $ref на components/responses/... из другой спецификации, эта спецификация теперь тоже должна генерироваться с strict-server: true.

Добавьте в конфиг источника:
generate:
models: true
strict-server: true


2. Переименование анонимных схем

Анонимные схемы внутри oneOf, anyOf и additionalProperties теперь генерируются по единому шаблону:
GetPets_200_Data_Item            →  GetPets200JSONResponseBody_Data_Item
GetPets200JSONResponse_Data_Item → GetPets200JSONResponseBody_Data_Item


3. Инлайн middleware-типы в strict-server

StrictHandlerFunc и StrictMiddlewareFunc больше не являются алиасами к типам из пакета runtime/strictmiddleware/<framework>.

Теперь они определяются прямо в сгенерированном коде:
// было
import strictecho "github.com/oapi-codegen/runtime/strictmiddleware/echo"
type StrictHandlerFunc = strictecho.StrictEchoHandlerFunc

// стало
type StrictHandlerFunc func(ctx echo.Context, request any) (any, error)


Если вы ссылались на типы вида strictecho.StrictEchoHandlerFunc напрямую, замените их на StrictHandlerFunc из сгенерированного пакета.

Что нового

Разрешение коллизий имён. Новая опция output-options.resolve-type-name-collisions: true позволяет генерировать код, когда два разных определения претендуют на одинаковое Go-имя.

Коллизия разрешается детерминированно через суффикс:
type Status string          // из components.schemas.Status
type StatusParameter = string // из components.parameters.Status


Опция отключена по умолчанию.

Полная матрица параметров. Теперь поддерживаются все комбинации style × explode × type для параметров пути, запроса, заголовков и куки. Если раньше падала ошибка unsupported style или поведение отличалось в разных бэкендах, регенерация должна это исправить. Требуется github.com/oapi-codegen/runtime версии v1.4.0 или выше.

Опциональные заголовки ответов. В strict-server необязательные заголовки теперь генерируются как указатели. Если поле nil, заголовок не отправляется:
// было
type GetFoo200ResponseHeaders struct {
XOptional string // всегда в ответе, даже пустым
}

// стало
type GetFoo200ResponseHeaders struct {
XOptional *string // nil — заголовок не отправляется
}


Старое поведение возвращается через compatibility.headers-implicitly-required: true.

Echo v5. Добавлена поддержка Echo v5 через флаг generate.echo5-server: true. Echo v4 не затронут.

Middleware на уровне операций в Echo. В RegisterHandlersWithOptions появился OperationMiddlewares:
api.RegisterHandlersWithOptions(e, server, api.RegisterHandlersOptions{
OperationMiddlewares: map[string][]echo.MiddlewareFunc{
"createPet": {authMiddleware, auditMiddleware},
},
})


Middleware на уровне хендлеров в Fiber. В FiberServerOptions появился слайс HandlerMiddlewares []HandlerMiddlewareFunc.

Обработчики ошибок в strict-server для Gin. Теперь Gin поддерживает RequestErrorHandlerFunc и ResponseErrorHandlerFunc в StrictServerOptions, как это уже было в Echo.

Требования к окружению

Для сборки и запуска oapi-codegen теперь нужен Go 1.24.4+. Сгенерированный код по-прежнему может работать на более старых версиях. Из-за транзитивных зависимостей может потребоваться зафиксировать версии:
github.com/speakeasy-api/jsonpath v0.6.3
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936


➡️ Репозиторий

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека Go-разработчика

#GoLive
Please open Telegram to view this post
VIEW IN TELEGRAM
👍42🔥2🤩2👾1
👨‍💻 Менеджер SSH-туннелей, который не мешает работать

Вы настроили SSH-туннель, отошли на час, вернулись, а соединение упало. Поднимаете снова вручную. Через день история повторяется. Если туннелей несколько, это превращается в рутину.

boring решает именно это. Он держит туннели живыми, переподключает их автоматически и управляет всем через один TOML-файл.

Что умеет

Поддерживает три режима проброса портов: локальный, удалённый и динамический через SOCKS5. Работает с ~/.ssh/config и ssh-agent, если хост уже описан там, достаточно указать его имя. Умеет работать с Unix-сокетами. Есть автодополнение для bash, zsh и fish.

Бинарник появится в папке dist. Его нужно переложить в любую директорию из $PATH.

Пример конфига с двумя туннелями:
# туннель к dev-серверу, хост берётся из SSH config
[[tunnels]]
name = "dev"
local = "9000"
remote = "localhost:9000"
host = "dev-server"

# туннель к prod с явными параметрами
[[tunnels]]
name = "prod"
local = "5001"
remote = "localhost:5001"
host = "prod.example.com"
user = "root"
identity = "~/.ssh/id_prod"


Каждый туннель описывается несколькими полями. Обязательные: name, local, remote, host. Остальное опционально, boring подтянет нужное из SSH config или использует значения по умолчанию.

Основные команды:
boring list          # показать все туннели
boring open dev # открыть туннель по имени
boring open -a # открыть все туннели сразу
boring close dev # закрыть туннель
boring edit # открыть файл конфигурации


Поддерживаются glob-паттерны. Можно открыть сразу несколько туннелей командой вида boring open prod*.

Keep-alive

По умолчанию boring отправляет keep-alive каждые 120 секунд. Значение можно переопределить глобально или на уровне конкретного туннеля:
keep_alive = 60  # в секундах

[[tunnels]]
name = "flaky-server"
keep_alive = 30
...


boring не требует демона, не тянет зависимостей и не лезет в системные настройки. Один бинарник, один TOML-файл и туннели просто работают в фоне.

➡️ Репозиторий

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека Go-разработчика

#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍83👾1
🍵 Почтовый клиент прямо в терминале

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

matcha это TUI-клиент для работы с email, написанный на Go с использованием библиотеки Bubble Tea. Работает прямо в консоли, поддерживает несколько аккаунтов, вложения, черновики и даже отображение изображений в письмах, если терминал это позволяет.

Что умеет

Читать и отправлять письма, отвечать с цитированием, скачивать вложения, искать по входящим. Поддерживает Gmail, iCloud и любой кастомный IMAP/SMTP.

Если пишете письмо в Markdown клиент сам конвертирует его в HTML перед отправкой. Черновики сохраняются автоматически при выходе из редактора.

Навигация в стиле Vim: j/k для движения по списку, r для обновления входящих, / для поиска, d для удаления.

Настройка

При первом запуске клиент попросит указать email, пароль и провайдера. Конфиг хранится в ~/.config/matcha/config.json. Для Gmail и iCloud нужен app-specific password, обычный пароль от аккаунта не подойдёт.

Важно знать: пароли хранятся в конфиге в открытом виде. Авторы сами об этом предупреждают.

Если терминал это ваша основная рабочая среда, matcha позволяет не выходить из неё ради почты.

➡️ Репозиторий

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека Go-разработчика

#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍2
🌐 HTTP-клиент для Go с имитацией браузера

surf — продвинутый HTTP-клиент для Go. Он поддерживает имитацию браузерных отпечатков, HTTP/2 и HTTP/3, сессии, middleware, стриминг и многое другое.

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

Стандартный net/http выдаёт себя по TLS-отпечатку. Большинство защищённых сайтов различают настоящий браузер и скрипт именно по нему. surf эмулирует не только заголовки, но и низкоуровневые параметры TLS-рукопожатия, порядок заголовков и настройки QUIC.

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

Простой GET-запрос выглядит так:
resp := surf.NewClient().Get("https://api.github.com/users/github").Do()
if resp.IsErr() {
log.Fatal(resp.Err())
}
fmt.Println(resp.Ok().Body.String().Unwrap())


Имитация Chrome с сессией:
client := surf.NewClient().
Builder().
Impersonate().Chrome().
Session().
Build().
Unwrap()

resp := client.Get("https://example.com").Do()


Firefox со случайной ОС:
client := surf.NewClient().
Builder().
Impersonate().
RandomOS().
Firefox().
Build().
Unwrap()


HTTP/3 с полным QUIC-отпечатком:
client := surf.NewClient().
Builder().
Impersonate().Chrome().
ForceHTTP3().
Build().
Unwrap()

resp := client.Get("https://cloudflare-quic.com/").Do()
fmt.Println(resp.Ok().Proto) // HTTP/3.0


Декодирование JSON-ответа:
type User struct {
Name string `json:"name"`
Company string `json:"company"`
}

resp := surf.NewClient().Get("https://api.github.com/users/github").Do()
if resp.IsOk() {
var user User
resp.Ok().Body.JSON(&user)
}


Middleware для логирования:
client := surf.NewClient().
Builder().
With(func(resp *surf.Response) error {
fmt.Printf("статус: %d, время: %v\n", resp.StatusCode, resp.Time)
return nil
}).
Build().
Unwrap()


Что ещё умеет

• поддерживает прокси SOCKS5 с UDP для HTTP/3, DNS-over-TLS
• привязку к сетевому интерфейсу
• Unix-сокеты
• кэширование тела ответа
• стриминг, Server-Sent Events

Клиент совместим со стандартным net/http через метод Std() — это позволяет использовать его с любыми сторонними библиотеками, ожидающими *http.Client.

➡️ Репозиторий

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека Go-разработчика

#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
15👍9👾2
😊 Топ-вакансий для Go-разработчиков за неделю

Golang-разработчик (eHealth) — до 200 000 ₽, удаленно по Москве

Middle+ Software Engineer — удаленно + помогут с переездом

Tech Lead / Senior Backend Developer (Golang) — до 4 000 $ , удалёнка вне РФ и РБ

➡️ Еще больше топовых вакансий — в нашем канале Go jobs

🐸 Библиотека Go-разработчика

#GoWork
Please open Telegram to view this post
VIEW IN TELEGRAM
👩‍💻 git reset vs git revert: когда что использовать

Оба инструмента отменяют изменения, но делают это по-разному. Путаница между ними приводит к потере коммитов или конфликтам в общей ветке.

Что делает git reset

git reset перемещает указатель ветки назад. Он буквально переписывает историю коммитов — те коммиты, которые были после указанной точки, исчезают.

Три режима работы:

git reset --soft HEAD~1 — отменяет коммит, но сохраняет изменения в индексе. Файлы готовы к новому коммиту.

git reset --mixed HEAD~1 — отменяет коммит и убирает файлы из индекса. Изменения остаются в рабочей директории. Это режим по умолчанию.

git reset --hard HEAD~1 — удаляет коммит и все изменения без возможности восстановления.

Пример: написали коммит с опечаткой в сообщении или забыли добавить файл.
git reset --soft HEAD~1
git add forgotten-file.js
git commit -m "правильное сообщение"


Что делает git revert

git revert создаёт новый коммит, который отменяет изменения из указанного коммита. История не переписывается — в ней появляется дополнительная запись об отмене.

git revert a1b2c3d


Git создаст коммит вида Revert "название вашего коммита". В нём будут обратные изменения — то, что было добавлено, удалится, и наоборот.

Если нужно отменить несколько коммитов подряд:
git revert HEAD~3..HEAD


В чём принципиальная разница

git reset меняет историю. Если вы уже отправили ветку на сервер и потом сделали reset, вам придётся делать git push --force. Это создаёт проблемы для всех, кто работает с этой веткой — их локальные копии окажутся не синхронизированы с удалённой.

git revert историю не трогает. Новый коммит просто добавляется поверх. Это безопасно для общих веток.

Как выбрать

Работаете в локальной ветке, которую ещё не пушили — используйте git reset. Он чище, не засоряет историю лишними коммитами.

Нужно отменить изменения в ветке, с которой работает команда — только git revert. Даже если очень хочется сделать reset и force push, это плохая идея.

Что делать, если всё сломалось

Если случайно сделали git reset --hard и потеряли коммиты — не паникуйте. Git хранит ссылки на все объекты в reflog ещё 30 дней.
git reflog
git reset --hard HEAD@{2}


reflog покажет историю всех перемещений HEAD. Найдите нужный коммит и восстановитесь на него.

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

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека Go-разработчика

#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍162🥱1
📎 CLAUDE.md для Go-сервиса

Когда вы подключаете AI-ассистента к проекту, он не знает ни ваших правил, ни архитектуры, ни того, почему нельзя вызывать os.Exit в середине сервиса. Каждый раз объяснять это заново — потеря времени.

Что должен содержать CLAUDE.md для Go-сервиса

Файл делится на несколько блоков. Первый — команды сборки и тестирования:
# Build & Test
build: go build ./...
test: go test -race ./... -count=1
lint: golangci-lint run ./...
vet: go vet ./...


Второй — критические ограничения. Здесь важно не просто запретить что-то, но объяснить почему. Ассистент лучше следует правилу, когда понимает его смысл:
# Critical Constraint
Never use log.Fatal or os.Exit outside of main().
Return errors. We instrument error rates via middleware
and os.Exit bypasses it entirely.


Третий блок — правила работы с Git. Мелкие, но важные детали: как формировать коммиты, что не делать автоматически:
# Git
Commit per logical change (not per file, not per session).
Format: <type>(<scope>): <summary>
Types: feat | fix | refactor | test | chore
Do not push. Do not open PRs. Stage only.


Четвёртый — критерии готовности задачи. Без этого ассистент может считать работу завершённой раньше времени:
# Definition of Done
1. go test -race ./... passes
2. golangci-lint run ./... passes
3. No new TODOs without a linked GitHub issue
4. godoc updated for any changed public API


И последний блок — ссылки на документацию проекта. Используйте @-синтаксис, чтобы ассистент мог подтянуть файлы напрямую:
# References
Architecture: @docs/architecture.md
DB schema: @docs/db-schema.sql
Deploy playbook: use /deploy skill


Почему это работает лучше, чем системный промпт

CLAUDE.md живёт в репозитории рядом с кодом. Его видят все в команде, его можно версионировать и обновлять вместе с проектом. Системный промпт — это что-то невидимое и нигде не зафиксированное. CLAUDE.md — это документ, как README или Makefile.

Такой файл полезен не только для Claude. Большинство AI-ассистентов с поддержкой контекстных файлов умеют его читать. Один файл — меньше объяснений при каждом новом сеансе.

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека Go-разработчика

#GoDeep
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱10👍73😁1
🔄 Развернуть слайс красиво

В Go есть паттерн для перебора слайса в обратном порядке. Выглядит он так:
for i := len(s) - 1; i >= 0; i-- {
use(s[i])
}


Работает, но заставляет читателя каждый раз вручную разворачивать логику у себя в голове.

Go 1.23 добавил slices.Backward из пакета slices. Теперь тот же цикл можно написать так:
for _, v := range slices.Backward(s) {
use(v)
}


Если в теле цикла нужен не только элемент, но и его позиция, оба значения остаются доступны:
for i, v := range slices.Backward(s) {
fmt.Println(i, v)
}


Переписывать старые циклы вручную никто не хочет. Для этого есть анализатор slicesbackward из пакета golang.org/x/tools. Так что меняем все такие паттерны на новые методы:
go vet -vettool=$(which modernize) ./...


📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека Go-разработчика

#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍134😁2
🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖
🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖
🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖
🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖
🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖
🤖🤖🤖🤖🤖🤖🤖🤖🤖
🤖🤖🤖🤖🤖🤖🤖🤖🤖
🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖
🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖
🤖🤖🤖🤖🤖🤖🤖🤖🤖🤖

Можно бесконечно ходить на самые разные митапы, знакомиться с людьми… И совсем не видеть эффекта

А всё потому, что всем нам нужна не встреча на 3 часа, а постоянное и активное коммьюнити, чтобы обсуждать наболевшее. Именно это сейчас хорошо получается у AvitoTech — регулярно у них в канале видим анонсы интересных встреч, отдельных подкастов для SRE, Go-разработчиков, тим и техлидов и какой-то бесконечный поток прикладных статей и советов.

Если находили ещё что-то похожее, делитесь в комментах!

Реклама. Рекламодатель ООО «Авито Тех». erid: 2VtzqwWdsH9
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱64😁1🌚1
🔄 Фиксы безопасности Go

Вышли патч-релизы Go 1.26.3 и 1.25.10. Оба содержат только security-фиксы, никаких новых фич.

Что исправили
 
Самая серьёзная уязвимость в cmd/go. Вредоносный прокси мог подсунуть изменённую версию тулчейна, обойдя проверку через базы контрольных сумм.

Проблема была в том, что если база контрольных сумм возвращала пустой ответ без записи о модуле, команда go считала валидацию успешной. Теперь проверяется наличие нужной подписи, а не просто корректность той, что пришла.
 
Важно: установка GOTOOLCHAIN в фиксированную версию не защищает от этой уязвимости. Нужно обновить сам базовый тулчейн.
 
Если вы использовали нестандартный GOPROXY, проверить, не были ли затронуты зависимости, можно так:
rm go.sum && go mod tidy && go mod verify

 
Остальные исправления затрагивают стандартную библиотеку.
 
net/http/httputil: ReverseProxy мог пробрасывать параметры запроса, которые превышали лимит urlmaxqueryparams, скрывая их от функции Rewrite.
 
net/mail: два отдельных бага в consumePhrase и consumeComment приводили к квадратичным аллокациям при парсинге email-адресов — классический вектор для DoS.
 
net/http: HTTP/2 транспорт уходил в бесконечный цикл при получении SETTINGS_MAX_FRAME_SIZE равного нулю. Это позволяло серверу положить клиент.
 
html/template: два XSS-бага. Первый — пустой или содержащий пробелы атрибут type ломал экранирование в блоке script. Второй — пробелы вокруг = в атрибуте мета-тега обходили URL-экранирование.
 
net: паника в Dial и LookupPort на Windows при NUL-байте в строке. Теперь возвращается ошибка.
 
net: двойное освобождение C-памяти при обработке очень длинного CNAME-ответа через cgo.
 
cmd/go: команда go bug писала файлы с предсказуемыми именами во временную директорию. Атакующий мог создать симлинк и получить перезапись произвольного файла. Исправлено через os.MkdirTemp.
 
cmd/go: go tool pack не проверял пути при извлечении архива, что позволяло записывать файлы в произвольные места. Теперь имена с компонентами пути отклоняются.

➡️ Источник

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека Go-разработчика

#GoLive
Please open Telegram to view this post
VIEW IN TELEGRAM
6🔥3
✏️ Оператор % и float64

На собеседованиях по Go этот вопрос про оператор % звучит неожиданно. На всякий случай готовимся заранее:
Можно ли применить оператор % к значениям типа float64?


➡️ Вы знаете верный ответ?

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека Go-разработчика

#ReadySetGo
Please open Telegram to view this post
VIEW IN TELEGRAM
👨‍💻 Cobra переоценён. Попробуйте urfave/cli/v3

cobra встречается почти в каждом Go-проекте с CLI. Но для большинства инструментов он создаёт лишнюю сложность там, где её не нужно.

Чтобы добавить один подкоманд в Cobra, нужно пройти пять шагов: создать *cobra.Command, настроить RunE, зарегистрировать флаги на команде, добавить команду в корневую, вызвать Execute(). Флаги регистрируются через init(), что делает порядок инициализации неочевидным.

Что использовать вместо

github.com/urfave/cli/v3 делает то же самое, но проще. Команды — это обычные структуры с полем Action. Флаги объявляются рядом с командой. Никакого глобального состояния, никакой рефлексии, никакого init().

Пример CLI для аудита AWS-ресурсов:
app := &cli.App{
Commands: []*cli.Command{
{
Name: "audit",
Usage: "Audit AWS resources",
Flags: []cli.Flag{
&cli.StringFlag{Name: "region", Value: "us-east-1"},
},
Action: func(c *cli.Context) error {
return runAudit(c.String("region"))
},
},
},
}


Структура сама по себе является документацией. Всё явно, всё рядом.

Когда Cobra всё же оправдан

Для действительно крупных CLI с десятками подкоманд, сложными деревьями команд и автогенерацией документации Cobra может быть уместен. Для типичных утилит, деплой-скриптов и DevOps-инструментов urfave/cli/v3 справится чище.

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека Go-разработчика

#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9😁52🌚2
💡 Синтаксис Go, скорость C, без рантайма

Если вы пишете на Go и вам нужна низкоуровневая работа с памятью или вызов C-библиотек без оверхеда от Cgo — Solod именно для этого.

Что это такое

Solod (сокращённо So) — системный язык программирования. Синтаксис почти полностью совпадает с Go, но код транспилируется в C и компилируется в нативный бинарник через GCC или Clang. Никакого рантайма, никакого GC, никаких пауз.

Выпущена версия v0.1 с портированной стандартной библиотекой Go и улучшенным интеропом с C.

Портированы пакеты из стандартной библиотеки Go:

io, bufio, fmt — ввод-вывод общего назначения
bytes, strings, strconv, unicode/utf8 — работа со строками и байтами
slices, maps — обобщённые структуры данных
crypto/rand, math/rand — генерация случайных данных
flag, os, path — CLI и файловая система
log/slog — структурированные логи
time — работа со временем

Плюс два собственных пакета: mem — управление памятью с подключаемыми аллокаторами, и c — хелперы для интеропа с C.

Как выглядит интероп с C

Вот как подключить SQLite через его C API с помощью директивы so:include и инструмента sobind:
package main

import "solod.dev/so/c"

//so:include <sqlite3.h>

//so:extern SQLITE_OK
const sqliteOK = 0

//so:extern
type sqlite3 struct{}

func sqlite3_open(filename string, ppDb **sqlite3) int32
func sqlite3_exec(arg0 *sqlite3, sql string, ...) int32


Функции без тела транспилятор сам считает extern-декларациями. Go-строка в вызове sqlite3_exec автоматически превращается в const char* — без ручного преобразования.

Память: явная, но управляемая

Подход к аллокациям похож на Zig — всё выделяется явно через интерфейс mem.Allocator. Например, при чтении значения из SQLite:
name, err := m.Get(mem.System, "name")
// ...
mem.FreeString(mem.System, name)


Можно использовать mem.System (libc malloc/free) или арену на стеке:
var buf [1024]byte
arena := mem.NewArena(buf[:])
name, _ := m.Get(&arena, "name")


Нет пауз GC, нет моста Cgo при вызове C-библиотек. Платить за это приходится ручным управлением памятью.

Solod v0.1 подходит для домашних проектов и CLI-утилит — пакет flag работает так же, как в Go. Примеры cat, head, sort, wc есть в репозитории.

➡️ Попробовать в браузере

➡️ Репозиторий

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека Go-разработчика

#GoLive
Please open Telegram to view this post
VIEW IN TELEGRAM
13👍7😁2👾1
This media is not supported in your browser
VIEW IN TELEGRAM
🗓 14 мая в 19:00 (Мск) встречаемся в онлайне.

Тема: Почему AI-продукты на базе LLM ломаются и как сделать, чтобы работало.

В кружке выше Эмиль Сатаев рассказал, какие именно проблемы с LLM в проде будем разбирать.

Что в программе:
- Разберем реальные кейсы стартапов и ограничения LLM.
- Обсудим рабочие архитектуры: RAG, human-in-the-loop, контроль качества.
- Ответим на ваши вопросы и разберем кейсы участников.


🎁 Бонусы: в конце вебинара подарим промокод на скидку 10.000 ₽ на курсы и разыграем подписки на полезные AI-сервисы.

👉 Зарегистрироваться на вебинар
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱2🔥1
⭐️ Батчинг и кэш без лишнего кода

Когда пишете GraphQL API на Go, рано или поздно сталкиваетесь с проблемой N+1. Резолвер запрашивает пользователя для каждого поста отдельно — и вместо одного запроса к базе получаете сотню. graph-gophers/dataloader решает именно это.

Что делает библиотека

Она собирает отдельные запросы в батч и выполняет их одним вызовом. Параллельно кэширует результаты на время жизни загрузчика. Это реализация Facebook DataLoader для Go, с поддержкой дженериков начиная с v7.

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

Вы описываете одну батч-функцию, которая принимает список ключей и возвращает список результатов. Загрузчик сам накапливает ключи из параллельных вызовов Load(), вызывает вашу функцию один раз и раздаёт ответы обратно через thunk.
Thunk — это функция, которую возвращает Load(). Она блокирует выполнение до тех пор, пока батч не завершится, и отдаёт значение или ошибку.

Пример использования


Допустим, нужно загружать пользователей по ID. Без батчинга каждый резолвер делает свой запрос. С dataloader все запросы в рамках одного окна объединяются:
batchFn := func(ctx context.Context, ids []int) []*dataloader.Result[*User] {
users := fetchUsersByIDs(ctx, ids) // один запрос к БД
results := make([]*dataloader.Result[*User], len(ids))
for i, id := range ids {
results[i] = &dataloader.Result[*User]{Data: users[id]}
}
return results
}

loader := dataloader.NewBatchedLoader(batchFn)

// вызывается из разных горутин или резолверов
thunk := loader.Load(ctx, 42)
user, err := thunk()


Все вызовы Load() в одном временном окне попадут в один батч — fetchUsersByIDs выполнится один раз.

По умолчанию используется встроенный in-memory кэш. Он рассчитан на короткий цикл жизни — один HTTP-запрос. Если кэш не нужен совсем, есть NoCache:
loader := dataloader.NewBatchedLoader(batchFn, dataloader.WithCache(&dataloader.NoCache[int, *User]{}))


Можно передать свою реализацию интерфейса Cache, например, на основе Redis.

Если вы пишете GraphQL-сервер на Go и у вас есть резолверы, которые загружают связанные сущности — библиотека избавит от N+1 без ручного переписывания логики. Работает и без GraphQL: подходит для любых сценариев, где нужно объединять параллельные запросы за данными.

➡️ Репозиторий

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека Go-разработчика

#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
📎Напишите конфиг сами

viper — самая частая рекомендация для конфигурации в Go. Он умеет читать файлы, переменные окружения, удалённые источники и устанавливать дефолты. Звучит удобно, но на практике создаёт проблемы.

В чём проблема

Viper тянет большое дерево зависимостей. Методы GetString, GetInt и подобные не типизированы — значения возвращаются в рантайме без проверки на этапе компиляции. Ключи конфига разбросаны по коду как строковые литералы. При нетривиальном использовании поведение при слиянии и приоритетах конфигов требует чтения исходников.

Что использовать

Структура с тегами и yaml.NewDecoder:
type Config struct {
DatabaseURL string `yaml:"database_url"`
Kafka KafkaConfig `yaml:"kafka"`
Server ServerConfig `yaml:"server"`
}

func Load(path string) (*Config, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()

var cfg Config
if err := yaml.NewDecoder(f).Decode(&cfg); err != nil {
return nil, err
}
return &cfg, nil
}

Переменные окружения добавляются вручную через os.Getenv там, где это нужно.

Что получаем

Полностью типизированный конфиг. Никаких строковых ключей. Граф зависимостей, который можно проверить за секунды. Код, который вы полностью контролируете.

Для JSON-конфига подход тот же, только с encoding/json. Пакет gopkg.in/yaml.v3 — единственная внешняя зависимость, и та минимальная.

📍 Навигация: ВакансииЗадачиСобесы

🐸 Библиотека Go-разработчика

#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
11👾2