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

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

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

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

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

#WXSSA
Download Telegram
Самый востребованный навык в ИТ в 2026-м — навык создания ИИ-агентов

Мы полностью переработали курс «Разработка AI-агентов» под реалии 2026 года. Никакой долгой теории — с самого начала пишем код. Обучать и делиться набитыми шишками будут эксперты-практики из Газпромбанка, Альфа-Банка и других бигтехов.

В программе:

— архитектура автономных систем с тестированием, ReAct-циклами и контролем токенов;
— практическая работа с актуальными фреймворками LangGraph, AutoGen, MCP и CrewAI;
— настройка продвинутого RAG для парсинга документов и точного поиска;
— внедрение решений с учётом действующего законодательства (152-ФЗ);
— дипломная работа, за основу которой можно взять свой рабочий проект или задачу, которую предложим мы.

Эксперты поделятся инсайтами из реального продакшна — тем, о чём вам никогда не расскажет ни одна нейросеть.

Запись первого открытого вебинара, на котором мы вместе с руководителем AI-направления в Альфа-Банке Полиной Полуниной пилили агента в прямом эфире.


Ах да, чуть не забыли! Дарим промокод AGENTSWEB на скидку 10 000 рублей и два курса сверху при покупке до 15 марта 🎁

Освоить разработку AI-агентов
🥱20😁5👍2
✏️ Вопрос, который отделяет Go-разработчиков от оффера

Сколько раз можно вызвать cancel() из context.WithCancel до паники?


Большинство мидлов отвечают уверенно — и ошибаются. Не потому что не знают контексты, а потому что никогда не проверяли это руками и не читали исходники пакета context.

ctx, cancel := context.WithCancel(context.Background())
cancel()
cancel() // что будет?
cancel() // а теперь?


А там есть один нюанс, который меняет ответ.

Проверить себя → в канале с вопросами с собесов

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

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

#ReadySetGo
Please open Telegram to view this post
VIEW IN TELEGRAM
5
👨‍💻 Инкапсуляция внутри пакета в Go

В Go инкапсуляция работает на уровне пакета. Всё с маленькой буквы видно только внутри пакета, всё с большой буквы доступно снаружи. Звучит просто, пока пакет небольшой.

Но когда пакет разрастается, внутри него появляется несколько файлов и сотни строк кода, и возникает вопрос: как ограничить доступ к конкретным структурам или функциям между файлами внутри одного пакета? В Java для этого есть private. В Go такого нет.

Почему это вообще проблема

В Go весь код внутри одного пакета видит всё, что объявлено в этом пакете, независимо от файла. Если у вас payment пакет с файлами processor.go, validator.go и refund.go, то любая функция из refund.go может обратиться к любому unexported-идентификатору из processor.go. Компилятор не запрещает, конвенций нет, всё держится на дисциплине команды.

Три подхода, которые используют в реальных проектах

• Разбить пакет на подпакеты

Первый и самый частый совет. Если логика достаточно самостоятельная, вынесите её в отдельный пакет. Тогда вы явно контролируете, что экспортировать, а что нет. Это идиоматичный Go.
payment/
processor.go
validator/
validator.go // видит только то, что экспортирует payment
refund/
refund.go


Минус: иногда код слишком связан, чтобы разбивать его на подпакеты без боли.

• Директория internal

Это не просто конвенция, это механизм компилятора. Пакет внутри internal можно импортировать только из пакетов, которые находятся в том же дереве директорий выше internal.
myapp/
payment/
processor.go
internal/
cardutils/
cardutils.go // доступен только внутри payment/


cardutils не получится импортировать из myapp/order или любого другого места вне myapp/payment. Компилятор выдаст ошибку. Это настоящая изоляция, которую не обойти дисциплиной.
// myapp/order/order.go
import "myapp/payment/internal/cardutils" // ошибка компиляции


• Интерфейсы для скрытия деталей

Если нужно скрыть конкретную реализацию от остальных частей пакета, можно работать через интерфейс. Другие части пакета видят только интерфейс, не конкретный тип.
// processor.go
type cardProcessor interface {
charge(amount int) error
}

// stripeProcessor.go
type stripeProcessor struct { ... }

func (s *stripeProcessor) charge(amount int) error { ... }


Это не запрещает доступ технически, но снижает связность. Команда знает, что работать нужно через интерфейс.

