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

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

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

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

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

#WXSSA
Download Telegram
🧑‍💻 Вы прошли техсобес. Задачи решены. Оффер получил другой

Это vibe hiring — найм по ощущению, где решение принимается до того, как рекрутер посмотрел на ваши компетенции.

В статье — данные двух крупных исследований 2025–2026 годов, гендерная статистика по фидбеку с интервью и объяснение, почему субъективный найм бьёт по самой компании сильнее, чем по кандидату.

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

🐸 Библиотека Go-разработчика
Please open Telegram to view this post
VIEW IN TELEGRAM
3😢2🥱2
Пакет time в Go: длительность, арифметика и таймзоны

Разберём тип Duration, операции сложения и вычитания дат, сравнение и работу с часовыми поясами.

Duration

time.Duration — это промежуток времени в наносекундах. Создаётся через умножение на константы пакета:
d := 5 * time.Second
d := 2*time.Hour + 30*time.Minute
d := time.Duration(500) * time.Millisecond


Можно распарсить из строки:
d, err := time.ParseDuration("1h30m")
d, err := time.ParseDuration("2h45m30s")
d, err := time.ParseDuration("100ms")
d, err := time.ParseDuration("-1h") // отрицательная длительность


У Duration есть методы для конвертации в нужные единицы:
d := 2*time.Hour + 30*time.Minute + 45*time.Second
d.Hours() // 2.5125
d.Minutes() // 150.75
d.Seconds() // 9045
d.Milliseconds() // 9045000
d.String() // 2h30m45s


Доступные константы:
time.Nanosecond  // 1
time.Microsecond // 1000 наносекунд
time.Millisecond // 1000 микросекунд
time.Second // 1000 миллисекунд
time.Minute // 60 секунд
time.Hour // 60 минут


Арифметика времени

Добавить Duration к time.Time:
now := time.Now()
future := now.Add(24 * time.Hour)
future := now.Add(2*time.Hour + 30*time.Minute)


Для календарных операций (где важны границы месяцев и лет) есть AddDate():
nextMonth := now.AddDate(0, 1, 0)  // +1 месяц
nextYear := now.AddDate(1, 0, 0) // +1 год
lastWeek := now.AddDate(0, 0, -7) // -7 дней


Разница между двумя моментами:
diff := future.Sub(now) // возвращает Duration
fmt.Println(diff.Hours())


Два удобных сокращения:
elapsed := time.Since(start)      // то же, что time.Now().Sub(start)
remaining := time.Until(deadline) // то же, что deadline.Sub(time.Now())


Сравнение

Методы Before(), After() и Equal() делают то, что ожидаешь:
t1 := time.Now()
t2 := t1.Add(time.Hour)

t1.Before(t2) // true
t1.After(t2) // false
t1.Equal(t2) // false


Начиная с Go 1.20 появился метод Compare(), который возвращает -1, 0 или 1:
cmp := t1.Compare(t2) // -1 (t1 раньше t2)


Таймзоны

Загрузка таймзоны по имени IANA:
nyc, err := time.LoadLocation("America/New_York")
tokyo, err := time.LoadLocation("Asia/Tokyo")
utc := time.UTC
local := time.Local


Конвертация между зонами:
now := time.Now()
utcTime := now.UTC()
nycTime := now.In(nyc)


Создание времени сразу в нужной зоне:
t := time.Date(2024, 1, 15, 10, 30, 0, 0, nyc)


Получить информацию о текущей зоне:
name, offset := now.Zone()
fmt.Println(name, offset) // EST -18000 (смещение в секундах)


Если нужна зона с фиксированным смещением:
est := time.FixedZone("EST", -5*60*60)


Важный момент: time.LoadLocation() ищет данные IANA в системе. Если на сервере нет файла tzdata, вызов вернёт ошибку. В Go 1.15 появился пакет time/tzdata, который вшивает базу зон прямо в бинарник. Достаточно импорта:
import _ "time/tzdata"


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

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

