Библиотека Go для собеса | вопросы с собеседований
Открыть в Telegram
Вопросы с собеседований по Go и ответы на них. Покажем, как запустить своего ии-агента: https://clc.to/tvpmD По рекламе: @proglib_adv Для обратной связи: @proglibrary_feeedback_bot Наши каналы: https://t.me/proglibrary/9197
Больше7 429
Подписчики
-324 часа
-167 дней
+930 день
Архив постов
❓ Как корректно прерывать паузы в Go при завершении программы
Стандартная функция
time.Sleep() в Go имеет существенный недостаток — она блокирует выполнение и не реагирует на сигналы завершения работы через context. Горутина замирает на указанное время, игнорируя любые попытки её остановить.
Вместо прямого использования time.Sleep() можно комбинировать select с time.After() и каналом контекста:
select {
case <-time.After(duration):
// Пауза завершилась естественным образом
case <-ctx.Done():
// Контекст отменён — немедленно выходим
return ctx.Err()
}
Такой подход позволяет горутине мгновенно реагировать на отмену контекста.
🎁 Новогодняя акция: 3 курса по цене 1
🤝 Помощь с выбором курса
🐸Библиотека Go для собеса🦾 ИИ в Data Engineering: что реально работает, а что может навредить?
На открытом вебинаре разберём практические кейсы: как ИИ автоматизирует сопоставление схем, повышает качество данных, оптимизирует ETL и снижает операционную нагрузку на команды. Покажем, как AI уже встроен в современные платформы и инструменты — от Databricks до AWS Glue — и где именно он даёт измеримый эффект. Обсудим архитектурные паттерны и типичные ошибки внедрения, которые ломают пайплайны вместо того, чтобы улучшать их.
Вы поймёте, какие задачи действительно стоит усиливать ИИ, а где инженерное мышление по-прежнему важнее моделей. Получите чёткий план внедрения AI в текущие процессы и увидите, какие навыки становятся критичными для Data Engineer ближайших лет.
🗓Встречаемся 29 января в 20:00 МСК в преддверии старта курса «Data Engineer». Регистрация открыта:
https://clc.to/E_bmYA
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
❓ Объясните систему пакетов в Go
Каждый .go файл стартует с декларации
package название — это определяет, к какой логической группе относится код.
Пакет с именем main компилируется в исполняемый бинарник. Без main — это библиотеки для переиспользования в других проектах.
Никаких магических автоимпортов — каждую зависимость указываете руками: import "github.com/user/repo/pkg". Go-компилятор сразу видит все связи между модулями.
Простое, но мощное правило:
PublicFunc() — заглавная буква, доступна везде
privateFunc() — строчная буква, видна только внутри пакета
Это работает для всего: функций, типов, структур, переменных. Никаких ключевых слов public/private — компилятор смотрит только на первую букву.
🐸Библиотека Go для собеса❓ Зачем нужен CGO_ENABLED=0 при сборке Go-приложений
Флаг
CGO_ENABLED=0 вырубает CGo на этапе компиляции — ваш код собирается полностью на чистом Go, без единой строчки из C-библиотек.
При CGO_ENABLED=1 компилятору нужны C-тулчейн и хедеры для целевой платформы. С CGO_ENABLED=0 просто делаете GOOS=linux GOARCH=arm64 go build — и готово, бинарник для любой архитектуры собран за секунды.
Можно использовать минималистичные образы типа scratch или distroless — приложение запустится даже в контейнере без единой установленной библиотеки.
Убирая C-код, вы автоматически избавляетесь от целого класса уязвимостей: buffer overflow, use-after-free и прочих радостей ручного управления памятью. Плюс код становится действительно платформонезависимым.
🐸Библиотека Go для собеса❓ Как писать обёртки для фаззинг-тестов в Go
Фаззинг-тест начинается с
func Fuzz и принимает *testing.F:
func FuzzParse(f *testing.F) {
// Seed-корпус
f.Add("valid input")
f.Add("edge case")
// Фаззинг-функция
f.Fuzz(func(t *testing.T, input string) {
result, err := Parse(input)
if err != nil {
return // Ошибка — нормально
}
// Проверки инвариантов
if result == nil {
t.Error("результат не должен быть nil при отсутствии ошибки")
}
})
}
Часто тестируемая функция принимает сложные типы, а фаззер работает только с базовыми: string, []byte, int, bool и несколькими другими. Обёртка конвертирует примитивы в нужные структуры:
func FuzzUserValidation(f *testing.F) {
f.Add("john@example.com", 25, true)
f.Fuzz(func(t *testing.T, email string, age int, active bool) {
// Обёртка: преобразуем примитивы в структуру
user := User{
Email: email,
Age: age,
Active: active,
}
err := ValidateUser(user)
// Проверяем инварианты
if age < 0 && err == nil {
t.Error("отрицательный возраст должен вызывать ошибку")
}
})
}
🎁 Новогодняя акция: 3 курса по цене 1
🤝 Помощь с выбором курса
🐸 Библиотека Go для собеса🔼 Требования к Go-разработчикам растут быстрее, чем обновляются резюме. То, что вчера считалось сильным навыком, завтра становится необходимым опытом.
🗓 15 января в 20:00 МСК пройдет встреча с двумя экспертами, которые формируют требования к рынку: рекрутером Ксенией Маловой, знающей, почему 90% кандидатов отсеиваются ещё до техсобеса, и архитектором Александром Хохловым, который понимает, какие инженерные компетенции станут критичными для новых проектов.
❗️ Разберём тренды, требования и реальные ожидания компаний, поговорим о том, какие пробелы чаще всего мешают росту и как сформировать долгосрочную траекторию развития в Go-разработке.
▶️ Встречаемся в преддверие старта курса «Golang Developer. Professional», регистрация открыта: https://clc.to/cp02dQ
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru
❓ Как отправителю правильно закрыть канал
В таком случае, обычно используется дополнительный канал, называемый каналом управления или сигнальным каналом, который получатель может использовать для отправки сигнала об остановке.
После получения сигнала, отправитель может корректно закрыть основной канал данных:
func main() {
dataCh := make(chan int)
stopCh := make(chan struct{})
go func() {
for {
select {
case data, ok := <-dataCh:
if !ok {
// Канал закрыт, прекращаем обработку
return
}
// Обработка данных
fmt.Println(data)
case <-stopCh:
// Получен сигнал остановки, закрываем канал dataCh
close(dataCh)
return
}
}
}()
// Отправка данных в канал
dataCh <- 1
dataCh <- 2
// Отправка сигнала остановки
stopCh <- struct{}{}
}
🎁 Новогодняя акция: 3 курса по цене 1
🤝 Помощь с выбором курса
🐸Библиотека Go для собеса❓ Как вам вопросы прошедшей недели
Оцените их по шкале 🔥,❤️,👍,😢, 🥱,
где 🔥 — это супер, а 🥱 — это скучно.
Также приветствуется фидбек в комментах.
🐸 Библиотека Go для собеса
❓ Можно ли закрыть канал со стороны читателя
В Go есть правило: канал закрывает только отправитель. Если читатель закроет канал, а отправитель попытается записать в него данные — программа упадёт с паникой.
Паника при записи в закрытый канал — это не баг, а защита от гонок данных. Отправитель точно знает, когда закончил отправлять. Читатель — не знает.
Но технически читатель закрыть канал может.
🎁 Новогодняя акция: 3 курса по цене 1
🤝 Помощь с выбором курса
🐸Библиотека Go для собеса
❓ Сколько памяти занимает слайс
Заголовок слайса занимает 24 байта на 64-битной системе. Это фиксированный размер независимо от содержимого.
Внутренняя структура слайса выглядит так:
type slice struct {
array unsafe.Pointer // 8 байт — указатель на underlying array
len int // 8 байт — текущая длина
cap int // 8 байт — ёмкость
}
24 байта — это только заголовок. Сами данные хранятся отдельно в массиве, на который указывает array.
Пример расчёта общего размера:
s := make([]int64, 100)
// Заголовок: 24 байта
// Данные: 100 * 8 = 800 байт
// Итого: 824 байта
При передаче слайса в функцию копируется только заголовок, а не данные — поэтому слайсы дешёвые в передаче.
🎁 Новогодняя акция: 3 курса по цене 1
🤝 Помощь с выбором курса
🐸Библиотека Go для собеса❓ Как корректно посчитать количество символов в строке
Функция
len() возвращает размер в байтах, а не в символах. Для Unicode-текста это критически важно.
Кириллица кодируется в UTF-8 двумя байтами, китайские иероглифы — тремя, а emoji, например, может занимать до четырёх байт. Поэтому для подсчёта символов нужно использовать utf8.RuneCountInString().
🎁 Новогодняя акция: 3 курса по цене 1
🤝 Помощь с выбором курса
🐸Библиотека Go для собеса❓ Как встроить профайлер в приложение
В Go есть пакет
net/http/pprof, который при импорте автоматически регистрирует HTTP-обработчики для сбора метрик. Обычно профайлер запускают на отдельном порту, чтобы не смешивать с основным API.
import (
"net/http"
_ "net/http/pprof" // подчёркивание означает импорт ради side-effect
)
func main() {
// Запускаем профайлер в отдельной горутине на порту 6060
go func() {
http.ListenAndServe(":6060", nil)
}()
// Здесь основная логика приложения...
}
После запуска становятся доступны эндпоинты для анализа:
• /debug/pprof/profile — нагрузка на процессор
• /debug/pprof/heap — использование памяти
• /debug/pprof/goroutine — состояние горутин
• /debug/pprof/block — блокировки
🎁 Новогодняя акция: 3 курса по цене 1
🤝 Помощь с выбором курса
🐸Библиотека Go для собеса❓ Как преобразовать int в строку и обратно
Использовать пакет
strconv:
// Строка → int
num, err := strconv.Atoi("42")
// int → строка
str := strconv.Itoa(42)
// Для других типов:
i64, _ := strconv.ParseInt("42", 10, 64) // string → int64
str64 := strconv.FormatInt(42, 10) // int64 → string
Atoi возвращает ошибку, если строка не число — проверяйте err
🎁 Новогодняя акция: 3 курса по цене 1
🤝 Помощь с выбором курса
🐸Библиотека Go для собеса❓ Как глубоко доходит отмена контекста
Отмена в Go всегда течёт сверху вниз: как только отменяется родительский контекст, автоматически отменяются все его дочерние контексты на любом уровне вложенности.
Обратного эффекта нет: отмена дочернего контекста не затрагивает родителя и других веток, только эту конкретную часть.
🎁 Новогодняя акция: 3 курса по цене 1
🤝 Помощь с выбором курса
🐸Библиотека Go для собеса
❓ Что такое Cache Contention
Допустим, у вас есть структура со счётчиками, которые обновляют разные горутины:
type Counters struct {
a int64
b int64
c int64
}
Каждая горутина работает со своим полем. Конфликтов нет, мьютексы не нужны. Но производительность почему-то проседает.
Проблема в том, как устроен кеш процессора. CPU не читает отдельные переменные — он загружает данные кеш-линиями по 64 байта. Все три поля структуры попадают в одну линию.
Когда горутина на одном ядре изменяет поле a, процессор инвалидирует эту кеш-линию на всех остальных ядрах. Другое ядро хочет изменить b — ему приходится заново загружать линию из памяти. И так по кругу.
Это и есть Cache Contention, или false sharing — ядра конкурируют за кеш-линию, хотя работают с разными данными.
🎁 Новогодняя акция: 3 курса по цене 1
🤝 Помощь с выбором курса
🐸Библиотека Go для собеса❓ Как вам вопросы прошедшей недели
Оцените их по шкале 🔥,❤️,👍,😢, 🥱,
где 🔥 — это супер, а 🥱 — это скучно.
Также приветствуется фидбек в комментах.
🐸 Библиотека Go для собеса
❓ Какие виды релизов вы знаете
По стабильности:
• Alpha — ранняя версия, функциональность не заморожена, баги ожидаемы. Для внутреннего тестирования.
• Beta — фичи готовы, но могут быть проблемы. Уже можно показывать внешним тестировщикам.
• Release Candidate — кандидат в релиз. Если критичных багов не найдут, станет финальной версией.
• Stable / General Availability — готов к продакшену.
По масштабу изменений:
• Major — ломающие изменения, несовместимость с предыдущей версией. 2.0.0 → 3.0.0.
• Minor — новая функциональность с обратной совместимостью. 2.1.0 → 2.2.0
• Patch — багфиксы без изменения API. 2.1.1 → 2.1.2
По способу деплоя:
• Canary — выкатка на небольшой процент пользователей
• Blue-green — две идентичные среды, переключение трафика между ними
• Rolling release — постепенное обновление без конкретных версий
🎁 Новогодняя акция: 3 курса по цене 1
🤝 Помощь с выбором курса
🐸Библиотека Go для собеса
❓ Какие у GOOS могут быть значения
Узнать список значений:
go tool dist listКоманда выведет все комбинации GOOS/GOARCH, которые поддерживает ваша версия Go. Пример:
aix/ppc64 android/386 android/amd64 android/arm android/arm64 darwin/amd64 darwin/arm64 dragonfly/amd64 freebsd/386 freebsd/amd64 freebsd/arm freebsd/arm64 freebsd/riscv64 illumos/amd64 ios/amd64 ios/arm64 js/wasm linux/386 linux/amd64 linux/arm linux/arm64 linux/loong64 linux/mips linux/mips64 linux/mips64le linux/mipsle linux/ppc64 linux/ppc64le linux/riscv64 linux/s390x netbsd/386 netbsd/amd64 netbsd/arm netbsd/arm64 openbsd/386 openbsd/amd64 openbsd/arm openbsd/arm64 openbsd/ppc64 openbsd/riscv64 plan9/386 plan9/amd64 plan9/arm solaris/amd64 wasip1/wasm windows/386 windows/amd64 windows/arm64🎁 Новогодняя акция: 3 курса по цене 1 🤝 Помощь с выбором курса 🐸Библиотека Go для собеса
❓ Можно ли через //go:linkname получить доступ к приватной функции стандартной библиотеки
Да, эта директива позволяет связать локальное имя с любым символом в рантайме, включая неэкспортируемые функции. Например, так можно вызвать приватную функцию runtime.nanotime:
package main
import _ "unsafe"
//go:linkname nanotime runtime.nanotime
func nanotime() int64
func main() {
println(nanotime())
}
Но использовать это в продакшене не стоит. Приватные функции — это деталь реализации. Они могут измениться или исчезнуть в следующей версии Go без предупреждения.
🎁 Новогодняя акция: 3 курса по цене 1
🤝 Помощь с выбором курса
🐸 Библиотека Go для собеса❓ Можно ли передавать канал в функцию как аргумент
Да, в канал можно передавать в функцию как аргумент.
При этом можно ограничить направление канала:
// Только для отправки
func send(ch chan<- int) {
ch <- 42
}
// Только для чтения
func receive(ch <-chan int) {
val := <-ch
}
// Двунаправленный
func process(ch chan int) {
ch <- 1
val := <-ch
}
Ограничение направления помогает на этапе компиляции предотвратить ошибки: функция не сможет случайно читать из канала, предназначенного только для записи, и наоборот.
🎁 Новогодняя акция: 3 курса по цене 1
🤝 Помощь с выбором курса
🐸 Библиотека Go для собеса
Уже доступно! Исследование Telegram 2025 — ключевые инсайты года 