Go не даёт private внутри пакета намеренно. Граница инкапсуляции здесь — это пакет, а не файл или структура. Если вы чувствуете, что пакет стал слишком большим и внутри него нужны границы, это сигнал его разбить, а не попытка заставить Go работать как Java.

internal-директория закрывает вопрос на уровне модуля. Подпакеты закрывают вопрос на уровне логики. Остальное — это архитектура и договорённости в команде.

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

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

#GoDeep
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🤔21
👨‍💻 Цепочка append в слайсах

Если в часто вызываемом коде есть цепочка append, то скорее всего там скрытая лестница аллокаций.

// аллокация #1
defaultFields := []zap.Field{...}
// аллокация #2 — вместимость кончилась
defaultFields = append(defaultFields, ...)
// аллокация #3 — финальное слияние
Log.Info("...", append(defaultFields, ...)...)


Каждый раз когда append упирается в capacity, Go выделяет новый массив, копирует данные, а старый уходит сборщику мусора. В логгере, который вызывается на каждый запрос, это поток короткоживущего мусора.

Решение простое — посчитать финальный размер заранее и передать его в make:
allFields := make([]zap.Field, 0, 7+len(fields))
allFields = append(allFields, zap.Int("status_code", code))
// ... остальные поля ...
Log.Info("Response sent", allFields...)

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

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

#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱13👍52
🖼️ Дайджест недели

Короткая рабочая неделя закончилась. Собрали чуток новостей и материалов.

Go в Китае

Go занимает в Китае куда более заметное место, чем в остальном мире. В феврале 2026 года язык занял лишь 16-е место в индексе TIOBE глобально, но по данным Google Trends запросы по слову «golang» из Китая стабильно опережают любую другую страну.

DI контейнер для Go

По резюме не берут. Берут по рекомендации

PVS-Studio запускает бета-тест анализатора для Go

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

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

#GoLive
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
⚡️ Ретраи на Go

Если вы писали retry-логику руками, вы знаете, как это выглядит в итоге. Счётчик попыток, sleep с захардкоженной задержкой, иногда экспоненциальный backoff, скопированный со Stack Overflow.

Работает, пока не начинает ломаться под нагрузкой: все клиенты начинают повторять запросы одновременно и кладут и без того перегруженный сервис.

Resile — open-source библиотека для Go, которая реализует устойчивое выполнение операций в распределённых системах.

Самый простой случай — повторить операцию, которая возвращает ошибку:
err := resile.DoErr(ctx, func(ctx context.Context) error {
return db.PingContext(ctx)
})


По умолчанию библиотека сделает 5 попыток, начиная с 100ms, максимум 30s. Ничего настраивать не нужно, если дефолты подходят.

Если нужно вернуть значение — используете Do с дженериками:
user, err := resile.Do(ctx, func(ctx context.Context) (*User, error) {
return apiClient.GetUser(ctx, userID)
}, resile.WithMaxAttempts(3))

Библиотека даёт готовые решения для типичных проблем распределённых систем без лишних зависимостей.

➡️ Репозиторий

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

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

#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
6👍6🔥3🥱1
📎 Множество в Go

В Go нет встроенного множества. Это сознательное решение — язык не добавляет то, что можно сделать через уже существующие примитивы. И мапа справляется с задачей хорошо.

Два способа объявить множество

Первый — через map[string]bool. Читается проще, но каждый bool занимает 1 байт:
visited := make(map[string]bool)
visited["page1"] = true
visited["page2"] = true

if visited["page1"] {
fmt.Println("Already visited")
}


Второй — через map[string]struct{}. Выглядит непривычно, но struct{} занимает 0 байт в памяти:
seen := make(map[string]struct{})
seen["item1"] = struct{}{}
seen["item2"] = struct{}{}

if _, exists := seen["item1"]; exists {
fmt.Println("Already seen")
}


Операции над множествами

Все три классические операции реализуются через перебор мапы.

Объединение — добавляем всё из обоих множеств:
setA := map[int]struct{}{1: {}, 2: {}, 3: {}}
setB := map[int]struct{}{2: {}, 3: {}, 4: {}}

union := make(map[int]struct{})
for k := range setA { union[k] = struct{}{} }
for k := range setB { union[k] = struct{}{} }
// union: {1, 2, 3, 4}


