fa
Feedback

فریب کلاهبرداران را نخورید! تل‌متریو این کانال‌ها را شناسایی و برچسب‌گذاری می‌کند 👉 برای مشاهده برچسب، اشتراک تهیه کنید

Библиотека Go для собеса | вопросы с собеседований

Библиотека Go для собеса | вопросы с собеседований

رفتن به کانال در Telegram

Вопросы с собеседований по Go и ответы на них. Учиться у нас: clc.to/iEeaZw По рекламе: @tproger_sales_bot Для обратной связи: @proglibrary_feeedback_bot Наши каналы: https://t.me/proglibrary/9197

نمایش بیشتر
7 419
مشترکین
-1224 ساعت
-237 روز
-4530 روز
آرشیو پست ها
❓ Как вам вопросы этой недели
Anonymous voting

❓ Чем отличаются Lock-Free и Wait-Free алгоритмы Оба подхода относятся к неблокирующей синхронизации, но дают разные гарантии прогресса. Lock-Free • Гарантируют, что хотя бы один поток завершит операцию за конечное число шагов. • Отдельные потоки при этом могут надолго застревать в повторных попытках (starvation). • Проще в реализации и обычно показывают высокую производительность. Wait-Free • Гарантируют, что каждый поток завершит операцию за конечное число шагов. • Сложнее в реализации и часто медленнее из-за более тяжёлой координации между потоками. • Применяются там, где критично равноправие (fairness) — например, в системах реального времени. Коротко: Lock-Free гарантирует прогресс системы в целом, а Wait-Free — прогресс каждого отдельного потока. 🐸 Библиотека Go для собеса

❓ Как отличить закрытый канал от нулевого значения Значение само по себе ничего не говорит. Если по каналу chan int пришёл 0, это может быть и реально отправленный ноль, и нулевое значение типа после закрытия. Поэтому в Go для приёма есть вторая переменная.
v, ok := <-ch
if !ok {
    // канал закрыт и полностью вычитан
    return
}
fmt.Println(v)
ok равен false только тогда, когда канал закрыт и в буфере ничего не осталось. Пока в буфере есть данные, приём из закрытого канала возвращает их с ok == true. То есть закрытие не отбрасывает уже отправленные значения. 🐸 Библиотека Go для собеса

🧑‍💻 Учиться агентной разработке — у тех, кто уже внедряет её в бигтехе Курс «ИИ для разработчиков» ведут практики из ведущи
🧑‍💻 Учиться агентной разработке — у тех, кто уже внедряет её в бигтехе Курс «ИИ для разработчиков» ведут практики из ведущих российских IT-компаний 🔥 🔵 Они прошли путь от первых экспериментов с AI до рабочих процессов, в которых агенты помогают команде писать код, готовить PR и быстрее доводить задачи до продакшена. В ближайшее время познакомим вас с каждым преподавателем. Программа курса — по ссылке 🎹 🏃‍♀️ Proglib Academy

🤷‍♂️ Что важнее в хорошем курсе: программа или люди, которые её создают? У Proglib Academy вышел пост о том, на каком опыте построен новый курс по AI для разработчиков 🦴

❓ Что такое Compare-And-Swap (CAS)? CAS — это атомарная операция, лежащая в основе многих lock-free алгоритмов. Она позволяет синхронизировать доступ к общим данным без блокировок. Принцип прост: операция обновляет значение переменной только в том случае, если её текущее значение совпадает с ожидаемым. Если за это время значение успел изменить другой поток — обновление не произойдёт, и вы об этом узнаете. Схематично CAS работает с тремя аргументами: • адрес переменной, • ожидаемое значение, • новое значение. Если текущее значение равно ожидаемому — оно заменяется на новое. Иначе операция сообщает о неудаче, и поток может повторить попытку. 🐸 Библиотека Go для собеса

❓ Как ограничить количество одновременных запросов к внешнему API. Какой примитив вы возьмёте и почему Сначала уточнение, за которое дают плюс на собесе: важно понять, что именно ограничиваем. Concurrency limit — сколько запросов летит одновременно (не больше 10 в моменте). Примитив — семафор. Rate limit — сколько запросов в единицу времени (100 rps). Примитив — token bucket, x/time/rate. Их часто путают. «Не больше 10 параллельных» — первое. «Не больше 100 в секунду» — второе. Дальше про concurrency. Хороший выбор — семафор. В Go проще всего сделать его на буферизированном канале: канал ёмкости N — это счётчик свободных слотов:
sem := make(chan struct{}, 10)

