Библиотека Go-разработчика | Golang
Все самое полезное для Go-разработчика в одном канале. По рекламе: @proglib_adv Учиться у нас: https://proglib.io/w/32d20779 Для обратной связи: @proglibrary_feeedback_bot РКН: https://gosuslugi.ru/snet/67a4a8c24689c2151c752af0 #WXSSA
Mostrar más📈 Análisis del canal de Telegram Библиотека Go-разработчика | Golang
El canal Библиотека Go-разработчика | Golang (@goproglib) en el segmento lingüístico de Ruso es un actor destacado. Actualmente la comunidad reúne a 24 020 suscriptores, ocupando la posición 5 674 en la categoría Tecnologías y Aplicaciones y el puesto 27 915 en la región Rusia.
📊 Métricas de audiencia y dinámica
Desde su creación el невідомо, el proyecto ha mostrado un crecimiento acelerado, reuniendo a 24 020 suscriptores.
Según los últimos datos del 05 junio, 2026, el canal mantiene una actividad estable. En los últimos 30 días la variación de miembros fue de 51, y en las últimas 24 horas de 6, conservando un alto alcance.
- Estado de verificación: No verificado
- Tasa de interacción (ER): El promedio de interacción de la audiencia es 10.51%. Durante las primeras 24 horas tras publicar, el contenido suele obtener 7.82% de reacciones respecto al total de suscriptores.
- Alcance de las publicaciones: Cada publicación recibe en promedio 2 524 visualizaciones. En el primer día suele acumular 1 879 visualizaciones.
- Reacciones e interacción: La audiencia responde de forma activa: el promedio de reacciones por publicación es 9.
- Intereses temáticos: El contenido se centra en temas clave como навигация, лучшее_из_библиотеки_2025, git, string, golive.
📝 Descripción y política de contenido
El autor describe el recurso como un espacio para expresar opiniones subjetivas:
“Все самое полезное для Go-разработчика в одном канале.
По рекламе: @proglib_adv
Учиться у нас: https://proglib.io/w/32d20779
Для обратной связи: @proglibrary_feeedback_bot
РКН: https://gosuslugi.ru/snet/67a4a8c24689c2151c752af0
#WXSSA”
Gracias a la alta frecuencia de actualizaciones (últimos datos recibidos el 07 junio, 2026), el canal mantiene la vigencia y un amplio alcance. La analítica demuestra que la audiencia interactúa activamente con el contenido, lo que lo convierte en un punto de referencia dentro de la categoría Tecnologías y Aplicaciones.
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
¡Ya disponible! Investigación de Telegram 2025 — los principales insights del año 