Пересечение — только то, что есть в обоих:
intersection := make(map[int]struct{})
for k := range setA {
if _, ok := setB[k]; ok {
intersection[k] = struct{}{}
}
}
// intersection: {2, 3}


Разность — то, что есть в A, но нет в B:
difference := make(map[int]struct{})
for k := range setA {
if _, ok := setB[k]; !ok {
difference[k] = struct{}{}
}
}
// difference: {1}


Для большинства задач хватает обычного map. Добавлять зависимость ради множества скорее избыточно.

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

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

#GoDeep
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🥱3🤔2😁1
👨‍💻 Массовая вставка в Go

При массовой вставке данных в PostgreSQL из Go разработчики обычно выбирают между двумя подходами. COPY работает быстро, но не поддерживает ON CONFLICT. Многострочный INSERT поддерживает конфликты, но медленнее. Есть способ получить оба свойства одновременно.

Паттерн с промежуточной таблицей

Идея простая: сначала заливаем данные через COPY во временную таблицу, потом перекладываем их в основную с обработкой конфликтов.
-- 1. Создаём промежуточную таблицу
CREATE TEMP TABLE промежуточная (LIKE блоки INCLUDING ALL);

-- 2. Заливаем через COPY (быстро)
COPY промежуточная FROM ...;

-- 3. Перекладываем с обработкой конфликтов
INSERT INTO блоки
SELECT * FROM промежуточная
ON CONFLICT (хеш_блока) DO NOTHING;

-- 4. Чистим промежуточную таблицу
TRUNCATE промежуточная;


Два круговых обращения вместо одного, но для пакетов по 25 блоков накладные расходы незаметны. Пропускная способность остаётся близкой к чистому COPY.

Альтернатива через unnest

Если промежуточная таблица кажется избыточной, есть вариант через unnest — передаём массивы значений одним запросом:
_, err := db.Exec(`
INSERT INTO блоки (хеш, номер, метка_времени)
SELECT * FROM unnest($1::text[], $2::int[], $3::timestamptz[])
ON CONFLICT (хеш) DO NOTHING`,
pq.Array(хеши),
pq.Array(номера),
pq.Array(метки_времени),
)


Работает быстрее многострочного INSERT с отдельными параметрами и поддерживает ON CONFLICT. Хорошее среднее между скоростью и простотой.

Про lib/pq

Если используете lib/pq — стоит присмотреться к переходу на pgx. Библиотека активно поддерживается, имеет встроенную поддержку COPY через CopyFrom и в целом быстрее:
соединение, _ := pgx.Connect(ctx, строкаПодключения)
_, err := соединение.CopyFrom(
ctx,
pgx.Identifier{"блоки"},
[]string{"хеш", "номер", "метка_времени"},
pgx.CopyFromRows(строки),
)


Переход с lib/pq на pgx в большинстве проектов занимает пару часов.

Ограничение PostgreSQL

При многострочном INSERT с параметрами помните про ограничение PostgreSQL — максимум 65 535 параметров на запрос. Делите на количество столбцов и получаете максимальное число строк за один запрос. При 10 столбцах это не больше 6 553 строк за раз.

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

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

#GoDeep
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥203👍2
📅 Парсинг дат на человеческом языке

Когда пользователь вводит "5 minutes ago" или "next monday", стандартный time.Parse бесполезен — он ждёт строгий формат вроде 2006-01-02T15:04:05. Писать парсер для произвольных человекочитаемых дат самостоятельно долго и скучно. naturaldate.go решает именно это.

Что это такое

Библиотека для Go, которая превращает фразы на естественном языке в time.Time. Форк tj/go-naturaldate с дополнительными возможностями. Под капотом — PEG-грамматика через pointlander/peg.

Установка:
go get github.com/anatol/naturaldate.go


Как использовать

Основная функция — naturaldate.Parse. Принимает строку и опорное время, возвращает time.Time:
t, err := naturaldate.Parse("5 minutes ago", time.Now())
t, err := naturaldate.Parse("December 25th at 7:30am", time.Now())
t, err := naturaldate.Parse("last sunday at 5:30pm", time.Now())


Библиотека умеет игнорировать лишний текст вокруг даты, так что фраза "Restart the server in 5 days from now" отработает корректно — из неё достанется нужная часть.