for _, req := range requests {
    sem <- struct{}{}            // заняли слот
    go func(r Request) {
        defer func() { <-sem }() // освободили
        callExternalAPI(r)
    }(req)
}
struct{} — потому что нужен только факт занятости, он весит 0 байт. Канал даёт и счётчик, и блокировку сразу. На практике берут errgroup.SetLimit — тот же семафор, но с ошибками, Wait и отменой контекста из коробки:
g, ctx := errgroup.WithContext(ctx)
g.SetLimit(10)

for _, req := range requests {
    req := req
    g.Go(func() error {
        return callExternalAPI(ctx, req)
    })
}
return g.Wait()
🐸 Библиотека Go для собеса

🫡 Один и тот же запрос к AI можно выполнить двумя способами — и получить совершенно разный результат. 1️⃣ способ — просто по
🫡 Один и тот же запрос к AI можно выполнить двумя способами — и получить совершенно разный результат. 1️⃣ способ — просто попросить написать код. Модель может сгенерировать рабочее решение, но именно на этапе проверки часто всплывают пропущенные edge case’ы, ошибки обработки или проблемы с тестами. 2️⃣ способ — дать агенту цикл Think → Act → Observe. Он не останавливается после первой попытки: планирует действия, проверяет результат и, если находит ошибку, исправляет её и пробует снова. Самое интересное, что разница здесь не столько в модели ❕ Claude, GPT или другая LLM могут быть одинаковыми. Разницу создаёт инженерная система вокруг них. 📅 Именно это разберём 23 июля в 19:00 (МСК) на бесплатном вебинаре с Алексеем Жиряковым (Executive Director в Сбере, GenAI Data Platform, ex-CTO Stream и KION). На живом демо покажем, как один и тот же запрос работает без агентной системы и с ней 🔥 🔗 Регистрация 🏃‍♀️ Proglib Academy

❓ Как спроектировать пул воркеров для обработки задач из очереди. Сколько горутин запускать и от чего зависит это число Базовая конструкция — один канал jobs как очередь и N воркеров, которые из него читают. Канал сам работает балансировщиком: свободный воркер забирает следующую задачу, вручную раздавать ничего не нужно. Для управления жизненным циклом добавляем context (отмена, таймаут, shutdown), sync.WaitGroup (дождаться завершения всех) и правило «кто пишет в канал, тот его и закрывает» — results закрываем только после wg.Wait(), иначе паника. Закрытие jobs — сигнал воркерам, что задачи кончились. Сколько горутин — зависит от природы задачи. Для CPU-bound (вычисления, парсинг) смысла запускать больше горутин, чем ядер, нет: они будут драться за те же ядра и добавлять оверхед на переключение контекста. Ориентир — runtime.GOMAXPROCS(0). Для IO-bound (БД, HTTP, диск) горутина большую часть времени спит в ожидании ответа, поэтому воркеров имеет смысл сильно больше ядер — десятки, сотни. Но потолок здесь задаёт не CPU, а узкое место вниз по потоку: пул коннектов к БД, rate limit API, лимит дескрипторов. Запускать 500 воркеров при 20 коннектах к базе бессмысленно. Грубая прикидка через закон Литтла: N ≈ ядра × (1 + время_ожидания / время_CPU). Плюс не забываем про память — горутина дешёвая (~2–8 КБ стека), но при сотнях тысяч воркеров плюс их буферы это уже гигабайты. Правильный инженерный ответ: точное число не угадывается, а подбирается. Берём разумный старт (GOMAXPROCS для CPU, кратно больше для IO), вешаем метрики (p99-латентность, throughput, глубина очереди, загрузка CPU) и тюним под реальную нагрузку. Число воркеров — это параметр конфигурации, а не константа в коде. И отдельно важен backpressure: если продьюсер быстрее пула, нужно заранее решить — копить в буфере, блокировать продьюсера или дропать задачи, иначе очередь растёт до OOM. 🐸 Библиотека Go для собеса

