Библиотека 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
😊 Топ-вакансий для 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
🥱3🔥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
👍5
📎Напишите конфиг сами

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👾3