Поддерживаемые форматы:
now, today, yesterday
5 minutes ago
three days ago
last month / next month
one year from now
yesterday at 10am
last sunday at 5:30pm
next January / last February
December 25th at 7:30am
10am / 10:05pm / 10:05:22pm
Remind me on the 25th of December at 7:30am
Message me in two weeks


Парсинг длительностей

Отдельная функция ParseDuration возвращает time.Duration из человекочитаемой строки:
d, err := naturaldate.ParseDuration("1 year and 2 months", time.Now())
fmt.Println(d) // 10248h0m0s

// с указанием направления — в прошлое
p, err := naturaldate.ParseDuration("1 year and 2 months", time.Now(),
naturaldate.WithDirection(naturaldate.Past))
fmt.Println(p) // -10224h0m0s


Направление для неоднозначных выражений

Фраза «sunday» без контекста может означать прошлое или будущее воскресенье. По умолчанию библиотека выбирает прошлое. Это меняется через опцию:
// "sunday" = "next sunday"
t, err := naturaldate.Parse("sunday", time.Now(),
naturaldate.WithDirection(naturaldate.Future))


Где пригодится

CLI-утилиты с фильтрацией по времени, поисковые интерфейсы, планировщики задач, любой интерфейс где пользователь вводит дату текстом. Библиотека изначально создавалась для поиска по логам в Apex Logs.

➡️ Репозиторий

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

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

#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
13👍6
У «Библиотеки программиста» появился резервный канал в мессенджере MAX

Он нужен исключительно для связи с теми, кто не может следить за обновлениями здесь из-за трудностей с доступом. Поэтому, если вы видите это сообщение, распространите его среди жильцов вашего ЖЭКа.

Контент в MAX будет дублировать телеграмный — основной нашей площадкой был и остаётся Telegram. Надеемся, это временная мера.

Подписаться на «Библиотеку программиста» в MAX
😢33🌚16🔥3🥱2👾1
Кажется, мы окончательно перешли от игрушек к суровому AgentOps

Приглашаем на наш обновлённый курс по разработке ИИ-агентов. Никакой воды про «будущее нейросетей», только инженерный подход.

На курсе мы:

— пошагово строим готовые системы на LangGraph, CrewAI и MCP;
— настраиваем кэширование и роутинг, чтобы бот не сожрал токены;
— разбираемся со стейтом, учимся дебажить через time-travel и прикручиваем human-in-the-loop;
— выводим RAG в прод так, чтобы безопасники не завернули архитектуру из-за 152-ФЗ.

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

Кстати, на днях мы пилили агента в прямом эфире, если пропустили — есть запись вебинара.


Сегодня последний день, когда можно забрать курс по старым ценам. Базовый тариф сейчас стоит 49 000 ₽ (вместо 62 990 ₽), продвинутый трек — 99 000 ₽ (вместо 124 990 ₽). Если не хочется отдавать всю сумму сразу, есть рассрочка. Торопитесь — на потоке осталось всего 5 мест!

Зафиксировать цену и перейти к сборке своих агентов
😁4
👋 Тимлид сказал «пойдёшь на бенч»

Одни слова в IT звучат невинно, но означают что-то неприятное. Другие — пугают с виду, но на деле просто описывают рабочий процесс. Разбираться в карьерном сленге важно не меньше, чем в технологиях.

Гостинг, квитинг, джоб-хоппинг и синдром тревожной пятницы — всё в новой статье.

👉 Разобраться в терминах

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

🐸 Библиотека Go-разработчика
Please open Telegram to view this post
VIEW IN TELEGRAM
3👍1
❤️ Топ-вакансий для Go-разработчиков за неделю

Senior Backend Engineer (Golang) — от 450 000 ₽, удаленно в Москве

Senior Go — от 328 200 ₽, удаленно

Разработчик WebRTC-сервисов на Go — от 250 000 до 500 000 ₽, офис/гибрид в Москва или в Санкт-Петербурге

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

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

#GoWork
Please open Telegram to view this post
VIEW IN TELEGRAM
2
⚡️ Wire стал быстрее в 70 раз. Как это стало возможным

Если вы работаете с Go и используете Wire для генерации зависимостей, то наверняка знаете это чувство: запускаете wire gen, ждёте секунду-две, и так сотни раз за день. Мелочь? Нет. Это накапливается.

Go-Разработчик решил разобраться с этим и результат получился серьёзный.

В чём была проблема