#GoDeep
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9👏1
Твой код — в сердце мощного ИИ! 💚

Команда GigaChat зовёт на One Day Offer амбициозных Java-разработчиков, которые готовы создавать AI‑продукты уровня BigTech и стать частью крупнейшего AI-комьюнити.

Если ты дружишь с Java (версии 8–25), ладишь со Spring и Hibernate, а PostgreSQL и ClickHouse для тебя — не просто слова, переходи по ссылке и занимай слот на One Day Offer.

Встречаемся 23 мая — очень ждём именно тебя!
🥱6😁1
🤨 Топ-вакансий для Go-разработчиков за неделю

Senior Golang Developer — офис в Сербии

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

Golang-разработчик — до 150 000 ₽, удаленно или гибрид в Москве

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

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

#GoWork
Please open Telegram to view this post
VIEW IN TELEGRAM
😁2🌚1
🔥 База по экономике токенов и кэшированию от AI Platform Lead из Bitrix24

Знакомьтесь, Сергей Нотевский. AI Platform Lead в Bitrix24.

Он один из ключевых экспертов нашего курса AgentOps. На своих лекциях он детально разбирает экономику AI-агентов, кэширование токенов, LLM-инфраструктуру и вывод генеративных систем в стабильный прод.

Мы попросили Сергея поделиться материалами для тех, кто хочет оптимизировать косты на LLM в проде. Сохраняйте методичку по prefix cache метрике, которая напрямую влияет на ваши деньги.

Как говорят создатели Manus:
“KV-cache hit rate is the single most important metric for a production-stage AI agent.”


🛠 Что внутри методички (комбо из 3 статей + код):
Экономика кэширования — особенности провайдеров и как правильно считать затраты.

Частые анти-паттерны — почему ваш кэш постоянно сбрасывается и вы платите больше.

Кэш в AI-агентах — специфика работы с памятью в автономных системах.


🍒 Вишенка на торте: готовый SKILL для агента, который делает ревью вашего проекта, находит анти-паттерны и предотвращает низкое попадание в кэш.

Забрать комбо-материалы на GitHub

P.S. Если хотите послушать Сергея вживую — ловите его на конференциях Kode Waves (май), Conversations AI и Highload Spb (июнь).

🎁 Акция в честь старта продаж!

Прямо сейчас при покупке Инженерного трека вы получаете полный доступ к материалам курса «Разработка ИИ-агентов» в подарок.

👉 Забрать 2 курса по цене 1 и начать обучение
😉 Тесты для конкурентного кода

Каждый, кто писал тесты для конкурентного Go, знает эту боль. Нужно проверить, что что-то произошло (или не произошло) после работы горутины. А единственный способ это сделать — воткнуть time.Sleep и надеяться, что на CI этого хватит.

Выглядело это примерно так:
func TestTimeout(t *testing.T) {
// ... настраиваем логику таймаута ...

time.Sleep(10 * time.Millisecond) // авось хватит

if !timedOut {
t.Fatal("expected timeout, got none")
}
}


10 миллисекунд на один тест — мелочь. Но когда таких тестов сотни, набегают секунды пустого ожидания. А на нагруженном CI раннере 10ms иногда недостаточно, и тест становится flaky.

Что делает synctest

Пакет testing/synctest появился экспериментально в Go 1.24 за флагом GOEXPERIMENT=synctest. В Go 1.25 он вышел в GA с обновлённым API — флаг больше не нужен. Вместо старого synctest.Run используем synctest.Test. Старый API удалён в Go 1.26.

Пакет решает проблему двумя механизмами.

Bubble. synctest.Test() оборачивает тест в изолированную среду. Все горутины, запущенные внутри, принадлежат этому «пузырю».

Виртуальное время. Внутри пузыря time.Sleep, таймеры и тикеры работают на фейковых часах. Реальное время не идёт. Часы сдвигаются только тогда, когда все горутины в пузыре заблокированы и ждут. Тест с пятисекундным таймаутом выполняется за микросекунды.