🔥 Курс «Разработка ИИ-агентов» стартовал, но вы еще успеваете присоединиться! Мы только начали, а первый практический воркшо
🔥 Курс «Разработка ИИ-агентов» стартовал, но вы еще успеваете присоединиться! Мы только начали, а первый практический воркшоп пройдет только 23 июля. До этого времени вы спокойно успеете изучить материалы и нагнать группу. 💼 Главная фишка: финальный проект в портфолио:
В финальном проекте вы сможете выбрать один из двух вариантов: реализовать свою идею или решить задачу от партнера курса — крупной российской финтех-компании. Вы будете работать с датасетом, построенным по реальным сценариям, получите бизнес-контекст и продовые ограничения. В результате соберете AI-агента, который станет сильным проектом для портфолио.
🎁 Упомяните менеджеру специальное предложение «3 курса по цене 1»: Берете VIP-тариф — получаете курс «Разработка ИИ-агентов», хардкорный «AgentOps» и ещё один курс на выбор. Выгода 129.000 ₽! Двери потока вот-вот закроются окончательно. Успейте забрать стек курсов и начать работу над реальным проектом 🔗 Занять место и забрать 3 курса

❓ Как предотвратить XSS и SQL инъекции Мы никогда не склеиваем запрос строкой. Всегда плейсхолдеры драйвера, значения уходят отдельно от текста запроса:
row := db.QueryRowContext(ctx, "SELECT id FROM users WHERE email = $1", email)
Имена таблиц и колонок так подставить нельзя, для них белый список допустимых значений:
var allowed = map[string]string{"created": "created_at", "email": "email"}
col, ok := allowed[r.URL.Query().Get("sort")]
if !ok {
    col = "created_at"
}
query := fmt.Sprintf("SELECT id FROM users ORDER BY %s DESC", col)
Дополнительно ограниченные права у пользователя базы, чтобы ошибка не стоила всей схемы. XSS Пользовательский текст не попадает в HTML напрямую. Рендерим через html/template, он экранирует с учётом контекста, то есть по разному внутри тега, атрибута, href и блока <script>. text/template не экранирует ничего, в веб хендлере его быть не должно. Приводить ввод к template.HTML нельзя, это отключение защиты. Если пользователю нужна разметка, чистим санитайзером вроде bluemonday по белому списку тегов. Для API хватает encoding/json, но заголовки обязательны, иначе браузер угадает тип и отрендерит ответ как страницу:
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
Сверху Content-Security-Policy и куки с HttpOnly, чтобы случайная дыра не привела к краже сессии. 🐸 Библиотека Go для собеса

🔥 Стартуем СЕГОДНЯ! Новый поток курса «Разработка ИИ-агентов» открыт По этому поводу мы решили выложить закрытую запись одно
🔥 Стартуем СЕГОДНЯ! Новый поток курса «Разработка ИИ-агентов» открыт По этому поводу мы решили выложить закрытую запись одного из уроков из программы. Найти её в поиске YouTube нельзя — она доступна только по ссылке и всем, кто будет на курсе. Внутри глубокий разбор LLM от Алексея Яндутова (Senior ML-инженер, развивал ответы «Алисы» и «Нейро» в Яндексе). Учимся получать точный результат без галлюцинаций. Что внутри урока:
- Устройство LLM. - Рабочие шаблоны промптов (Persona, Chain-of-Thought и др.). - Разбор реального кейса Яндекса. Как автоматизировать разметку, обойти качество людей на 5% и срезать косты на 60%.
После просмотра вы поймете, когда хватает промпт-инжиниринга, а когда нужен RAG или fine-tuning. 👉Смотреть закрытый урок на YouTube Понравился урок? Переходите на новый уровень! Оставляйте заявку на курс, чтобы научиться проектировать надежные автономные системы. Обучение началось, но вы еще успеваете присоединиться. 🔗 Занять место на курсе

