В стандартной библиотеке 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.📍 Навигация: Вакансии • Задачи • Собесы
#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 заморожен
📍 Навигация: Вакансии • Задачи • Собесы
#GoLive
Please open Telegram to view this post
VIEW IN TELEGRAM
👾1
Интерфейсы повсюду, многослойные пакеты, DI-контейнеры, generic-репозитории. Код выглядит солидно на ревью. Но стоит починить баг, и вы проходите шесть слоёв абстракции ради пяти строк логики. В Go это обходится дороже, чем кажется.
Почему это происходит
Интерфейсы в Go не требуют
implements, любой тип удовлетворяет им неявно. Это удобно, но создаёт ловушку. Интерфейсы легко определять, поэтому их определяют слишком много. Пакеты легко создавать, поэтому кодовая база обрастает слоями ради диаграммы, а не ради задачи.Чем это бьёт
Вы видите
s.repo.Find(ctx, query.NewUserQuery().WithID(id)) и не знаете, это база, кэш или мок. Индирекция без пользы, просто шум. Интерфейсы создаются под единственную реализацию ещё до первого теста. Тесты обрастают моками для моков. Файл теста длиннее кода, который он тестирует.Как оставаться простыми
Начинайте с конкретики. Напишите структуру, заставьте работать. Интерфейс выносите, когда появится вторая реализация. Так устроена стандартная библиотека Go.
io.Reader не определили заранее:type UserStore struct {
db *sql.DB
}
func (s *UserStore) GetByID(ctx context.Context, id string) (*User, error) {
// реальная реализация
}
// Интерфейс только когда нужен
type UserGetter interface {
GetByID(ctx context.Context, id string) (*User, error)
}Пакеты должны делать, а не транслировать. Если пакет только переводит между другими пакетами, это шов без назначения. Для большинства сервисов плоская структура
cmd/, internal/store/, internal/api/ работает лучше.Функции вместо интерфейсов, где уместно. Не каждое поведение нужно выражать через интерфейс:
func SendWelcome(ctx context.Context, user User, send func(context.Context, string, string) error) error {
return send(ctx, user.Email, welcomeMessage(user))
}Называйте по действию, а не по слою.
UserActivator, SessionIssuer, PasswordResetter вместо UserService, UserManager, UserHandler. Точность в имени сопротивляется желанию сваливать всё в одну абстракцию.Простой симптом. Если для одной фичи вы трогаете интерфейс, структуру, конструктор, мок и фикстуру до написания логики, архитектура работает против вас.
📍 Навигация: Вакансии • Задачи • Собесы
#GoDeep
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🌚7🥱3😁2
До 31 мая можно забрать любой курс Proglib Academy со скидкой 40%
Если давно хотели прокачаться в Python, ML, алгоритмах или AI-агентах, сейчас самое время выбрать программу и начать обучение по сниженной цене.
🎁 Разработка AI-агентов — от 49.000 ₽ (вместо 69.000 ₽)
Практический курс по разработке AI-агентов для автоматизации задач, работы и собственных проектов
🎁 Курс AgentOps — 129.000 ₽ (вместо 149.000 ₽)
Для разработчиков и LLM-инженеров, которые хотят внедрять AI-логику в бэкенд и сохранять стабильность сервиса.
🎁 Математика для разработки AI-моделей — 23.990 ₽ (вместо 31.990 ₽)
Практическая база по математике для анализа данных, ML и дальнейшего развития в AI.
🎁 Математика для Data Science — от 29.990 ₽ (вместо 39.990 ₽)
Курс для тех, кто хочет решать задачи, которые дают на собеседованиях на позицию дата-сайентиста в бигтехе.
🎁 ML для старта в Data Science — 28.990 ₽ (вместо 38.990 ₽)
Разберётесь в машинном обучении: от базовых понятий и линейных моделей до ансамблей, бустинга и рекомендательных систем.
🎁 Основы IT для непрограммистов — 16.990 ₽ (вместо 28.990 ₽)
Курс для IT-рекрутеров, маркетологов, проджектов, продактов и всех, кто работает с IT, но не пишет код.
🎁 Архитектуры и шаблоны проектирования — 27.990 ₽ (вместо 37.900 ₽)
Освоите основные паттерны проектирования и прокачаете навыки архитектора программного обеспечения.
🎁 Специалист по ИИ — 89.000 ₽ (вместо 113.900 ₽)
Курс для тех, кто хочет получить профессию в сфере ИИ, собрать портфолио из 5 проектов и научиться разрабатывать сложных AI-агентов.
🎁 Алгоритмы и структуры данных — 33.990 ₽ (вместо 57.990 ₽)
Подготовитесь к алгоритмическим собеседованиям, разберёте структуры данных и научитесь писать более эффективный код.
🎁 Программирование на языке Python — 27.990 ₽ (вместо 47.390 ₽)
Освоите Python на практике: без сухой теории, с пошаговой прокачкой навыков и итоговым проектом в портфолио.
🙌 Выбирайте курс по ссылке, оставляйте заявку, и менеджер поможет подобрать программу под ваши цели — https://clc.to/U79QHw
Если давно хотели прокачаться в Python, ML, алгоритмах или AI-агентах, сейчас самое время выбрать программу и начать обучение по сниженной цене.
🎁 Разработка AI-агентов — от 49.000 ₽ (вместо 69.000 ₽)
Практический курс по разработке AI-агентов для автоматизации задач, работы и собственных проектов
🎁 Курс AgentOps — 129.000 ₽ (вместо 149.000 ₽)
Для разработчиков и LLM-инженеров, которые хотят внедрять AI-логику в бэкенд и сохранять стабильность сервиса.
🎁 Математика для разработки AI-моделей — 23.990 ₽ (вместо 31.990 ₽)
Практическая база по математике для анализа данных, ML и дальнейшего развития в AI.
🎁 Математика для Data Science — от 29.990 ₽ (вместо 39.990 ₽)
Курс для тех, кто хочет решать задачи, которые дают на собеседованиях на позицию дата-сайентиста в бигтехе.
🎁 ML для старта в Data Science — 28.990 ₽ (вместо 38.990 ₽)
Разберётесь в машинном обучении: от базовых понятий и линейных моделей до ансамблей, бустинга и рекомендательных систем.
🎁 Основы IT для непрограммистов — 16.990 ₽ (вместо 28.990 ₽)
Курс для IT-рекрутеров, маркетологов, проджектов, продактов и всех, кто работает с IT, но не пишет код.
🎁 Архитектуры и шаблоны проектирования — 27.990 ₽ (вместо 37.900 ₽)
Освоите основные паттерны проектирования и прокачаете навыки архитектора программного обеспечения.
🎁 Специалист по ИИ — 89.000 ₽ (вместо 113.900 ₽)
Курс для тех, кто хочет получить профессию в сфере ИИ, собрать портфолио из 5 проектов и научиться разрабатывать сложных AI-агентов.
🎁 Алгоритмы и структуры данных — 33.990 ₽ (вместо 57.990 ₽)
Подготовитесь к алгоритмическим собеседованиям, разберёте структуры данных и научитесь писать более эффективный код.
🎁 Программирование на языке Python — 27.990 ₽ (вместо 47.390 ₽)
Освоите Python на практике: без сухой теории, с пошаговой прокачкой навыков и итоговым проектом в портфолио.
🙌 Выбирайте курс по ссылке, оставляйте заявку, и менеджер поможет подобрать программу под ваши цели — https://clc.to/U79QHw
🆚 Git merge и rebase. В чём разница и когда что использовать
Обе команды решают одну задачу — подтянуть изменения из одной ветки в другую. Но делают это совершенно по-разному. Понимание этой разницы убирает половину путаницы при работе с ветками.
Ситуация
Вы работаете в feature-ветке. Пока вы пишете код, кто-то вливает новые коммиты в
Тут два варианта. Первый —
Что делает merge
Merge берёт изменения из обеих веток и объединяет их в один новый коммит. Этот коммит называется merge commit:
Git создаёт точку слияния, где обе линии разработки сходятся. История сохраняется как есть — видно, когда ветки разошлись и когда соединились.
Это безопасный вариант. Ничего не перезаписывается, старые коммиты остаются на месте. Для новичков merge проще, потому что конфликты решаются один раз.
Минус проявляется на больших проектах. Когда разработчиков много, лог засоряется merge-коммитами вида «Merge branch 'main' into feature». Через пару месяцев читать такую историю становится тяжело.
Что делает rebase
Rebase переносит ваши коммиты поверх последнего состояния целевой ветки. Вместо объединения историй он перестраивает вашу ветку так, будто вы начали работу с самой свежей версии
Git временно снимает ваши коммиты, обновляет ветку до актуального
Результат — чистая линейная история без лишних merge-коммитов. Читать лог проще, ревью и дебаг тоже упрощаются.
Главное правило rebase
Не делайте rebase на ветках, с которыми работают другие люди. Rebase переписывает коммиты, и если кто-то уже забрал старые версии, возникнет каша из конфликтов.
Безопасно — rebase своей локальной feature-ветки. Опасно — rebase общей ветки, в которую пушат несколько человек.
Конфликты
Обе команды могут вызвать конфликты. Разница в том, как вы их решаете. При merge конфликт обычно один — в момент слияния. При rebase Git может останавливаться на каждом переносимом коммите. Это чуть утомительнее, но итоговая история получается чище.
Что используют на практике
Многие команды комбинируют оба подхода. Типичная схема выглядит так. Локально вы делаете
Такой подход даёт чистую историю в feature-ветке и безопасное слияние в общую.
Merge сохраняет полную картину — кто, когда и откуда вливал изменения. Rebase делает историю линейной и читаемой. Ни один из подходов не лучше другого в абсолюте. Выбор зависит от того, что важнее вашей команде — полнота истории или её чистота.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
#GoToProduction
Обе команды решают одну задачу — подтянуть изменения из одной ветки в другую. Но делают это совершенно по-разному. Понимание этой разницы убирает половину путаницы при работе с ветками.
Ситуация
Вы работаете в feature-ветке. Пока вы пишете код, кто-то вливает новые коммиты в
main. Ваша ветка отстала. Нужно обновиться.Тут два варианта. Первый —
git merge main. Второй — git rebase main. Оба подтянут свежие изменения, но история коммитов будет выглядеть по-разному.Что делает merge
Merge берёт изменения из обеих веток и объединяет их в один новый коммит. Этот коммит называется merge commit:
git checkout feature
git merge main
Git создаёт точку слияния, где обе линии разработки сходятся. История сохраняется как есть — видно, когда ветки разошлись и когда соединились.
Это безопасный вариант. Ничего не перезаписывается, старые коммиты остаются на месте. Для новичков merge проще, потому что конфликты решаются один раз.
Минус проявляется на больших проектах. Когда разработчиков много, лог засоряется merge-коммитами вида «Merge branch 'main' into feature». Через пару месяцев читать такую историю становится тяжело.
Что делает rebase
Rebase переносит ваши коммиты поверх последнего состояния целевой ветки. Вместо объединения историй он перестраивает вашу ветку так, будто вы начали работу с самой свежей версии
main:git checkout feature
git rebase main
Git временно снимает ваши коммиты, обновляет ветку до актуального
main, а потом накатывает ваши коммиты по одному заново. При этом коммиты пересоздаются — у них меняются хеши. Коммит C становится C', F становится F'. Именно поэтому говорят, что rebase переписывает историю.Результат — чистая линейная история без лишних merge-коммитов. Читать лог проще, ревью и дебаг тоже упрощаются.
Главное правило rebase
Не делайте rebase на ветках, с которыми работают другие люди. Rebase переписывает коммиты, и если кто-то уже забрал старые версии, возникнет каша из конфликтов.
Безопасно — rebase своей локальной feature-ветки. Опасно — rebase общей ветки, в которую пушат несколько человек.
Конфликты
Обе команды могут вызвать конфликты. Разница в том, как вы их решаете. При merge конфликт обычно один — в момент слияния. При rebase Git может останавливаться на каждом переносимом коммите. Это чуть утомительнее, но итоговая история получается чище.
Что используют на практике
Многие команды комбинируют оба подхода. Типичная схема выглядит так. Локально вы делаете
git rebase main на своей feature-ветке, чтобы держать историю чистой. А когда feature готова, вливаете её в main через git merge или pull request.Такой подход даёт чистую историю в feature-ветке и безопасное слияние в общую.
Merge сохраняет полную картину — кто, когда и откуда вливал изменения. Rebase делает историю линейной и читаемой. Ни один из подходов не лучше другого в абсолюте. Выбор зависит от того, что важнее вашей команде — полнота истории или её чистота.
📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤3
Go отлично справляется с циклами. Компилятор умеет автовекторизировать простые паттерны, и на современных процессорах с AVX-512 стандартный
for range работает быстро. Но не для всех операций. Сравнения: min, max, count; компилятор векторизирует хуже, и на больших массивах это заметно. TurboSlice закрывает именно эту нишу.turboslice — Go-библиотека, которая заменяет ручные циклы по слайсам на вызовы с SIMD-ускорением через 128-битные SSE-инструкции. Построена на пакете simd/archsimd из Go 1.26. Работает на любой платформе, но ускорение даёт на AMD64. На ARM64 и остальных архитектурах автоматически откатывается на скалярную реализацию.Без CGo, без ассемблерных файлов, без
unsafe в публичном API.Что умеет
Агрегации (
Sum, Min, Max, MinMax), поиск (Find, Contains, Count), поэлементная математика (AddSlices, MulSlices, DotProduct), а также набор дженерик-утилит (Map, Filter, Reduce, Chunk, Unique, Flatten и другие).Когда SIMD помогает, а когда нет
Авторы честно описывают границы применимости. Реальный выигрыш от SSE получают
Min, Max, MinMax, Count — от 2x до 2.6x на всех размерах. Для Sum и DotProduct[int32/float32] SIMD включается только на слайсах от 4K и 16K элементов соответственно. Ниже этих порогов накладные расходы на настройку SIMD съедают всю выгоду.А вот
Find, Contains, DotProduct[float64] и AddSlices[float64] остаются скалярными даже в SIMD-сборке. Компилятор Go на этих паттернах генерирует код лучше, чем ручной SSE. Для 64-битных целых умножений (MulSlices[int64], DotProduct[int64]) SSE/AVX2 просто не имеют нужной инструкции.Нужен Go 1.26+. Для SIMD-ускорения собираем с флагом:
GOEXPERIMENT=simd go build ./...
Пример использования:
import "github.com/atul-007/turboslice"
data := []int32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
turboslice.Sum(data) // 55
turboslice.Find(data, 7) // 6
turboslice.Min(data) // 1
turboslice.Max(data) // 10
turboslice.Contains(data, 5) // true
Вместо типичного цикла:
total := 0
for _, v := range data {
total += v
}
Пишем одну строку:
total := turboslice.Sum(data)
Typed API для горячих путей
Если тип известен заранее и нужна максимальная производительность, есть типизированные функции (
SumInt32, MinFloat64, CountInt32 и т.д.). Они инлайнятся без прохода через interface{} или type switch. На скалярной сборке работают наравне с ручным циклом, на SIMD-сборке получают ускорение.turboslice.SumInt32(data) // инлайнится напрямую
turboslice.MinFloat64(vals) // без диспатча через интерфейс
Пример из практики
Обработка сигналов:
signal := loadSensorData()
weights := precomputeWeights(len(signal))
weighted := turboslice.MulSlices(signal, weights)
energy := turboslice.DotProduct(signal, signal)
lo, hi := turboslice.MinMax(signal)
Аналитический пайплайн:
scores := fetchAllScores() // []int32, миллионы записей
total := turboslice.Sum(scores)
lo, hi := turboslice.MinMax(scores)
outliers := turboslice.Filter(scores, func(s int32) bool {
return s > 3*stddev
})
Что стоит учесть
Min, Max, MinMax паникуют на пустых слайсах, как и slices.Min/Max из стандартной библиотеки. AddSlices, MulSlices, DotProduct молча обрезают до минимальной длины. Переполнение целых чисел не проверяется. Обработка NaN различается между SIMD и скалярной реализацией для Min/Max.TurboSlice — нишевый инструмент. Если вы работаете с числовыми слайсами от десятков тысяч элементов на AMD64 и ваш bottleneck — агрегации типа min/max/count, библиотека даёт ощутимый прирост без компромиссов в безопасности кода. Для мелких слайсов и операций, где компилятор уже справляется, она просто не мешает.
📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6❤3
Пакет
testing в Go покрывает большинство задач без сторонних библиотек. Но сам по себе инструмент не гарантирует качество тестов. Ниже собраны практики, которые помогают писать тесты понятнее, надёжнее и проще в поддержке.1. Пишите table-driven тесты
Это идиоматический подход в Go. Все сценарии описываются в слайсе структур и прогоняются в цикле через
t.Run. Добавить новый кейс — одна строка. Сразу видно, что покрыто, а что нет:tests := []struct {
name string
input int
expected int
wantErr bool
}{
{"positive", 5, 25, false},
{"zero", 0, 0, false},
{"negative", -1, 0, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := Square(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("error = %v, wantErr %v", err, tt.wantErr)
}
if result != tt.expected {
t.Errorf("got %d, want %d", result, tt.expected)
}
})
}2. Используйте t.Run для подтестов
Подтесты позволяют запускать конкретный сценарий по имени. Это экономит время при отладке, когда не нужно гонять весь набор:
go test -run TestSquare/negative
3. Помечайте хелперы через t.Helper()
Без
t.Helper() стек ошибки укажет на строку внутри хелпера. С ним — на строку вызова в тесте:func assertEqual(t *testing.T, got, want int) {
t.Helper()
if got != want {
t.Errorf("got %d, want %d", got, want)
}
}4. Мокайте зависимости через интерфейсы
Определяете интерфейс, в проде подставляете реальную реализацию, в тестах — мок:
type Storage interface {
Get(id int) (*Item, error)
}
type MockStorage struct {
items map[int]*Item
}
func (m *MockStorage) Get(id int) (*Item, error) {
if item, ok := m.items[id]; ok {
return item, nil
}
return nil, errors.New("not found")
}5. Тестируйте поведение, а не реализацию
Если тест ломается при рефакторинге внутренней логики без изменения внешнего контракта — это плохой тест. Начинайте с покрытия экспортируемых функций. Один тест проверяет одну вещь.
6. Давайте тестам понятные имена
Имя теста должно описывать сценарий и ожидаемый результат. При падении вы должны понять, что сломалось, не открывая код.
7. Запускайте тесты с флагом -race
Детектор гонок ловит проблемы конкурентного доступа, которые почти невозможно найти вручную.
go test -race ./...
8. Не гонитесь за 100% покрытия
Высокий процент не гарантирует отсутствие багов. Покрывайте критическую логику и граничные случаи. Качество тестов важнее количества.
9. Используйте TestMain для дорогого setup
Если подготовка окружения занимает время, вынесите её в
TestMain. Она запускается один раз на весь пакет, а не перед каждым тестом:func TestMain(m *testing.M) {
setup()
code := m.Run()
teardown()
os.Exit(code)
}10. Выбирайте инструменты под задачу
Стандартного
testing хватает для большинства случаев. testify добавляет удобные ассерты и моки. httptest закрывает тестирование HTTP-хендлеров. testcontainers-go помогает с интеграционными тестами через Docker. Не тащите лишнего.📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
❤11👍10
🌐 h2c в Go 1.24+. HTTP/2 без TLS стало проще
Если вы запускаете Go-сервис в Google Cloud Run и используете долгоживущие соединения, у HTTP/1.1 есть известная проблема. Cloud Run не пробрасывает разрыв клиентского соединения на бэкенд. Сервер продолжает держать мёртвое соединение открытым, пока не истечёт таймаут. Решение — перейти на HTTP/2 cleartext (h2c).
Cloud Run сам терминирует TLS на фронтенде, а трафик до контейнера отправляет без шифрования. Можно выбрать HTTP/1.1 или h2c. Это тот самый режим «HTTP/2 with Prior Knowledge» из RFC 9113 (секция 3.3), когда клиент и сервер договариваются об HTTP/2 заранее, без Upgrade.
Как было до Go 1.24
Нужен был пакет
Выглядело громоздко:
Внешняя зависимость, неочевидный порядок вызовов, лишний бойлерплейт.
Как стало в Go 1.24+
Теперь всё настраивается через стандартный
Три строки вместо цепочки из обёртки, конфигурации и внешней зависимости. Сервер теперь принимает и HTTP/1.1, и h2c одновременно.
Проверка локально:
Если h2c не настроен, curl вернёт ошибку.
Конфигурация Cloud Run через Terraform
В описании контейнера порт нужно назвать
На стороне балансировщика ничего менять не нужно. Serverless NEG сам корректно апгрейдит соединение до HTTP/2 при общении с Cloud Run.
Go 1.24 убрал главное неудобство h2c — зависимость от
➡️ Источник
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
#GoToProduction
Если вы запускаете Go-сервис в Google Cloud Run и используете долгоживущие соединения, у HTTP/1.1 есть известная проблема. Cloud Run не пробрасывает разрыв клиентского соединения на бэкенд. Сервер продолжает держать мёртвое соединение открытым, пока не истечёт таймаут. Решение — перейти на HTTP/2 cleartext (h2c).
Cloud Run сам терминирует TLS на фронтенде, а трафик до контейнера отправляет без шифрования. Можно выбрать HTTP/1.1 или h2c. Это тот самый режим «HTTP/2 with Prior Knowledge» из RFC 9113 (секция 3.3), когда клиент и сервер договариваются об HTTP/2 заранее, без Upgrade.
Как было до Go 1.24
Нужен был пакет
golang.org/x/net/http2/h2c. Хендлер оборачивался в обёртку, потом отдельно конфигурировался http2.Server. Выглядело громоздко:
import (
"net/http"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
)
h2s := &http2.Server{}
handler = h2c.NewHandler(handler, h2s)
srv := &http.Server{
Addr: ":9888",
Handler: handler,
}
err = http2.ConfigureServer(srv, h2s)
Внешняя зависимость, неочевидный порядок вызовов, лишний бойлерплейт.
Как стало в Go 1.24+
Теперь всё настраивается через стандартный
http.Server. Никаких обёрток и внешних пакетов:srv := &http.Server{
Addr: ":9888",
Handler: handler,
}
srv.Protocols = new(http.Protocols)
srv.Protocols.SetHTTP1(true)
srv.Protocols.SetUnencryptedHTTP2(true)Три строки вместо цепочки из обёртки, конфигурации и внешней зависимости. Сервер теперь принимает и HTTP/1.1, и h2c одновременно.
Проверка локально:
curl -i --http2-prior-knowledge http://localhost:9888
Если h2c не настроен, curl вернёт ошибку.
Конфигурация Cloud Run через Terraform
В описании контейнера порт нужно назвать
h2c. Это сигнал для Cloud Run использовать HTTP/2 cleartext:resource "google_cloud_run_v2_service" "api" {
name = "api"
location = var.primary_region
template {
containers {
ports {
name = "h2c"
container_port = 9888
}
}
max_instance_request_concurrency = 200
timeout = "900s"
}
}На стороне балансировщика ничего менять не нужно. Serverless NEG сам корректно апгрейдит соединение до HTTP/2 при общении с Cloud Run.
Go 1.24 убрал главное неудобство h2c — зависимость от
x/net/http2/h2c и запутанную конфигурацию. Если вы работаете с Cloud Run и SSE или gRPC, или любыми долгоживущими соединениями, переход на h2c решает проблему с зависшими коннектами и настраивается за минуту.📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8
Map на новом слайсе пишется за минуту — все его видели:func Map[T, U any](slice []T, fn func(T) U) []U {
result := make([]U, len(slice))
for i, v := range slice {
result[i] = fn(v)
}
return result
}Работает. Просто. Но всегда выделяет новый слайс, даже если результат сразу пойдёт в следующую трансформацию.
Как использовать iter.Seq
С Go 1.23
Map можно написать иначе — возвращать не слайс, а итератор:func Map[A, B any](seq iter.Seq[A], f func(A) B) iter.Seq[B] {
return func(yield func(B) bool) {
for v := range seq {
if !yield(f(v)) {
return
}
}
}
}Функция возвращает замыкание, которое будет вызываться по требованию.
Пример:
numbers := slices.Values([]int{1, 2, 3, 4, 5})
result := slices.Collect(
Map(
Map(numbers, func(n int) int { return n * 2 }),
func(n int) int { return n + 10 },
),
)
// [12, 14, 16, 18, 20]Два
Map подряд — один проход по данным, одна аллокация на Collect. Промежуточного слайса нет.Нюансы
iter.Seq, slices.Values и slices.Collect — всё это Go 1.23+.Fluent-чейнинга
seq.Map(f).Map(g) не будет — в Go нельзя навесить дженерик-метод на произвольный тип. Живём с вложенными вызовами.📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍4🎉3👾2
Golang-разработчик — от 220 000 до 250 000 ₽, удаленно по Москве
Go-разработчик — офис/гибрид в Москве
Software Golang Engineer — удалённо in english.
#GoWork
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥2👏2
⚡️ Знакомьтесь с экспертом Proglib Academy: AI-архитектор Антон Будняк
Антон — мастер превращения сырых AI-идей в отказоустойчивые системы. Он знает, как запустить MVP за неделю и масштабировать его так, чтобы архитектура не рассыпалась под нагрузкой в сотни тысяч юзеров.
За что его ценит IT-комьюнити:
🟣 Опыт в финтехе и крупном бизнесе
🟣 Запуск продуктов на 6.000+ пользователей
🟣 Ускоряет разработку
📚 Где Антон черпает знания (рекомендации эксперта):
- X* — главный источник новостей. Рекомендую блог Бориса Черни (создателя Claude Code) — там база про использование ИИ в разработке.
- Нетворкинг в ТГ: чаты LLM под капотом и AI-чат — здесь можно найти ответ почти на любой технический вопрос.
- Новости AI: каналы Сиолошная и Denis Sexy IT.
На курсе Agentops Антон учит строить «неубиваемый» бэкенд: работать с очередями, таймаутами и балансировкой нагрузки, чтобы ваши агенты работали стабильно 24/7.
🎁 Майские СКИДКИ в Proglib Academy!
До конца мая на все курсы академии (включая AgentOps и разработку ИИ-агентов) действует скидка -40%. Это лучший момент, чтобы войти в AI-разработку под присмотром практиков.
Узнать больше о программе и обучении у Антона:
👉 Курс о том, как внедрять AI-логику в бэкенд и сохранять стабильность сервиса
Продолжаем знакомить вас с командой?
👍 — Да, ждем новых лиц
🔥 — Пойду подпишусь на каналы из списка Антона
🏃♀️ Proglib Academy
* - запрещен в рф
Антон — мастер превращения сырых AI-идей в отказоустойчивые системы. Он знает, как запустить MVP за неделю и масштабировать его так, чтобы архитектура не рассыпалась под нагрузкой в сотни тысяч юзеров.
За что его ценит IT-комьюнити:
Руководил разработкой ML-моделей в финтехе с экономическим эффектом более 100 млн ₽
Антон строит сервисы, которыми пользуются тысячи людей в реальном проде.
Оптимизировал ML-пайплайны и кратно сократил время от начала разработки до релиза
📚 Где Антон черпает знания (рекомендации эксперта):
- X* — главный источник новостей. Рекомендую блог Бориса Черни (создателя Claude Code) — там база про использование ИИ в разработке.
- Нетворкинг в ТГ: чаты LLM под капотом и AI-чат — здесь можно найти ответ почти на любой технический вопрос.
- Новости AI: каналы Сиолошная и Denis Sexy IT.
На курсе Agentops Антон учит строить «неубиваемый» бэкенд: работать с очередями, таймаутами и балансировкой нагрузки, чтобы ваши агенты работали стабильно 24/7.
🎁 Майские СКИДКИ в Proglib Academy!
До конца мая на все курсы академии (включая AgentOps и разработку ИИ-агентов) действует скидка -40%. Это лучший момент, чтобы войти в AI-разработку под присмотром практиков.
Узнать больше о программе и обучении у Антона:
👉 Курс о том, как внедрять AI-логику в бэкенд и сохранять стабильность сервиса
Продолжаем знакомить вас с командой?
👍 — Да, ждем новых лиц
🔥 — Пойду подпишусь на каналы из списка Антона
* - запрещен в рф
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2👏2❤1🥱1
Про
sync.Pool обычно пишут в контексте оптимизации. Меньше аллокаций, ниже давление на GC, быстрее обработка. Звучит как бесплатный буст, поэтому разработчики начинают пулить всё подряд. А потом бенчмарки показывают, что стало хуже. Разберём конкретные ситуации, когда sync.Pool не помогает или ломает то, что работало.Маленькие объекты
Пулить структуру из трёх int — хуже, чем просто создать новую. У пула есть собственные накладные расходы: упаковка в
any (interface boxing), type assertion при каждом Get, атомарные операции на P-локальном кеше. Для маленьких объектов эти расходы перевешивают стоимость обычного new. Вы добавляете сложность в код и получаете отрицательный результат.Простой способ проверить — бенчмарк с
-benchmem. Если B/op и allocs/op не упали заметно, а ns/op вырос, пул обходится дороже, чем аллокация.Объекты без сброса состояния
Это фабрика багов. Вы вызываете
Get, получаете старый bytes.Buffer с данными предыдущего запроса, забываете вызвать Reset() и начинаете дописывать к чужим данным. В реальных системах это приводило к тому, что данные одного пользователя утекали в ответ другого.Правильный паттерн — сбрасывать состояние сразу после
Get:buf := bufPool.Get().(*bytes.Buffer)
buf.Reset()
defer bufPool.Put(buf)
Можно сбрасывать и при
Put. Главное — выбрать одну стратегию и не отступать от неё. Но сброс при Get надёжнее, потому что защищает от случаев, когда кто-то положил объект в пул без очистки.Долгоживущие объекты
sync.Pool спроектирован для вещей, которые вы берёте, используете и возвращаете в рамках одной операции. Взяли буфер, записали ответ, вернули. Если объект живёт между запросами или хранит состояние на протяжении сессии, пул не нужен. Просто храните ссылку.Ещё один нюанс: GC может очистить пул в любой момент. Объекты в
sync.Pool не гарантированно доживают до следующего вызова Get. Если вы рассчитываете на то, что объект будет на месте, вы рассчитываете зря.Буферы переменного размера
Допустим, вы пулите
[]byte. Большинство буферов занимают 1KB, но изредка некоторые вырастают до 10MB. После возврата в пул эти 10MB остаются закреплёнными в памяти до следующего цикла GC. Под нагрузкой таких раздутых буферов накапливается много, и потребление памяти растёт непредсказуемо.Решение — ограничивать размер того, что возвращаете в пул:
defer func() {
if buf.Cap() < 64*1024 {
bufPool.Put(buf)
}
}()Буферы крупнее порога просто отпускаются в GC. Если размеры сильно разнятся, можно завести отдельный
sync.Pool для каждого класса размеров.sync.Pool вместо кеша
sync.Pool — это не кеш. GC может стереть его содержимое в любой момент, и вы ничего с этим не сделаете. Нет ни TTL, ни лимита на размер, ни гарантии сохранности. Если вам нужно, чтобы объекты жили дольше одного цикла GC, используйте полноценный кеш.Как понять, что пул вредит
Запустите бенчмарк с
-benchmem и сравните версии с пулом и без:BenchmarkHandler-8 500000 2400 ns/op 1024 B/op 3 allocs/op
BenchmarkHandlerPooled-8 800000 1500 ns/op 48 B/op 1 allocs/op
Такой результат означает, что пул работает. Но если
ns/op вырос, а allocs/op почти не изменился, пул не оправдал себя. Убирайте его без сожалений.В проде настоящий эффект пула виден через метрики GC: частоту запусков и длительность пауз (
runtime.ReadMemStats, pprof). Если пул не снижает эти показатели, он занимает место в коде без пользы.sync.Pool не стоит применять по умолчанию. Сначала профилируйте, найдите аллокации, которые реально влияют на производительность, и только потом пробуйте пул. Замерьте результат. Если улучшения нет, откатывайте. Большей части кода пул не нужен.📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max
#GoDeep
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5👏2
В Go 1.26
go fix переписан с нуля. Теперь это полноценный движок модернизации: заменяет if/else на min/max, переписывает циклы на range-over-int, подставляет strings.Cut вместо strings.Index — и ещё десятки трансформаций за один запуск.Звучит удобно. Но после
go fix ./... код может сломаться или, хуже, молча изменить поведение.Два типа конфликтов
Синтаксические —
go fix ловит сам и просит запустить повторно.Семантические — опаснее. Две независимые правки вместе могут сделать переменную неиспользуемой — и это уже ошибка компиляции. Лишние импорты инструмент уберёт сам, но с переменными придётся разбираться вручную.
Тихие изменения поведения
appendclipped заменяет append([]T(nil), s...) на slices.Clone(s). Если s пустой — Clone вернёт nil. Для JSON это разница между null и []. Анализатор по умолчанию выключен, но показательно.slicescontains заменяет цикл на slices.Contains. Если выражение для искомого элемента имело побочные эффекты — поведение изменится. Этот баг попал в трекер и был исправлен в 1.26.3.Как работать безопасно
Сначала смотрите diff без применения:
go fix -diff ./...
Запускайте из чистого git-состояния, в отдельной ветке.
Если сломалось — изолируйте анализатор:
go fix -slicescontains=false ./...
go fix -minmax ./...
Не забывайте про платформы: один запуск обрабатывает только текущую конфигурацию сборки. Для мультиплатформенных проектов запускайте с разными
GOOS/GOARCH.go fix — мощный инструмент, но не автопилот.📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max
#GoDeep
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6👍4🔥2
Если вы пишете на Go и вам нужен нативный GUI, выбор невелик. Есть
fyne, есть gio, есть веб-обёртки. Но если нужен полноценный Qt с виджетами, диалогами, таблицами и всей экосистемой — до недавнего времени единственным рабочим вариантом был therecipe/qt. У него LGPL-лицензия, нет поддержки Go-модулей, нет Qt 6, и архитектура через IPC с отдельным C++-бинарником.MIQT — это прямые биндинги Qt 5.15 и Qt 6.4+ через CGO, под лицензией MIT. Никаких промежуточных процессов, никаких рантайм-зависимостей от авторских серверов. Вы вызываете Qt напрямую из Go-кода.
Что внутри
Биндинги покрывают QtCore, QtGui, QtWidgets, QtMultimedia, QtNetwork, QtWebEngine, QtCharts, QML, и ещё десяток модулей. Есть поддержка subclassing (можно переопределять виртуальные методы вроде
PaintEvent), собственная реализация uic и rcc для работы с Qt Designer, и инструмент miqt-docker для кросс-компиляции.Проект стартовал в августе 2024, но уже набрал 600+ звёзд и оброс реальными приложениями: менеджер Docker-контейнеров, просмотрщик JSON, загрузчик видео, RTSP-клиент для камер.
Как выглядит код
API максимально приближен к оригинальному Qt C++. Контейнерные типы Qt (
QString, QList, QMap) проецируются в нативные Go-типы: string, []T, map[K]V. Сигналы и слоты работают через коллбэки:button := qt.NewQPushButton2("Нажми меня")
button.OnClicked(func() {
qt.QMessageBox_Information(
window,
"Привет",
"Это MIQT",
)
})Наследование классов Qt реализовано через встраивание структур. Чтобы передать
*qt.QLabel в функцию, принимающую *qt.QWidget, пишете myLabel.QWidget.Платформы
MIQT работает на Linux (x86_64, ARM64), Windows (x86_64), macOS (x86_64, ARM64), FreeBSD и Android. Поддерживается и статическая, и динамическая линковка. Для Windows и Android есть готовые Docker-образы для кросс-компиляции.
Нюансы, о которых стоит знать
Первая сборка занимает около 10 минут. Все последующие быстрые, потому что CGO-часть кешируется. На Go 1.26 стало ещё быстрее.
Бинарник по умолчанию получается большим. С флагами
-ldflags "-s -w" helloworld уменьшается с 43MB до 6MB. С upx --best — до 2MB.Сравнение указателей на Qt-объекты в Go работает не так, как в C++.
QTabWidget.CurrentWidget() каждый раз создаёт новую Go-структуру, оборачивающую тот же C++-указатель. Для сравнения используйте .UnsafePointer() или сравнивайте по индексу.Горутины мигрируют между OS-потоками, а Qt привязывает объекты к конкретному потоку. MIQT автоматически вызывает
runtime.LockOSThread() при создании QApplication. Для доступа к Qt-объектам из других горутин используйте mainthread.Wait() или mainthread.Start().Быстрый старт на Linux
Ставим зависимости, пример для Debian/Ubuntu с Qt 6:
apt install qt6-base-dev build-essential golang-go
Собираем:
go build -ldflags '-s -w'
Для кросс-компиляции под Windows:
go install github.com/mappu/miqt/cmd/miqt-docker@latest
miqt-docker win64-qt6-static -windows-build --tags=windowsqtstatic
MIQT — это способ писать Qt-приложения на Go без лицензионных ограничений LGPL. Биндинги зрелые, покрытие API широкое, кросс-платформенность работает. Если вам нужен нативный десктопный GUI на Go с полным набором виджетов, MIQT сейчас самый практичный вариант.
📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7
👀 Практический курс «Разработка AI-агентов для автоматизации задач, работы и собственных проектов» со скидкой 40% до конца мая!
Мы поговорили с десятками разработчиков, учли главные боли индустрии и запускаем полностью обновленный курс «ИИ-агенты 5.0». 🎉
Что вы узнаете?
- Как радикально оптимизировать траты на токены.
- Как на практике оценивать качество и точность работы агента.
- Как «докручивать» RAG-системы без потери качества.
- Как обеспечить устойчивость агента к сбоям внешних сервисов без падения всей системы, и про многое-многое другое.
Спикеры — практики с опытом в AI и Data Science в крупных IT-компаниях, таких как Яндекс, Сбер, Raft и Газпромбанк др.
Длительность: 6–12 недель в зависимости от тарифа.
👉 Занимайте место на главном агентском интенсиве по лучшей цене
Мы поговорили с десятками разработчиков, учли главные боли индустрии и запускаем полностью обновленный курс «ИИ-агенты 5.0». 🎉
Что вы узнаете?
- Как радикально оптимизировать траты на токены.
- Как на практике оценивать качество и точность работы агента.
- Как «докручивать» RAG-системы без потери качества.
- Как обеспечить устойчивость агента к сбоям внешних сервисов без падения всей системы, и про многое-многое другое.
Спикеры — практики с опытом в AI и Data Science в крупных IT-компаниях, таких как Яндекс, Сбер, Raft и Газпромбанк др.
Длительность: 6–12 недель в зависимости от тарифа.
👉 Занимайте место на главном агентском интенсиве по лучшей цене
❤1🔥1
Разработчик написал проход автовекторизации для компилятора Go на уровне SSA. Проход использует алгоритм SLP (Superword Level Parallelism) и работает поверх экспериментального
GOEXPERIMENT=simd. Поддерживаются arm64 и amd64.Компилятор сам находит в коде группы одинаковых скалярных операций и заменяет их на SIMD-инструкции. Без каких-либо правок со стороны разработчика. Например, четыре отдельных умножения float64 превращаются в два векторных.
Прототип пока консервативный: 128-битные векторы, без shuffles, без reductions, без cost model. Но уже работает на реальных кодовых базах и находит сотни мест для векторизации в etcd и самом
cmd/compile.Сообщество отнеслось с интересом, но просит бенчмарки вместо подсчёта регионов и приоритизацию модели стоимости.
📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max
#GoLive
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤2🔥2
Допустим, у вас есть вспомогательная функция в тестах:
func assertEqual(t *testing.T, got, want int) {
if got != want {
t.Errorf("got %d, want %d", got, want)
}
}Тест упал. В логе вы видите строку внутри assertEqual, а не место вызова в тесте.
Смогли исправить? Газ смотреть ответ
📍 Навигация: Вакансии • Задачи • Собесы • Канал в Max
#ReadySetGo
Please open Telegram to view this post
VIEW IN TELEGRAM