Предыдущие попытки ускорить Wire давали 8-10x на отдельных сценариях, но не решали главное. На каждом запуске Wire заново делал одно и то же: полный обход графа зависимостей через go/packages и полную проверку типов через go/types.

Для большого проекта это 300-500 мс только на обнаружение зависимостей, и это при каждом запуске, даже если вы поменяли одну строчку в теле функции.

На крупных кодовых базах Wire легко уходил в 1-3 секунды на каждый прогон. Умножьте это на тысячи запусков за время разработки.

Что изменилось

Загрузчик пакетов был переписан с нуля. Теперь обнаружение зависимостей и проверка типов разделены на два независимых этапа. Оба кэшируются. При следующем запуске Wire смотрит, что именно изменилось, и переделывает только то, что реально затронуто правкой.

Результат по сценариям для проекта с 42 локальными, 243 стандартными и 342 внешними пакетами в графике к посту.

Холодный старт стал медленнее — это цена построения кэша. Но в реальной разработке холодный старт случается один раз, а повторные запуски идут в 14-74 раза быстрее.

Все существующие тесты Wire проходят. Добавлены новые тесты под конкретные сценарии кэширования. При любом подозрительном состоянии кэша Wire откатывается к полному пересчёту. Автор использует это в своей работе уже несколько недель.

Как попробовать

Установить тестовую сборку можно одной командой:
go install github.com/goforj/wire/cmd/wire@cf52879


Чтобы понять размер своей кодовой базы перед тестом:
go list -deps -json ./... | \
jq -r '
select(.ImportPath != null) |
if .Standard then "stdlib"
elif (.Module != null and .Module.Main == true) then "local"
else "external"
end
' | sort | uniq -c


Если вы уже используете Wire и у вас достаточно большой проект, именно ваши тесты сейчас нужны. Разработчик просит активно делиться результатами.

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

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

#GoLive
Please open Telegram to view this post
VIEW IN TELEGRAM
👍87🔥2
🔍 Критическая дыра в gRPC-Go: обход авторизации

gRPC-Go сервер слишком мягко обрабатывал маршрутизацию: он принимал запросы, где в заголовке :path отсутствовал обязательный ведущий слэш — например, Service/Method вместо /Service/Method.

Сервер успешно направлял такие запросы нужному обработчику, но перехватчики авторизации, включая официальный пакет grpc/authz, проверяли сырую, неканоническую строку пути.

Кто под угрозой

Уязвимость затрагивает серверы, у которых одновременно:

• используются перехватчики авторизации по пути (grpc/authz, кастомные через info.FullMethod)

• политика содержит явные правила запрета + резервное «разрешить»

Атакующему достаточно отправить сырые HTTP/2-фреймы с кривым заголовком :path — без привилегий и взаимодействия с пользователем.

Лучшее решение — обновиться до v1.79.3, где запросы без ведущего слэша сразу отклоняются с ошибкой codes.Unimplemented.

➡️ Источник

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

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

#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍53🤩3🔥2😁1
📎 Когда каналов недостаточно

Когда нужно скоординировать горутины, первый инстинкт — взять канал. Чаще всего это правильно. Но есть один конкретный сценарий, где sync.Cond не просто уместен, а является единственным чистым решением.

Проблема: оповестить произвольное число горутин

Допустим, у вас пул воркеров, которые ждут задачи в очереди. Воркеров может быть 3, может быть 50 — вы не знаете заранее.

Как оповестить их всех, когда появилась новая задача?

С каналом это неловко: нужно либо N отдельных каналов, либо закрыть канал, но это одноразово, либо делать fan-out горутину. Всё это хрупко, когда число потребителей меняется динамически.

sync.Cond решает это в одну строку: cond.Broadcast().

Пример кода:
type Queue struct {
mu sync.Mutex
cond *sync.Cond
items []Job
}

func (q *Queue) Push(j Job) {
q.mu.Lock()
q.items = append(q.items, j)
q.cond.Broadcast() // будим всех ожидающих
q.mu.Unlock()
}

func (q *Queue) Pop() Job {
q.mu.Lock()
defer q.mu.Unlock()
for len(q.items) == 0 {
q.cond.Wait() // атомарно отпускает мьютекс и засыпает
}
j := q.items[0]
q.items = q.items[1:]
return j
}