🚀 Уже завтра стартует новый поток курса «ИИ-агенты»! Мы собрали мощнейший состав преподавателей. Учить вас проектировать арх
🚀 Уже завтра стартует новый поток курса «ИИ-агенты»! Мы собрали мощнейший состав преподавателей. Учить вас проектировать архитектуру и собирать продакшн-агентов будут инженеры и исследователи из топовых IT-компаний. Старт уже завтра! Сомневаетесь, подойдет ли вам программа и подача? Начните с бесплатного демо-урока! Всего за 2 часа вы заглянете под капот ИИ-агента, поймете, чем мышление модели отличается от ее ответа, и научите систему чинить собственный код. Это идеальный способ протестировать нашу платформу перед покупкой. 🔗 Пройти демо-урок и занять место на курсе

❓ Как найти подстроку в строке Задача звучит просто, но у неё три разных ответа. Нужно узнать, есть ли подстрока вообще. Нужно узнать, где она находится. Нужно достать то, что вокруг неё. Для каждого случая в пакете strings есть своя функция, и брать не ту это лишняя работа. Просто проверить наличие
if strings.Contains(s, "error") {
    // подстрока есть
}
strings.Contains возвращает bool. Рядом живут strings.HasPrefix и strings.HasSuffix для начала и конца строки, они дешевле, потому что не сканируют всё подряд.
if strings.HasPrefix(path, "/api/") {
    ...
}
Ещё есть strings.ContainsAny, которая проверяет вхождение любого символа из набора, и strings.ContainsRune для одной руны. Найти позицию
i := strings.Index(s, "role=")
if i == -1 {
    return errors.New("not found")
}
strings.Index возвращает байтовый индекс первого вхождения либо -1. Проверка на -1 обязательна, иначе получите панику при попытке сделать срез. strings.LastIndex ищет с конца. strings.IndexByte работает с одним байтом и заметно быстрее, если ищете, например, запятую. Индекс байтовый, но подставлять его в срез безопасно. Совпадение подстроки всегда начинается на границе символа, потому что UTF-8 самосинхронизирующаяся кодировка. Достать содержимое Чаще всего индекс нужен не сам по себе, а чтобы отрезать кусок. Тогда strings.Cut короче и понятнее.
before, after, found := strings.Cut("key=value", "=")
if !found {
    return errors.New("bad format")
}
// before "key", after "value"
Это идиоматичный способ с Go 1.18. Он заменяет связку Index плюс два среза и не даёт ошибиться со смещением на длину разделителя. 🐸 Библиотека Go для собеса

🤨 Как заставить AI-агента не ломать архитектуру, а нормально писать код? Мы привыкли общаться с ИИ промптами, но для автоном
🤨 Как заставить AI-агента не ломать архитектуру, а нормально писать код? Мы привыкли общаться с ИИ промптами, но для автономных систем это путь к бесконечным циклам и сливу бюджета. Чтобы убрать хаос, инженеры переходят на Spec-Driven Development (SDD). Вот как этот подход меняет работу агента на практике: 🔹Контракт вместо текста. Сначала пишется строгая спецификация (JSON-схема/OpenAPI) и автотесты. Агент зажат в рамки интерфейсов, за которые физически не может выйти. 🔹Контроль на шагах. Внутри петли Think-Act-Observe агент сверяет действия со спецификацией. 🔹Саморефлексия. Если ИИ нарушил типы или «додумал» лишнее, тест падает. Агент получает ошибку в контекст и сам правит код, не выходя за рамки ТЗ. Это лишь база того, как укротить ИИ-разработку. Если вы хотите глубоко внедрить эту методологию, научиться проектировать архитектурные контракты и собирать отказоустойчивые системы —оставляйте заявку на наш новый курс по Spec-Driven Development. Стартуем совсем скоро🙂

❓ Как извлечь подстроку из строки Строка в Go это неизменяемый срез байт. Поэтому подстрока берётся так же, как срез, но с одной оговоркой. Индексы считаются в байтах, а не в символах. На ASCII разницы не видно, на кириллице или эмодзи всё ломается. Срез по байтам
s := "hello world"
fmt.Println(s[0:5]) // hello
fmt.Println(s[6:])  // world
Такой срез не копирует данные, он ссылается на тот же массив байт. Работает за O(1). Теперь то же самое с кириллицей:
s := "привет"
fmt.Println(s[0:3]) // "пр"? нет, это "п" с хвостом
Каждая кириллическая буква в UTF-8 занимает два байта, так что s[0:3] разрежет символ пополам и вы получите битые данные. Срез по символам Если нужны именно символы, переводим строку в срез рун:
r := []rune("привет")
fmt.Println(string(r[0:3])) // при
Здесь уже есть аллокация и проход по всей строке, то есть O(n). Для длинных строк в горячем цикле это заметно. 🐸 Библиотека Go для собеса

