Библиотека Go-разработчика | Golang
Все самое полезное для Go-разработчика в одном канале. По рекламе: @proglib_adv Учиться у нас: https://proglib.io/w/32d20779 Для обратной связи: @proglibrary_feeedback_bot РКН: https://gosuslugi.ru/snet/67a4a8c24689c2151c752af0 #WXSSA
إظهار المزيد📈 نظرة تحليلية على قناة تيليجرام Библиотека Go-разработчика | Golang
تُعد قناة Библиотека Go-разработчика | Golang (@goproglib) في القطاع اللغوي الروسية لاعباً نشطاً. يضم المجتمع حالياً 24 020 مشتركاً، محتلاً المرتبة 5 674 في فئة التكنولوجيات والتطبيقات والمرتبة 27 915 في منطقة روسيا.
📊 مؤشرات الجمهور والحراك
منذ تأسيسه في невідомо، حقق المشروع نمواً سريعاً وجمع 24 020 مشتركاً.
بحسب آخر البيانات بتاريخ 05 يونيو, 2026، تحافظ القناة على نشاط مستقر. خلال آخر 30 يوماً تغيّر عدد الأعضاء بمقدار 51، وفي آخر 24 ساعة بمقدار 6، مع بقاء الوصول العام مرتفعاً.
- حالة التحقق: غير موثّقة
- معدل التفاعل (ER): يبلغ متوسط تفاعل الجمهور 10.51%. وخلال أول 24 ساعة من النشر يحصد المحتوى عادةً 7.82% من ردود الفعل نسبةً إلى إجمالي المشتركين.
- وصول المنشورات: يحصل كل منشور على متوسط 2 524 مشاهدة. وخلال اليوم الأول يجمع عادةً 1 879 مشاهدة.
- التفاعلات والاستجابة: يتفاعل الجمهور بانتظام؛ متوسط التفاعلات لكل منشور يبلغ 9.
- الاهتمامات الموضوعية: يركز المحتوى على مواضيع رئيسية مثل навигация, лучшее_из_библиотеки_2025, git, string, golive.
📝 الوصف وسياسة المحتوى
يصف المؤلف القناة بأنها مساحة للتعبير عن الآراء الذاتية:
“Все самое полезное для Go-разработчика в одном канале.
По рекламе: @proglib_adv
Учиться у нас: https://proglib.io/w/32d20779
Для обратной связи: @proglibrary_feeedback_bot
РКН: https://gosuslugi.ru/snet/67a4a8c24689c2151c752af0
#WXSSA”
بفضل وتيرة التحديث المرتفعة (أحدث البيانات بتاريخ 07 يونيو, 2026) تحافظ القناة على حداثتها ومستوى وصول مرتفع. وتُظهر التحليلات تفاعلاً نشطاً من الجمهور، ما يجعلها نقطة تأثير مهمة ضمن فئة التكنولوجيات والتطبيقات.
docker ps, который читает настройки из переменных окружения вроде DOCKER_HOST и сам согласует версию API с демоном:
package main
import (
"context"
"fmt"
"log"
"github.com/moby/moby/client"
)
func main() {
apiClient, err := client.New(client.FromEnv)
if err != nil {
log.Fatal(err)
}
defer apiClient.Close()
result, err := apiClient.ContainerList(context.Background(), client.ContainerListOptions{
All: true,
})
if err != nil {
log.Fatal(err)
}
for _, ctr := range result.Items {
fmt.Printf("%s %s %s\n", ctr.ID, ctr.Status, ctr.Image)
}
}
Этого хватает, чтобы из своего сервиса запускать контейнеры, тянуть образы, читать логи и управлять сетью, то есть делать всё то же, что умеет команда docker.
➡️ Кому это нужно.
Moby рассчитан на инженеров и энтузиастов, которым интересно ковыряться в открытом коде, чинить баги и строить системы на контейнерах.
Если коротко, Moby это фундамент Docker, открытый и модульный. Брать его стоит тогда, когда вам мало готового движка и хочется собрать или допилить свой.
➡️ Репозиторий
📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max
🐸 Библиотека Go-разработчика
#GoToProductionuser_id, арендатору или связке эндпоинт плюс регион, метрики ломаются.
Так делать не стоит, потому что число рядов взрывается:
// Плохо. user_id и tenant дают почти неограниченное число комбинаций.
httpRequests := prometheus.NewCounterVec(
prometheus.CounterOpts{Name: "http_requests_total"},
[]string{"endpoint", "region", "user_id", "tenant"},
)
httpRequests.WithLabelValues(endpoint, region, userID, tenant).Inc()
Безопаснее держать в метках только ограниченный набор значений, а детали уносить в трейс или лог:
// Лучше. В метках только то, у чего мало возможных значений.
httpRequests := prometheus.NewCounterVec(
prometheus.CounterOpts{Name: "http_requests_total"},
[]string{"endpoint", "region", "status"},
)
httpRequests.WithLabelValues(endpoint, region, status).Inc()
🖇 Инструментация видит только ожидаемое
Вы измеряете то, что предусмотрели заранее. Новый тип сбоя не попадает ни в один график, и в этот момент вы слепы.
🖇 Худшие баги живут между сервисами
Два микросервиса по своим метрикам здоровы, задержки в трейсах нормальные, логи чистые. А вместе они выдают неверный ответ. Проблема в связи между ними, и ни одна отдельная метрика её не ловит.
🖇 Зелёные метрики не значат правильный результат
Три столпа описывают инфраструктуру, а не бизнес. Сервис может держать отличную задержку и нулевые ошибки, при этом считать цену неверно.
Поэтому полезно мерить сам бизнес-результат, а не только технику:
// Считаем не задержку, а расхождение цены. Это и есть семантическая наблюдаемость.
span.SetAttributes(
attribute.String("order.id", order.ID),
attribute.Int64("order.expected_cents", expected),
attribute.Int64("order.charged_cents", charged),
)
if charged != expected {
priceMismatchTotal.Inc()
}
🖇 Лучшее улучшение делается после инцидента
Самый сильный приём звучит скучно. После разбора спросите, какой информации не хватило, и добавьте её.
Часто это пара полей в структурном логе через slog, которых раньше не было:
// После разбора добавили request_id и tenant. Без них инцидент искали вслепую.
slog.Error("payment declined",
"request_id", reqID,
"tenant", tenant,
"provider", provider,
"code", declineCode,
)
Эффект накапливается, и через год полтора команда отлаживает прод заметно быстрее.
Вывод простой. Три столпа это стартовая точка, а не финал. Современные системы грязнее и сильнее завязаны на деньги, поэтому и наблюдаемость нужна с высокой кардинальностью, ориентированная на события и привязанная к бизнес-результату.
Наша рассылка даёт максимум за минимум.
📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max
🐸 Библиотека Go-разработчика
#GoDeep14-лет в разработке. Занимается AI-адопшеном в команде Yandex Cloud, проводит мастер-классы и продвигает лучшие практики для повышения эффективности разработчиков.🟣 Техлид Sourcecraft Code Assistant
С сильным практическим бэкграундом принимал участие как технический лид в создании мощного AI-расширения для VS Code.🟣 Создатель полезного Open Source
Разрабатывает утилиты, которые позволяют быстро начать эксперименты с инференсом и агентами в локальном окружении: например, набор скриптов vllm-setup для быстрого запуска окружения и mini-proxy — минималистичный прокси для OpenAI API провайдеров.🟣 Автор интерактивных ML-визуализаций
Объясняет сложные концепции наглядно. Создал серию залипательных обучающих материалов, где можно вживую пощупать работу сетей Хопфилда, машин Больцмана и VC-размерности.Роман регулярно делится инженерными наработками, инсайтами и экспертизой в своем авторском Telegram-канале На курсе Роман выступает консультантом программы: он помогает формировать содержание уроков с опорой на актуальные инженерные практики и жесткие требования индустрии. Узнать больше о программе и разработке автономных систем: 👉 Курс «Разработка ИИ-агентов» Так, продолжаем знакомить вас с командой? 👍 — Да, ждем новых лиц 🔥 — Жду полезные материалы от Романа
== и !=
🔹 Спросите себя: можно ли сравнить два слайса через ==? Что вообще значит «два слайса равны»?
➡️ Ответ
📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max
🐸 Библиотека Go-разработчика
#ReadySetGo%w
В Go 1.13 появился %w, который сохраняет исходную ошибку внутри обёртки и позволяет проверять её дальше по коду через errors.Is и errors.As.
Когда ошибка проходит через несколько слоёв приложения, на каждом уровне к ней добавляют контекст. Если при этом теряется исходный тип, наверху остаётся только текст, и проверить причину можно лишь по подстроке. Любая правка формулировки ломает такую проверку.
Старый способ:
fmt.Errorf("failed to fetch user: %v", err)
%v подставляет текст ошибки и выбрасывает сам объект. Узнать, что под капотом лежала sql.ErrNoRows, после этого уже нельзя.
Современный способ:
fmt.Errorf("failed to fetch user: %w", err)
%w оборачивает ошибку и сохраняет ссылку на оригинал, поэтому цепочку потом можно размотать.
Как проверять причину
errors.Is идёт по всей цепочке обёрток и сравнивает каждое звено с конкретным значением:
if errors.Is(err, sql.ErrNoRows) {
// пользователь не найден
}
Не важно, сколько слоёв обёрток сверху, проверка доберётся до нужной ошибки.
Когда нужен сам объект
Если требуется не просто факт совпадения, а доступ к полям ошибки, помогает errors.As. Он находит в цепочке ошибку нужного типа и записывает её в переменную:
var pathErr *os.PathError
if errors.As(err, &pathErr) {
slog.Error("ошибка пути", "path", pathErr.Path)
}
Своя ошибка в цепочке
Чтобы ваш тип распознавался через errors.Is и errors.As, достаточно реализовать метод Unwrap:
type AppError struct {
Code int
Err error
}
func (e *AppError) Unwrap() error { return e.Err }
%w заменил хрупкое сравнение строк на надёжную проверку по значению и по типу. Оборачивайте ошибки этим глаголом везде, где добавляете контекст, и используйте errors.Is вместо сравнения текста.
📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max
🐸 Библиотека Go-разработчика
#GoToProductionfloat64 не подходит
Классический пример:
fmt.Println(0.1 + 0.2) // 0.30000000000000004
На одной операции это незаметно, но в отчёте на тысячи позиций расхождение в копейки превращается в расхождение в рубли. Сравнивать такие суммы через == тоже нельзя, потому что равные на бумаге значения в памяти отличаются.
📎 Способ первый. Целые числа в минимальных единицах
Самый надёжный приём хранить сумму в наименьшей единице валюты, то есть в копейках или центах, как int64.
Целочисленная арифметика точна, сложение и вычитание не теряют ничего:
type Money int64 // сумма в копейках
func (m Money) String() string {
sign := ""
if m < 0 {
sign = "-"
m = -m
}
return fmt.Sprintf("%s%d.%02d", sign, m/100, m%100)
}
Цену 199 рублей 99 копеек вы храните как Money(19999), а метод String собирает её обратно в читаемый вид. Этого подхода хватает для подавляющего большинства задач с фиксированной валютой.
📎 Способ второй. Библиотека shopspring/decimal
Целые числа неудобны там, где много делений и процентов, например при расчёте НДС или распределении суммы между участниками.
Тут помогает пакет decimal, который хранит число как набор цифр с десятичной точкой и считает в десятичной системе:
import "github.com/shopspring/decimal"
price := decimal.RequireFromString("199.99")
tax := price.Mul(decimal.RequireFromString("0.20"))
total := price.Add(tax).Round(2)
fmt.Println(total.StringFixed(2)) // 239.99
Обратите внимание, что значение задаётся строкой через RequireFromString, а не из float64. Так в число не попадёт двоичная погрешность ещё на входе.
📎 Округление и деление
При делении суммы остаток никуда не девается, и его нужно осознанно куда-то отнести. decimal позволяет задать правило округления явно:
total := decimal.RequireFromString("100.00")
share := total.Div(decimal.NewFromInt(3)).Round(2)
fmt.Println(share.StringFixed(2)) // 33.33
Сумма трёх таких долей даст 99.99, и одну копейку придётся добавить к одной из частей вручную. Это не баг библиотеки, а свойство самих денег, и решать его нужно по правилам вашей предметной области.
Для денег в Go не используйте float64 и точка.
📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max
🐸 Библиотека Go-разработчика
#GoDeepfmt.Printf("DNS: %v\n", dnsDone.Sub(dnsStart))
fmt.Printf("TLS: %v\n", tlsDone.Sub(tlsStart))
fmt.Printf("TTFB: %v\n", firstByte.Sub(gotConn))
2. Логирующий RoundTripper
Оборачиваете транспорт, в каждом запросе автоматически пишутся тайминги. Одна инициализация и всё.
Лайфхак: GotConnInfo.Reused = false на каждом запросе к одному хосту = где-то не закрывается тело ответа.
📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max
🐸 Библиотека Go-разработчика
#GoToProductionslog
До версии 1.21 в Go не было единого подхода к логированию. Команды выбирали между сторонними библиотеками вроде zap, logrus и zerolog, поэтому кодовая база разных проектов выглядела неодинаково.
Стандартный пакет log умел писать только обычные текстовые строки, которые тяжело парсить и собирать в системах вроде Loki или ELK. В Go 1.21 появился slog, который принёс структурное логирование прямо в стандартную библиотеку. Если из всего списка новинок вы захотите взять только одну, берите эту.
Какую боль решает
Структурные логи это пары ключ значение, а не склеенная строка. Их легко фильтровать, искать по конкретным полям и отдавать в JSON прямо в систему сбора. Раньше ради этого приходилось тянуть внешнюю зависимость, теперь хватает стандартной библиотеки.
Старый способ выглядел так:
log.Printf("[ERROR] User %d login failed from IP %s", userID, ip)
Здесь данные смешаны с текстом, и чтобы вытащить userID, нужно писать регулярки.
Современный способ:
import "log/slog"
slog.Error("Login failed", "userID", userID, "ip", ip)
Сообщение остаётся читаемым, а данные лежат в отдельных полях.
Что он делает
slog строится вокруг трёх частей. Logger принимает вызовы вроде Info и Error. Handler решает, куда и в каком формате писать. Attr это пара ключ значение. В комплекте идут два обработчика, TextHandler для человекочитаемого вывода и JSONHandler для машинного.
Как настроить JSON вывод
Чтобы получать логи в JSON, достаточно подменить обработчик у глобального логгера:
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
slog.SetDefault(logger)
slog.Info("server started", "port", 8080)
На выходе получится строка JSON с полями time, level, msg и port. Её сразу можно отправлять в сборщик логов без дополнительной обработки.
Группировка и контекст
Часто к каждому запросу удобно прикреплять одни и те же поля. Метод With создаёт логгер с заранее заданными атрибутами:
reqLogger := slog.With("requestID", reqID, "userID", userID)
reqLogger.Info("processing request")
reqLogger.Warn("slow response", "ms", 320)
Теперь requestID и userID попадут в каждую запись без повторного перечисления.
Переводить проект можно постепенно, начав с замены log на slog в новых модулях. Старый код при этом продолжит работать, а новые записи сразу станут пригодными для машинного разбора.
📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max
🐸 Библиотека Go-разработчика
#GoToProductionmime.
Метод WordDecoder.DecodeHeader имел квадратичную сложность, поэтому специально собранный заголовок с множеством некорректных encoded-word мог съесть процессор. Это CVE-2026-42504.
➡️ Уязвимость в net/textproto.
Функции пакета подставляли входные данные в текст ошибки без экранирования, а вход часто приходит от внешней стороны, например когда net/http разбирает заголовки ответа сервера.
Через это можно было протащить в чужие логи управляющие байты терминала и вводящий в заблуждение текст. Это CVE-2026-42507.
➡️ Фикс в crypto/x509.
Метод VerifyHostname вызывал strings.Split по точке в цикле для каждой записи DNS SAN, и при длинном списке имён проверка росла квадратично, причём даже для недоверенных сертификатов. Это CVE-2026-27145.
➡️ Источник
📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max
🐸 Библиотека Go-разработчика
#GoLiveAccept-Encoding: gzip. Сервер сжимает тело, ставит Content-Encoding: gzip и отдаёт. Клиент распаковывает сам, его код менять не нужно.
Middleware
Проверяем заголовок клиента, оборачиваем ResponseWriter в gzip.Writer и пропускаем через него ответ хендлера:
func gzipMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
next.ServeHTTP(w, r)
return
}
w.Header().Set("Content-Encoding", "gzip")
gz := gzip.NewWriter(w)
defer gz.Close()
gzr := gzipResponseWriter{Writer: gz, ResponseWriter: w}
next.ServeHTTP(gzr, r)
})
}
type gzipResponseWriter struct {
http.ResponseWriter
Writer *gzip.Writer
}
func (g gzipResponseWriter) Write(b []byte) (int, error) {
return g.Writer.Write(b)
}
Подключаем к нужному эндпоинту:
mux.Handle("/data", gzipMiddleware(http.HandlerFunc(dataHandler)))
Проверяем размер ответа:
curl http://localhost:8080/data --output raw.json
curl -H "Accept-Encoding: gzip" http://localhost:8080/data --output gz.json
ls -lh raw.json gz.json
Когда не стоит
Уже сжатые форматы вроде JPEG, PNG или ZIP жать заново смысла нет, размер почти не упадёт, а CPU потратите. Совсем мелкие ответы тоже лучше не трогать, накладные расходы перевесят.
GZIP в Go это одно middleware и пара заголовков. Клиент менять не надо, а текстовый ответ ужимается в разы. В продакшене удобно взять готовый gziphandler или включить сжатие на reverse proxy, но понимать, как оно устроено, полезно в любом случае.
📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max
🐸 Библиотека Go-разработчика
#GoDeepgofmt. Он не настраивается, опций про отступы и скобки у него нет, потому что стиль уже определён за вас. Любой код после gofmt выглядит одинаково в любом проекте. Обсуждать нечего.
Неаккуратный, но рабочий код:
func main(){
x:=10
if x>5{
fmt.Println("big")
}
}
После gofmt:
func main() {
x := 10
if x > 5 {
fmt.Println("big")
}
}
Запуск по всему проекту:
gofmt -w .
Импорты делает goimports
Рядом есть goimports. Он форматирует так же, но ещё правит импорты. Убирает то, что вы перестали использовать, и добавляет нужное. В Go это важно, потому что неиспользуемый импорт не предупреждение, а ошибка компиляции. То есть инструмент реально помогает коду собраться.
goimports -w .
Хороший опыт в редакторе
В VSCode и других редакторах это работает на сохранение файла. Вы пишете, жмёте сохранить, код уже отформатирован, импорты подчищены. Отдельный шаг не нужен.
Похожие форматтеры есть и в других языках, тот же Prettier или Black. Разница в том, что в Go это часть стандартного подхода, а не выбор команды среди десятка настроек. Один инструмент, один стиль, ноль споров.
💬 За что вы любите Go? Подписаны на нашу рассылку?
📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max
🐸 Библиотека Go-разработчика
#GoTalkhttp.HandlerFunc. Шаблон выглядит так:
type FooerFunc func(A, B) (C, D)
func (f FooerFunc) Foo(a A, b B) (C, D) {
return f(a, b)
}
Когда нужен, например, io.Writer, который считает записанные байты, обычно пишут структуру с полем и методом Write. Кода становится больше, чем сути, а единственная важная строка теряется среди обвязки.
Что предлагают
Предложение даёт писать то же самое через приведение функции к интерфейсу напрямую:
var N int64
cw := io.Writer(func(p []byte) (n int, err error) {
n, err = os.Stdout.Write(p)
N += int64(n)
return n, err
})
Важная деталь в том, что это именно явное приведение, а не неявное присваивание. У io.Reader и io.Writer одинаковая сигнатура метода, поэтому неявное превращение было бы опасным. Явное приведение снимает двусмысленность, ведь вы сами указываете, чем должна стать функция.
По задумке компилятор сам сгенерирует скрытый именованный тип с неэкспортируемым именем, который и несёт метод. Менять reflect, инструменты или работу с type assertion при этом не нужно. Более того, функцию можно положить прямо в таблицу методов, и тогда лишних накладных расходов на обёртку не будет.
Альтернатива
В обсуждении всплыл и более общий вариант. Это интерфейсные литералы из #25860, где интерфейс собирают из нескольких замыканий, по одному на каждый метод:
cw := io.Writer{
Write: func(p []byte) (n int, err error) {
// ...
},
}
Такой вариант не ограничен одним методом, но его заметно сложнее встроить в текущий язык.
Часть участников переживает, что появится ещё один способ писать одно и то же, и это размывает консистентность Go, за которую язык и ценят. Решение пока не финальное, но направление выбрано. Если предложение примут, обвязочных типов вроде http.HandlerFunc в коде станет заметно меньше.
📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max
🐸 Библиотека Go-разработчика
#GoLiveKatana от команды ProjectDiscovery решает эту задачу. Это краулер на Go, который собирает ссылки и эндпоинты, в том числе из JS, и умеет ходить по сайту через настоящий браузер.
Katana принимает на вход один URL, список из файла или поток через stdin, обходит цель и отдаёт найденные эндпоинты в текст, файл или JSON.
У него два режима работы:
• Стандартный использует обычный HTTP клиент Go и работает быстро, но видит только сырой ответ.
• Режим headless поднимает Chrome, выполняет JavaScript и находит эндпоинты, которые появляются после рендера или асинхронных запросов.
Дополнительно инструмент парсит ссылки внутри JS файлов, умеет автоматически заполнять формы, контролирует область обхода через scope и regex, фильтрует вывод по расширениям и условиям. Есть встроенное определение технологий и обход с авторизацией через свои заголовки или куки.
Как это работает
Базовый запуск по одной цели:
katana -u https://example.comСписок целей из файла:
katana -list urls.txtПоток через pipe, например из
httpx:
echo https://example.com | katana
Режим headless с разбором JavaScript находит больше эндпоинтов на динамических сайтах:
katana -u https://example.com -headless -jcКонтроль области обхода. Флаг
-fs dn держит краулер в пределах домена по ключевому слову:
katana -u https://example.com -fs dnВывод в JSON с записью в файл удобен для дальнейшей обработки в пайплайне:
katana -u https://example.com -jsonl -o endpoints.jsonlЗапускать
Katana можно и как библиотеку Go. Создаёте структуру Options, передаёте те же параметры, что и в CLI, и вызываете метод Crawl.
Колбэк OnResult получает каждый найденный результат:
options := &types.Options{
MaxDepth: 3,
FieldScope: "rdn",
Concurrency: 10,
Strategy: "depth-first",
OnResult: func(result output.Result) {
gologger.Info().Msg(result.Request.URL)
},
}
crawlerOptions, _ := types.NewCrawlerOptions(options)
crawler, _ := standard.New(crawlerOptions)
crawler.Crawl("https://example.com")
Кому пригодится
Katana пригодится пентестерам, специалистам по bug bounty и инженерам по безопасности для сбора поверхности атаки. Инструмент собирает пути, параметры и файлы, из которых потом строят словари и списки целей для других сканеров.
Важно помнить, что краулер шлёт реальные запросы к цели. Запускайте его только по тем доменам, на тестирование которых у вас есть разрешение.
Если работаете с разведкой по вебу и сидите на стеке ProjectDiscovery, Katana встаёт в пайплайн рядом с subfinder и httpx. Быстрый стандартный режим закрывает простые сайты, headless вытягивает динамику.
➡️ Репозиторий
📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max
🐸 Библиотека Go-разработчика
#GoToProduction
متاح الآن! بحث تيليغرام 2025 — أهم رؤى العام 