`synctest.Wait()` блокирует выполнение, пока все остальные горутины в пузыре не окажутся в состоянии устойчивой блокировки. После возврата из Wait() мы точно знаем, что система обработала всё, что могла.

Вот тот же тест, переписанный на synctest:
import "testing/synctest"

func TestTimeout(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
const timeout = 5 * time.Second
ctx, cancel := context.WithTimeout(
context.Background(), timeout,
)
defer cancel()

time.Sleep(timeout - time.Nanosecond)
synctest.Wait()
if err := ctx.Err(); err != nil {
t.Fatalf("context expired early: %v", err)
}

time.Sleep(time.Nanosecond)
synctest.Wait()
if ctx.Err() != context.DeadlineExceeded {
t.Fatalf("expected DeadlineExceeded, got %v", ctx.Err())
}
})
}


Никакого реального ожидания. Никаких flaky. Никаких изменений в тестируемом коде.

Что считается «устойчивой блокировкой»

Не все блокирующие операции равны. synctest.Wait() учитывает только те, которые могут быть разблокированы исключительно горутинами внутри пузыря.

Учитываются: отправка и получение из каналов, созданных внутри пузыря, time.Sleep, sync.Cond.Wait, sync.WaitGroup.Wait.

Не учитываются: sync.Mutex (обычно захватывается ненадолго), сетевые вызовы и чтение файлов (их может разблокировать ядро ОС).

Сетевые тесты

Для тестирования сетевого взаимодействия внутри пузыря используем net.Pipe() — это in-memory соединения, которые synctest считает устойчиво блокирующими:
func TestServerHandshake(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
serverConn, clientConn := net.Pipe()
defer serverConn.Close()
defer clientConn.Close()

go runServer(serverConn)
go runClient(clientConn)
synctest.Wait()
// Все горутины заблокированы — хендшейк завершён
})
}


Запуск

На Go 1.25+ никаких дополнительных флагов:
go test ./...


Если вы до сих пор боретесь с flaky-тестами в конкурентном коде, synctest убирает главную причину — гадание на time.Sleep. Тесты становятся быстрыми и детерминированными.

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

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

#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍102
🥶 Go 1.27 уже в release freeze

Команда Go объявила о начале заморозки релиза Go 1.27. С этого момента новые фичи и оптимизации не принимаются до следующего цикла. Багфиксы по-прежнему можно отправлять.

Release Candidate 1 запланирован на вторую неделю июня. Сейчас основной приоритет — закрытие issues, блокирующих RC 1.

➡️ Источник

У команды Go есть новости, а у нас есть новостная рассылка

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

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

#GoLive
Please open Telegram to view this post
VIEW IN TELEGRAM
5👍1
⚙️Практикуем Go вместе с Podlodka Go Crew

Сервис вроде работает, но любая новая нагрузка быстро вскрывает проблемы: растут задержки, появляются утечки памяти, а временные решения слишком часто закрепляются в архитектуре. Знакомо?

С 1 по 5 июня Podlodka Go Crew совместно с 2ГИС проведут сезон «Лучшие практики в Go», где участники разберут, как делать сервисы, которые нормально работают под нагрузкой и остаются управляемыми.

В программе:


🧩 Паттерны в Go: что работает, а что приходится переосмыслять

⚡️ Рост Go-сервисов под нагрузкой — путь от прототипа до миллионов RPS

🛠 Тестирование: от базовых сценариев до выстраивания пирамиды тестов

🔍 Живой разбор кейсов межсервисного взаимодействия

🤝 Обсуждение влияния AI на процессы разработки с экспертами Go-сообщества

Формат — пять дней живых Zoom-сессий утром и вечером, закрытое сообщество в Telegram и общение со спикерами.

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

👉 Билеты по early-bird цене и программа ждут здесь: https://podlodka.io/gocrew

А по нашему промокоду goproglib получите скидку🎁
👾1
Пакет time в Go: таймеры, тикеры и паттерны