cond.Wait() делает две вещи атомарно: отпускает мьютекс и засыпает. Когда горутина просыпается мьютекс снова захватывается автоматически. Без этой атомарности между отпустил лок и заснул было бы гоночное окно.

Broadcast или Signal: Broadcast() будит всех ожидающих, а Signal() ровно одного. Используйте Broadcast, когда изменение состояния важно многим; Signal — когда продвинуться может только один.

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

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

#GoDeep
Please open Telegram to view this post
VIEW IN TELEGRAM
11👍9😢1
Почитали тут свежий отчёт по рынку ИИ-ускорителей в РФ: оказывается, 54% компаний тормозят внедрение ИИ исключительно из-за конских цен на инфраструктуру.

Ну, то есть написать пет-проект с вызовом API это задача на вечер, а вот запустить агента в продакшн так, чтобы он не сжёг бюджет отдела за неделю — суровая инженерия.

По сути, сейчас мало уметь собирать RAG. Нужно считать токены, настраивать time-travel дебаг в LangGraph и уметь роутить запросы на лету. Всё это мы учли в обновлённом курсе по разработке AI-агентов, где акцент сделан именно на AgentOps и жёсткий контроль ресурсов.

Также в программе:

— оценка качества, трейсинг и защита от деградации пайплайнов;
— мультиагентные паттерны и интеграция по протоколу MCP;
— локальный деплой Open Source под 152-ФЗ (когда данные нельзя выносить наружу).

Кажется, это единственный адекватный roadmap по переходу от блокнотов к enterprise-решениям.

Прямо сейчас можно урвать курс с увесистой скидкой (49 000 ₽ 62 990 ₽ за базовый тариф и 99 000 ₽ 124 990 ₽ за продвинутый трек), но стоит поторопиться — на потоке осталось всего 5 мест.

👉 Зафиксировать цену и начать собирать агентов, за которых не стыдно в проде
🥱15👍21🤔1
💻 Свой Git-сервер, который запускается на Raspberry Pi

Gogs — self-hosted Git-сервис, написанный на Go. Это веб-интерфейс для работы с репозиториями, который разворачивается у вас на сервере.

По функциям похож на GitHub: репозитории, пул-реквесты, задачи, вики, вебхуки, деплой-ключи, организации, защищённые ветки.

Из необычного: поддержка Jupyter Notebook и PDF прямо в браузере, миграция репозиториев с других хостингов, Git LFS, аутентификация через LDAP, SMTP, GitHub и reverse proxy с поддержкой 2FA.

Требования к железу минимальные: хватит Raspberry Pi или VPS за $5. Для командной работы рекомендуется 2 ядра и 512 МБ ОЗУ. ОЗУ при росте команды почти не растёт, только процессор.

➡️ Репозиторий

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

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

#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
6🔥4😁2
Что выведет код

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

💡 Подсказка: вспомните, как работает cmp.Or под капотом. Это не || из си-подобных языков

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

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

#ReadySetGo
Please open Telegram to view this post
VIEW IN TELEGRAM
1
⚙️ sync.Pool это не кэш

sync.Pool это один из самых неправильно понимаемых типов в стандартной библиотеке. Выглядит как пул объектов, им и является, но с одной гарантией, которая удивляет: GC может выселить всё содержимое пула в любой момент, без предупреждения.

Для чего он реально нужен

Один конкретный сценарий: снизить давление аллокаций для короткоживущих переиспользуемых объектов: bytes.Buffer, структуры протокольных сообщений, временные срезы.

var bufPool = sync.Pool{
New: func() any {
return new(bytes.Buffer)
},
}

func processRequest(data []byte) []byte {
buf := bufPool.Get().(*bytes.Buffer)
buf.Reset() // обязательно: сбрасываем перед использованием
defer bufPool.Put(buf)

buf.Write(data)
return buf.Bytes()
}


Две ошибки, которые случаются почти всегда

Забыть Reset(). Пул переиспользует объект, но не его содержимое. Всё, что записал предыдущий пользователь, никуда не делось. Всегда сбрасывайте состояние перед работой с полученным объектом.

Использовать объект после Put. Как только вызвали Put — объект принадлежит пулу. Обращение к buf.Bytes() после возврата — это гонка данных, которая просто ещё не выстрелила.

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

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

#GoDeep
Please open Telegram to view this post
VIEW IN TELEGRAM
5🙏4👍2