❓Как узнать, сколько элементов лежит в канале Функция len(ch) возвращает количество элементов, которые сейчас лежат в буфере канала. Функция cap(ch) возвращает вместимость буфера, то есть сколько элементов канал может хранить, не блокируя отправителя. Пример с буферизированным каналом:
ch := make(chan int, 5)
ch <- 1
ch <- 2

fmt.Println(len(ch)) // 2
fmt.Println(cap(ch)) // 5
Для небуферизированного канала обе функции всегда дают ноль, потому что у него нет буфера, где данные могли бы задержаться.
ch := make(chan int)

fmt.Println(len(ch)) // 0
fmt.Println(cap(ch)) // 0
Значение len(ch) это снимок состояния на конкретный момент. Пока вы читаете результат и что-то с ним делаете, другая горутина уже могла записать или забрать элемент. 🐸 Библиотека Go для собеса

🔥 Как перестать слепо доверять ИИ и начать им управлять? 1–2 августа на «ИИ-выходных» вы научитесь проектировать архитектуру
🔥 Как перестать слепо доверять ИИ и начать им управлять? 1–2 августа на «ИИ-выходных» вы научитесь проектировать архитектуру автономных AI-агентов и контролировать их работу. Что будет в вашем портфолио после обучения: — Вы выйдете с готовым профилем AI-инженера (отлично дополнит ваши LinkedIn и GitHub). — Освоите связку Python, FastAPI, OpenAI API, Docker и PostgreSQL. — Соберете полноценный AI-сервис под вашу личную задачу. Для кого: junior-middle разработчики. Вы пишете на Python, работаете с Git и терминалом (с нуля не подойдет, темп очень быстрый!). 👨‍💻 Спикер: Алексей Жиряков (Сбер, GenAI). Места строго ограничены! 👉 Изучить программу и занять место

❓ Как узнать, что канал закрыт Обычное v := <-ch из закрытого канала не заблокируется и не паникует, а сразу вернёт нулевое значение типа. Поэтому по одному v нельзя отличить «пришёл реальный ноль» от «канал закрыт и пуст — ровно как с мапой:
v := <-ch // 0 — но это может быть и настоящее значение, и признак закрытия
Второй параметр решает вопрос: флаг ok равен true, пока из канала можно прочитать отправленное значение, и становится false, когда канал закрыт и в нём не осталось буферизованных данных. Важно, что закрытый канал сначала отдаёт всё, что в него успели записать, и только потом начинает возвращать false:
v, ok := <-ch
if !ok {
    // канал закрыт и пуст
}
На практике за приёмом из канала обычно стоит range, который сам корректно завершается при закрытии — отдельная проверка ok не нужна:
for v := range ch {
    // выполняется, пока канал не закрыт и не пустой
}

❓ Как отличить отсутствующий ключ от нулевого значения в мапе Через второй возвращаемый параметр при обращении к мапе — так называемый comma-ok. Обычное чтение v := m[key] всегда вернёт значение: если ключа нет, это будет нулевое значение типа (0 для чисел, "" для строк, nil для указателей). А значит, по одному только v нельзя понять, лежал ли там реальный ноль или ключа просто не было:
m := map[string]int{"a": 0}

v := m["a"] // 0 — но ключ есть
v = m["b"]  // 0 — а ключа нет
Форма с двумя переменными решает это: второе значение — булев флаг, который равен true, только если ключ действительно присутствует в мапе. Именно на него и нужно смотреть, а не на само значение:
v, ok := m["a"]
if ok {
    // ключ есть, v — реальное значение (даже если это 0)
}

_, ok = m["b"] // ok == false, ключа нет
Отсюда типичный идиоматичный паттерн — проверка и использование в одном if:
if v, ok := m[key]; ok {
    // работаем с v
}
🐸 Библиотека Go для собеса