Стандартный time закрывает почти все задачи по работе со временем без сторонних библиотек. Разберём основные примитивы и паттерны.

Таймеры

time.NewTimer() срабатывает один раз через заданный интервал:
timer := time.NewTimer(2 * time.Second)
<-timer.C
fmt.Println("Timer fired!")


Таймер можно остановить до срабатывания через timer.Stop() и перезапустить через timer.Reset().

time.After() — сокращение без доступа к таймеру. time.AfterFunc() вызывает функцию в отдельной горутине.

Тикеры

time.NewTicker() срабатывает повторно через равные интервалы. Всегда вызывайте Stop(), иначе горутина утечёт:
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()

for i := 0; i < 5; i++ {
<-ticker.C
fmt.Println("Tick", i)
}


Таймауты через context

В продакшене таймауты реализуют через context — это позволяет пробросить отмену по всей цепочке вызовов:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

select {
case result := <-doWork(ctx):
fmt.Println("Result:", result)
case <-ctx.Done():
fmt.Println("Timeout:", ctx.Err())
}


Замер времени выполнения

Простая обёртка через defer. Первый вызов запоминает старт, возвращённое замыкание откладывается и печатает разницу:
func measureTime(name string) func() {
start := time.Now()
return func() {
fmt.Printf("%s took %v\n", name, time.Since(start))
}
}

defer measureTime("main")()


Rate limiter

Простейший вариант на time.Tick() — один запрос в секунду:
limiter := time.Tick(time.Second)
for req := range requests {
<-limiter
process(req)
}


Retry с экспоненциальным backoff

Пауза удваивается после каждой неудачи, но не превышает maxBackoff. В продакшене стоит добавить jitter, чтобы клиенты не нагружали сервер одновременно:
backoff := 100 * time.Millisecond
maxBackoff := 10 * time.Second

for i := 0; i < maxRetries; i++ {
if err := fn(); err == nil {
return nil
}
time.Sleep(backoff)
backoff *= 2
if backoff > maxBackoff {
backoff = maxBackoff
}
}


Если переходите с других языков — запомните референсную дату 1/2 3:4:5 2006 и не забывайте вызывать ticker.Stop().

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

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

#GoDeep
Please open Telegram to view this post
VIEW IN TELEGRAM
5👾1
📊 pprof открывается с Flame Graph по умолчанию

Небольшое изменение в Go 1.26, которое заметно влияет на процесс отладки производительности.

До версии 1.26 команда go tool pprof -http=:8080 открывала top-down tree view. Чтобы переключиться на Flame Graph, нужно было сделать это вручную. На практике многие просто оставались в tree view, потому что оно открывалось первым.

Теперь pprof сразу показывает flame graph. Разница существенная. Flame graph визуально отображает весь стек вызовов: широкие блоки означают медленные функции, высокие столбцы показывают глубокие цепочки вызовов. Tree view содержит ту же информацию, но в виде текстового списка, который требует больше усилий для анализа.

Если в вашей команде регулярно занимаются отладкой производительности, обновите runbook'и и incident playbook'и с учётом нового дефолтного представления.

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

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

#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍7
✏️ Мнения разделились

Что выведет:
bytes.ContainsAny([]byte("hello"), "")


Казалось бы, очевидно. Но разработчики разделились почти на четыре группы.

Подсказка: загляните в исходники стандартной библиотеки. Поведение ContainsAny с пустым chars задокументировано — но интуиция здесь часто подводит.

➡️ Правильный ответ в собесах по Go

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

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

#ReadySetGo
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2
📌 Зачем дата-сайентисту матанализ?

Основная компетенция специалиста по Data Science – способность анализировать и интерпретировать данные, а математика является фундаментом для начала работы.

В карточках мы разбираем основные разделы математики, с которых стоит начать изучение специалисту по анализу данных.

Хотите подготовиться к офферу или подтянуть знания? Оставляйте заявку на наш курс по математике для Data Science 💙

P.S. Только до 31 мая на курс (и вообще на все программы Академии) действует СКИДКА 40%

А как у вас дела с высшей математикой?
❤️ — Помню всё
🔥 — Знаю основы
🌚 — Ничего не знаю

🏃‍♀️ Proglib Academy
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥42🌚2
📎 sync.Pool в Go, но с дженериками и типобезопасностью

В стандартной библиотеке Go есть sync.Pool. Штука простая по идее, но полезная на практике. Она позволяет переиспользовать объекты вместо того, чтобы каждый раз выделять под них новую память. Меньше аллокаций, меньше нагрузка на GC, выше производительность.

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

Представьте скоростной принтер, который печатает 100 страниц в минуту. Было бы странно, если бы он перед каждой страницей бегал на склад за новой пачкой бумаги. Вместо этого у него есть лоток на 100 листов. Берём лист, печатаем, кладём обратно. sync.Pool работает по тому же принципу.

Вот как это выглядит в коде:
var paperPool = sync.Pool{
New: func() interface{} {
return new(Paper)
},
}

func printPage() {
page := paperPool.Get().(*Paper)
page.Reset()

defer paperPool.Put(page)

page.Print()
}


Несколько важных моментов про sync.Pool.

У пула нет фиксированного размера. Вы можете класть и забирать объекты без жёстких ограничений. Но объект, который вы вернули в пул, может быть в любой момент удалён сборщиком мусора. Не стоит рассчитывать, что он там останется навсегда.

Ещё один момент. Объекты могут хранить состояние от предыдущего использования. Поэтому перед использованием или перед возвратом в пул их нужно сбрасывать. Если этого не делать, можно получить трудноуловимые баги с «грязными» данными.

Типобезопасная обёртка

sync.Pool хранит и возвращает interface{}. Это гибко, но не безопасно. Ничто не мешает положить в пул строку, а достать и попытаться привести к структуре. Приложение упадёт с паникой.

Дженерики в Go позволяют обернуть sync.Pool так, чтобы тип контролировался на этапе компиляции.
type Pool[T any] struct {
internal sync.Pool
}

func NewPool[T any](newF func() T) *Pool[T] {
return &Pool[T]{
internal: sync.Pool{
New: func() interface{} {
return newF()
},
},
}
}


Мы создали обёртку, привязанную к конкретному типу T. Под капотом всё тот же sync.Pool с interface{}, но снаружи пользователь работает только с типом T.

Теперь методы Get и Put:
func (p *Pool[T]) Get() T {
return p.internal.Get().(T)
}

func (p *Pool[T]) Put(x T) {
p.internal.Put(x)
}


В Get мы приводим interface{} к типу T без проверки ошибки. И это нормально. Наша обёртка гарантирует, что в пул попадают только значения типа T. Функция New создаёт T, метод Put принимает только T. Поэтому приведение типа p.internal.Get().(T) не вызовет панику при штатном использовании.

Код остаётся чистым, типобезопасным и не требует ручных проверок на каждый Get.

Обёртка с дженериками убирает необходимость в ручном приведении типов и снижает риск ошибок. Решение занимает десяток строк, при этом полностью совместимо со стандартным sync.Pool.

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

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

#GoDeep
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
😌 Дайджест недели

Осталось 7 дней до лета!

Две уязвимости в golang.org/x/image

В пакете golang.org/x/image исправлены две CVE. Первая вызывала панику при декодировании BMP с некорректным индексом палитры. Вторая позволяла через специально сформированный TIFF-файл заставить декодер потреблять непропорционально много ресурсов при распаковке PackBits.

Шесть уязвимостей в golang.org/x/net

В пакете golang.org/x/net закрыты шесть CVE. Четыре связаны с XSS через ошибки HTML-парсера, одна позволяет устроить отказ в обслуживании при парсинге HTML, ещё одна открывает возможность обхода проверки доменных имён через некорректную обработку Punycode в пакете idna.

Vibe теперь и hiring

Go 1.27 заморожен

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

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

#GoLive
Please open Telegram to view this post
VIEW IN TELEGRAM