uz
Feedback
Библиотека Go-разработчика | Golang

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

Kanalga Telegram’da o‘tish

Все самое полезное для Go-разработчика в одном канале. Учиться у нас: clc.to/qaSdww По рекламе: @proglib_adv Для обратной связи: @proglibrary_feeedback_bot РКН: https://gosuslugi.ru/snet/67a4a8c24689c2151c752af0 #WXSSA

Ko'proq ko'rsatish

📈 Telegram kanali Библиотека Go-разработчика | Golang analitikasi

Библиотека Go-разработчика | Golang (@goproglib) Rus til segmentidagi kanali faol ishtirokchi. Hozirda hamjamiyat 23 944 obunachidan iborat bo'lib, Texnologiyalar & Aralashmalar toifasida 5 620-o'rinni va Rossiya mintaqasida 27 758-o'rinni egallagan.

📊 Auditoriya ko‘rsatkichlari va dinamika

невідомо sanasidan buyon loyiha tez o‘sib, 23 944 obunachiga ega bo‘ldi.

28 Iyun, 2026 dagi oxirgi ma’lumotlarga ko‘ra kanal barqaror faollikka ega. Oxirgi 30 kunda obunachilar soni -86 ga, so‘nggi 24 soatda esa -4 ga o‘zgardi va umumiy qamrov yuqori darajada qolmoqda.

  • Tasdiqlash holati: Tasdiqlanmagan
  • Jalb etish (ER): Auditoriya o‘rtacha 11.88% darajada jalb etiladi. Nashrdan keyingi dastlabki 24 soatda kontent odatda umumiy obunachilar sonining 7.58% ini tashkil etuvchi reaksiyalarni to‘playdi.
  • Post qamrovi: Har bir post o‘rtacha 2 844 marta ko‘riladi; birinchi sutkada odatda 1 816 ta ko‘rish yig‘iladi.
  • Reaksiyalar va o‘zaro ta’sir: Auditoriya faol: har bir postga o‘rtacha 10 ta reaksiya keladi.
  • Tematik yo‘nalishlar: Kontent навигация, лучшее_из_библиотеки_2025, git, string, golive kabi asosiy mavzularga jamlangan.

📝 Tavsif va kontent siyosati

Muallif resursni shaxsiy fikrni ifoda etish maydoni sifatida ta’riflaydi:
Все самое полезное для Go-разработчика в одном канале. Учиться у нас: clc.to/qaSdww По рекламе: @proglib_adv Для обратной связи: @proglibrary_feeedback_bot РКН: https://gosuslugi.ru/snet/67a4a8c24689c2151c752af0 #WXSSA

Yuqori yangilanish chastotasi (oxirgi ma’lumot 29 Iyun, 2026 da olingan) sababli kanal doimo dolzarb va katta qamrovli bo‘lib qoladi. Analitika auditoriya kontent bilan faol hamkorlik qilishini, uni Texnologiyalar & Aralashmalar toifasidagi muhim ta’sir nuqtasiga aylantirishini ko‘rsatadi.

23 944
Obunachilar
-424 soatlar
-77 kunlar
-8630 kunlar
Postlar arxiv
🔍 LeetCode Daily: считаем подстроки за один проход Задача дня на LeetCode — Number of Strings That Appear as Substrings in W
🔍 LeetCode Daily: считаем подстроки за один проход Задача дня на LeetCode — Number of Strings That Appear as Substrings in Word. Дан массив строк patterns и строка word. Нужно вернуть количество строк из patterns, которые входят в word как подстроки. Подстрока — это непрерывная последовательность символов внутри строки. Если patterns = ["a","abc","bc","d"] и word = "abc", ответ будет 3: входят "a", "abc" и "bc", а "d" нет. ➡️ Решение   В Go для этого есть готовый инструмент — strings.Contains. Проходим по каждому паттерну и проверяем вхождение. Если вошло, увеличиваем счётчик.
import "strings"
 
func numOfStrings(patterns []string, word string) int {
    count := 0
    for _, p := range patterns {
        if strings.Contains(word, p) {
            count++
        }
    }
    return count
}
➡️ Сложность   strings.Contains внутри использует алгоритм поиска подстроки. При длине word = n и pattern = m это O(n·m) в худшем случае. С учётом ограничений задачи в 100 символов это несущественно. ➡️ Решить 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека Go-разработчика #ReadySetGo

🤝 Уйти грамотно — это тоже навык В IT репутация передается быстрее, чем резюме. Бывший тимлид может ответить на звонок рекру
🤝 Уйти грамотно — это тоже навык   В IT репутация передается быстрее, чем резюме. Бывший тимлид может ответить на звонок рекрутера через год, бывший коллега — написать в личку вашему потенциальному работодателю. Один неаккуратный уход способен закрыть двери туда, где вы еще даже не пытались открыть. ➡️ Как уйти красиво 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека Go-разработчика

🧑‍💻 Сравнение двух Go-структур без хардкода полей. Часть 1, проблема Почти в каждом бэкенде есть скучная рутина. Вы тянете данные из внешнего источника, у вас уже лежит их копия в своей базе, и нужно обновить только те строки, которые реально изменились. Звучит просто, но на практике это превращается в стену почти одинакового кода сравнения, который никто не хочет трогать. В этой серии разберём приём на рефлексии, который убирает эту стену целиком. Начнём с того, откуда вообще берётся боль. Допустим, приходит событие «водитель принял заказ», и вы подтягиваете свежий профиль водителя из внешнего сервиса. Он отдаёт маленькую структуру на три поля:
type DriverDriver struct {
    Name       string  `json:"name"`
    Number     string  `json:"number"`
    ProfilePic *string `json:"profile_pic"`
}
А ваша модель базы выглядит куда менее мило, в ней около восьмидесяти колонок:
type User struct {
    ID         int64   `gorm:"column:id"`
    Name       string  `gorm:"column:name"`
    Email      string  `gorm:"column:email"`
    Number     string  `gorm:"column:number"`
    ProfilePic *string `gorm:"column:profile_pic"`
    // ... и ещё около восьмидесяти колонок
}
Перезаписывать всю строку нельзя. Нужно отправить в UPDATE только те колонки, которые отличаются, чтобы не затереть поля, о которых внешний сервис вообще ничего не знает. Первый вариант, который пишут все, выглядит так:
updates := map[string]any{}
if driver.Name != user.Name {
    updates["name"] = driver.Name
}
if driver.Number != user.Number {
    updates["number"] = user.Number // упс, опечатка, скопировали не ту сторону
}
if !ptrEqual(driver.ProfilePic, user.ProfilePic) {
    updates["profile_pic"] = driver.ProfilePic
}
Этот код работает, но плохо стареет. Каждое новое поле это ещё три строки. Указатели требуют отдельного хелпера. Кто-то по невнимательности пишет user.Number вместо driver.Number, и вы получаете баг, который всплывёт только когда водитель сменит телефон. Помножьте это на три разных DTO и три таблицы, и поддерживать такое становится больно. В следующей части посмотрим, как переписать сравнение так, чтобы оно вообще не упоминало ни одного имени поля. 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека Go-разработчика #GoDeep

🤩 Фаззинг находит то, что вы не догадались проверить У обычных тестов есть слепое пятно. Они проверяют случаи, которые вы придумали, а баги живут в тех, что не придумали. Покрытие в 90% говорит, сколько строк вы прогнали, но молчит о том, сколько форм входа упустили. Зелёный прогон означает лишь, что код отработал на ваших примерах, а не что устоит против всех. ➡️ Идея в одном сдвиге Вы пишете не пример, а свойство, которое обязано держаться всегда. Машина сама ищет вход, который его ломает. Вместо «на входе X жду Y» вы описываете инвариант:
func FuzzParse(f *testing.F) {
    f.Add("platform=5000") // сиды это ваши готовые кейсы
    f.Fuzz(func(t *testing.T, rule string) {
        team, limit, err := ParseBudgetRule(rule)
        if err != nil {
            return // ошибка на кривом входе это норма
        }
        // раз ошибки нет, результат обязан быть валидным
        if limit <= 0 || team == "" {
            t.Errorf("bad result without error for %q", rule)
        }
    })
}
Частое заблуждение, что фаззер сыплет случайные байты. Современные фаззеры coverage‑guided. Стартуют с ваших сидов и мутируют их, отслеживая, какие мутации открывают новые ветки. Попал в новый путь, копает оттуда. Поэтому к багам он сходится в разы быстрее слепого рандома. Юнит‑тесты проверяют дороги, которые вы построили, фаззер ищет те, о которых вы не знали. ❓ Что ловит Класс ошибок на стыке кода и реальности. Паника на входе, которого «не бывает».
parts := strings.SplitN(rule, "=", 2)
limit, _ := strconv.Atoi(parts[1]) // "platform" без = и parts[1] не существует
// panic: index out of range [1] with length 1
Нарушения инвариантов, когда функция вернула мусор без ошибки, что хуже явной паники. Расхождения round‑trip, когда decode(encode(x)) не равно x. Их объединяет одно. Все появляются, когда снаружи приходит то, чего код не ждал. Обычные тесты это скипают, потому что их пишут под ожидаемые сценарии, а вы рассуждаете как автор кода, который знает, как им пользоваться. Фаззеру эта рамка незнакома. Найденный падающий вход не исчезает. Go сам кладёт минимальный контрпример в testdata/fuzz/ и делает из него регрессию:
go test -fuzz=FuzzParse -fuzztime=30s   # ищем баг
go test ./...                           # корпус гоняется всегда, даже без -fuzz
Один раз пойманный баг вернуться уже не может. ➡️ Куда прикладывать Туда, где код разбирает или валидирует внешний вход. Парсеры, декодеры, заголовки, query, конфиги, всё, что ветвится по содержимому. Чем больше условной логики, тем больше путей фаззеру. Не стоит фаззить чистую бизнес‑логику без зависимости от входа и функции, которые ходят в сеть или базу, их фаззер дёрнет тысячи раз. Правило короткое. Зависит поведение от того, что прислал внешний мир, заведите фаззинг. ❓ Почему окупается Барьер почти нулевой. В Go фаззер встроен в тулчейн с версии 1.18, свойство пишется в считаные строки. Цена это пара минут на разработке. Альтернатива это ждать, пока вход за вас подберёт прод. Боевыми данными, под нагрузкой, в три часа ночи, в виде алерта. Прод пофаззит ваш код в любом случае, вопрос лишь в том, узнаете вы о баге от теста или от дежурного. 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека Go-разработчика #GoDeep

📎 Главная проблема в микросервисах Сервис стартует чистым. Через полгода его трогать никто не хочет. Багов нет, тесты проходят, но любое изменение задевает пять файлов, а новому инженеру три дня объясняют, что где лежит. Проблема не в алгоритме и не в кэше, а в структуре. Команды копируют раскладку папок, не понимая контракт, который эта раскладка должна навязывать. Папки вместо архитектуры Спросите десять команд про структуру сервиса, и почти все опишут что‑то такое.
/cmd
/internal
  /handlers
  /services
  /repository
  /models
/pkg
Выглядит разумно и проходит ревью. Беда в том, что папки создают иллюзию границ, но не держат их. Ничто не мешает хендлеру импортировать репозиторий напрямую. Ничто не мешает модели обрасти бизнес‑логикой. Границы косметические, и код деградирует предсказуемо. Логика переползает туда, где удобнее, обычно в хендлеры, потому что там контекст. Типы репозитория протекают в API, потому что заводить отдельный DTO лень. Пакет models превращается в общий мешок структур, от которого зависят все, и поменять в нём что‑либо без каскада уже нельзя. Получается слоёная на вид архитектура, ведущая себя как монолит с накладными расходами на HTTP. В чём контракт слоёв Контракт про направление зависимостей. Они текут внутрь. Внешний слой (HTTP, gRPC, CLI) знает про слой приложения. Слой приложения знает про домен. Домен не знает ни про что. На практике это значит вот что. Хендлер принимает запрос, зовёт метод сервиса и сериализует ответ. Никакого SQL и никаких бизнес‑правил в нём нет. Сервис кодирует, что приложение делает. Он оркестрирует, валидирует, делегирует репозиторию и ничего не знает про HTTP. Репозиторий говорит с базой и возвращает доменные типы или ошибки, но не pgx.Rows. Доменные типы несут инварианты. Структура Member знает, что делает участника валидным, но не знает, как сериализовать себя в JSON. Когда контракт держится, слои тестируются независимо, реализацию репозитория можно подменить, не трогая хендлер, а gRPC добавить без дублирования логики. Анти‑паттерн god service Чаще всего контракт ломает god service. Сервис‑структура, которая всасывает всё:
type MemberService struct {
    db *pgxpool.Pool
}

func (s *MemberService) CreateMember(ctx context.Context, req CreateMemberRequest) (*Member, error) {
    // валидация, хеш, вставка строки, welcome‑письмо, событие в Kafka
}
Через полгода у MemberService сорок методов, пул базы, почтовый клиент, продюсер Kafka, клиент S3 и конфиг. Любой тест требует поднять все зависимости, даже когда проверяешь одну ветку валидации. Это уже не слой, а ящик для хлама. И дробить его на MemberCreationService и MemberUpdateService бесполезно, получите ящики поменьше. Лечит инверсия зависимостей Интерфейсы в Go удовлетворяются неявно, и это недоиспользуют. Вместо *pgxpool.Pool опишите то, что сервису реально нужно:
type MemberRepository interface {
    CreateMember(ctx context.Context, params CreateMemberParams) (*Member, error)
    GetMemberByID(ctx context.Context, id uuid.UUID) (*Member, error)
}

type EventPublisher interface {
    Publish(ctx context.Context, event DomainEvent) error
}

type MemberService struct {
    repo      MemberRepository
    publisher EventPublisher
}
Теперь сервис ничего не знает про PostgreSQL и Kafka. Он зависит от поведения, а не от реализации. Чтобы протестировать метод, достаточно мока под интерфейс, без базы, брокера и сети. Про pgx знает реализация репозитория, про franz-go знает реализация publisher, а сервис не знает ни про то, ни про другое. В этом и есть контракт. 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека Go-разработчика #GoDeep

🎮 Субботний оффтоп Кто-то собрал веб-версию Half-Life 2 и запустил её без установки и скачивания. Уровни и ресурсы грузятся
🎮 Субботний оффтоп Кто-то собрал веб-версию Half-Life 2 и запустил её без установки и скачивания. Уровни и ресурсы грузятся быстро, игра работает бодро. У многих сразу включается русский язык — Сити-17 и Рейвенхольм уже вовсю тестируют первые игроки. ➡️ Поиграть 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека Go-разработчика #GoLive

👨‍💻 Контекст неизменяемый, а память нет Правило знают все. context.WithValue ничего не меняет, а создаёт новую обёртку, поэтому контекст безопасно шарить между горутинами. Учебники забывают сказать главное. Иммутабельны обёртки, а объект в корне цепочки может переиспользоваться фреймворком через sync.Pool. Тогда горутина, пережившая запрос, держит указатель на память, которая вот‑вот достанется другому запросу. Хендлер запускает фоновую задачу и сразу возвращает ответ. Линтер ругается на отмену контекста, и со времён Go 1.21 его успокаивают одной строкой:
go s.processAsync(context.WithoutCancel(ctx), id)
Для net/http это правильно. Там контекст это обычная аллокация в куче. Но линтер слеп к архитектуре и не знает, что под context.Context может стоять пулящаяся структура. ❓ Где можно споткнуться В Fiber и всём на fasthttp c.Context() возвращает *fasthttp.RequestCtx. Пулящийся объект и есть ваш контекст, и после отправки ответа fasthttp сбрасывает его и кладёт обратно в пул. Gin пулит *gin.Context, поэтому передавать его в горутину нельзя (для снимка есть c.Copy()), а c.Request.Context() безопасен. Echo свой контекст пулит, но context.Context он не реализует, так что задеть гонку сложнее. Дальше воркер просыпается после I/O, зовёт ctx.Value("user_id") и читает память, которую перезаписывают данными другого пользователя. И вот что коварно. WithoutCancel снимает только отмену, а Value() по‑прежнему делегирует пулящемуся родителю. То есть фикс, который позеленил линтер, ровно и удерживает протухший указатель. ➡️ Как правильно Если задаче ничего не нужно из запроса, постройте свежий корень:
ctx := context.Background()
Если нужны trace‑id или user‑id, вытащите их как простые значения, пока горутина запроса ещё жива, и положите в свежий контекст:
userID := c.Locals("user_id").(string)
traceID := c.Locals("trace_id").(string)
go func(userID, traceID string) {
    ctx := context.WithValue(context.Background(), userCtxKey, userID)
    ctx = context.WithValue(ctx, traceCtxKey, traceID)
    h.svc.ProcessAsync(ctx, userID)
}(userID, traceID)
Вся безопасность держится на том, что значения сняты до возврата из хендлера. Логику стоит вынести в хелпер вроде ctxutil.Detach(ctx). Из штатных лазеек помогают c.Copy() у Gin и c.UserContext() у Fiber. Линтер видит синтаксис, но не жизненный цикл памяти. Если горутина переживает запрос, не давайте ей контекст запроса ни в какой обёртке. Снимайте значения как простые данные, стройте свежий корень и держите -race в CI. 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека Go-разработчика #GoDeep

🎬 Где ломаются архитектуры ИИ-агентов и как этого избежать: запись урока от Proglib.Academy и cloud․ru Proglib.аcademy вмест
🎬 Где ломаются архитектуры ИИ-агентов и как этого избежать: запись урока от Proglib.Academy и cloud․ru Proglib.аcademy вместе с cloud․ru провели вебинар, где разобрали реальные боли проектирования автономных систем. Вы просили запись встречи — она уже в открытом доступе! Что внутри: — критерии выбора между одним агентом и мультиагентной системой; — разбор популярных архитектурных ошибок; — реальные ограничения современных ИИ-агентов; — практические рекомендации по проектированию агентных систем. 👉 Посмотреть запись можно тут: VKYouTube

🐠 Открытый фреймворк для учебного фишинга Gophish это опенсорсный инструмент для проведения фишинговых учений и обучения сот
🐠 Открытый фреймворк для учебного фишинга Gophish это опенсорсный инструмент для проведения фишинговых учений и обучения сотрудников. Он рассчитан на бизнес и пентестеров, которым нужно легально проверить, как команда реагирует на поддельные письма. Проект написан на Go. Запускать такие кампании можно только против своей организации или там, где у вас есть письменное разрешение. Без него это уже не учения. Вы описываете кампанию в веб-интерфейсе, а инструмент сам рассылает письма, поднимает посадочные страницы, отслеживает открытия и переходы и собирает всё в отчёты. Вся функциональность доступна и через REST API, поэтому кампании можно гонять из скриптов и встраивать в свои пайплайны отчётности. ➡️ Как запустить Самый быстрый путь это готовый бинарь. Под Windows, macOS и Linux есть релизы, их достаточно скачать, распаковать и запустить. Или собрать самостоятельно:
git clone https://github.com/gophish/gophish.git
cd gophish
go build
./gophish
Есть и официальный Docker образ, если не хотите ставить ничего на хост:
docker run --name gophish -p 3333:3333 -p 8080:8080 gophish/gophish
После старта админка поднимается на https://localhost:3333. Логин и пароль не зашиты заранее, а печатаются в лог при первом запуске. Выглядит это примерно так:
level=info msg="Please login with the username admin and the password 4304d5255378177d"
Gophish превращает разрозненную ручную работу по фишинговым учениям в управляемый процесс с нормальной статистикой. ➡️ Репозиторий 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека Go-разработчика #GoToProduction

🤔 Функция с пустым телом — что вернёт компилятор? Вот простой код:
func foo() {}
Тело есть. Но оно пустое. Функция ничего не делает, ничего не возвращает. Go — язык строгий. Он не любит неиспользуемые переменные и лишний импорт. Но как он относится к функции, которая существует и при этом ничего не делает? Подсказка: вспомните, как Go обрабатывает неиспользуемый код на уровне функций, а не переменных. ➡️ Правильный ответ 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека Go-разработчика #ReadySetGo

🤖 Запускаем новый курс: «Spec-Driven Development»! Всем надоело, что ИИ-агенты (Cursor, Claude) генерируют сотни строк хаоти
🤖 Запускаем новый курс: «Spec-Driven Development»! Всем надоело, что ИИ-агенты (Cursor, Claude) генерируют сотни строк хаотичного кода, который приходится переписывать. Дело не в нейросети, а в том, что вы заставляете её угадывать условия задачи. На курсе вы освоите методологию SDD (Spec-Driven Development) — управление ИИ через спецификации (спеки) и тесты вместо «вайб-кодинга». Как это работает? До генерации кода вы фиксируете в спеке контракты, инварианты и граничные случаи. Документ четко определяет ЧТО делать, сводя лотерею в PR к нулю. За 8 недель с экспертами из BigTech вы: 🔹 Встроите ИИ в личный или командный workflow. 🔹 Превратите генерацию кода в предсказуемый инженерный процесс. 🔹 Перестанете перепроверять за моделями каждую строчку. 📖 Полный разбор методологии с примерами и готовый промпт для генерации спеки — в нашей статье 👉 Освоить SDD и ускорить разработку

💡 Предложение добавить slog.TestHandler в стандартную библиотеку Go В трекере Go появилось предложение добавить в пакет log/slog готовый хендлер, который пишет логи в testing.TB. Тогда логи приложения и логи теста окажутся в одном выводе и привяжутся к нужному тесту или подтесту. Зачем это Сопоставлять логи приложения с тестом, который их породил, приходится постоянно. Кирпичик для этого уже есть. Метод testing.TB.Output() добавили в Go 1.25 для T, B и F, а в Go 1.26 расширили на сам интерфейс TB. Но готового хендлера в slog нет, поэтому каждый проект собирает его заново. Обычно так:
logger := slog.New(slog.NewTextHandler(t.Output(), nil))
Этот вариант работает, но у него два минуса. Удобства тест ориентированного хендлера приходится дописывать руками, а наивная форма имеет реальную гонку при логировании после завершения теста. ➡️ Что предлагают сделать В log/slog появится тип TestHandler, реализующий интерфейс Handler, и конструктор к нему. Чтобы не тянуть testing внутрь slog и не словить циклический импорт, вводят узкий интерфейс TestLogger. Ему удовлетворяют *testing.T, *testing.B и *testing.F.
package slog

// TestLogger — подмножество testing.TB, нужное хендлеру.
type TestLogger interface {
    Output() io.Writer
}

// NewTestHandler возвращает Handler, который пишет записи в tb.Output().
// Вывод форматируется как у NewTextHandler. opts может быть nil.
func NewTestHandler(tb TestLogger, opts *HandlerOptions) *TestHandler

type TestHandler struct {
    tb TestLogger // куда писать
    // ...
}

func (h *TestHandler) Enabled(ctx context.Context, level Level) bool { ... }
func (h *TestHandler) Handle(ctx context.Context, record Record) error { ... }
func (h *TestHandler) WithAttrs(attrs []Attr) Handler { ... }
func (h *TestHandler) WithGroup(name string) Handler { ... }
На стороне теста всё сводится к одной строке:
func TestThing(t *testing.T) {
    logger := slog.New(slog.NewTestHandler(t, nil))

    // Логи лягут под TestThing с отступом от раннера.
    // Для прошедших тестов вывод скрыт, пока не передан флаг -v,
    // как у t.Log() и t.Logf().
    doWork(logger)
}
Пока это обсуждение, а не принятая фича. Следить за судьбой можно в самом issue #80138. 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека Go-разработчика #GoLive

⭐️ Фреймворк для микросервисов на Go Когда вы пишете микросервис на Go, заметная доля времени уходит не на бизнес логику, а на обвязку вокруг неё. Загрузка конфигов, логирование, трейсинг, метрики, подключение к базам, health чеки. Каждый новый сервис тащит этот набор заново, и в каждой команде он немного свой. GoFr берёт обвязку на себя. Это фреймворк, заточенный под микросервисы и деплой в Kubernetes. ❓ Как это выглядит Минимальное приложение умещается в десяток строк. Сначала ставите модуль:
go get -u gofr.dev/pkg/gofr
Дальше создаёте приложение и вешаете маршрут:
package main

import "gofr.dev/pkg/gofr"

func main() {
  app := gofr.New()

  app.GET("/greet", func(ctx *gofr.Context) (any, error) {
    return "Hello World!", nil
  })

  app.Run() // слушает на localhost:8000
}
После этого по адресу localhost:8000/greet уже отвечает рабочий сервис. Обработчик возвращает данные и ошибку, а сериализацию ответа по стандартам REST фреймворк делает сам. ➡️ Что внутри Главная фишка это наблюдаемость из коробки. Логи, трейсы и метрики собираются без ручной настройки, под капотом OpenTelemetry. Конфиги читаются из переменных окружения и файлов, так что код не привязан к окружению. Подключения к базам данных идут через объект ctx, и для каждого источника данных есть health чек. Фреймворк закрывает типичные потребности микросервиса: поддержка gRPC, HTTP клиент с circuit breaker, Pub/Sub, миграции базы, cron задачи, аутентификационный middleware и место под свой, смена уровня логирования без перезапуска, рендеринг Swagger, абстракция над файловыми системами, веб сокеты. GoFr подойдёт, если вы строите много однотипных сервисов и устали раз за разом писать одну и ту же инфраструктурную обвязку. Взамен вы принимаете правила фреймворка, потому что он именно опинионированный и навязывает свой способ делать вещи. Для разовой утилиты или нестандартной архитектуры это может оказаться лишним, а вот для парка микросервисов под Kubernetes экономия времени ощутимая. ➡️ Репозиторий 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека Go-разработчика #GoToProduction

👀 Умеете строить предсказуемую архитектуру с ИИ? Станьте спикером Proglib academy Мы в Proglib.academy запускаем курс по Spe
👀 Умеете строить предсказуемую архитектуру с ИИ? Станьте спикером Proglib academy Мы в Proglib.academy запускаем курс по Spec-Driven Development (SDD) — учим разработчиков управлять ИИ-агентами (Cursor, Copilot, Claude) через спецификации, контекст и тесты, чтобы не перепроверять за нейросетью каждую строку.
Для этого курса мы ищем классных спикеров-практиков.
➡️ Что требуется от вас?BigTech бэкграунд: опыт работы разработчиком, тимлидом или архитектором в крупных технологических компаниях. ● Опыт управляемой разработки с ИИ: вы на практике знаете, как встроить AI-инструменты в личный или командный workflow (от постановки задачи до ревью AI-кода). ● Системный подход: умение превращать хаотичную генерацию кода в предсказуемый инженерный процесс (работа с архитектурой, чек-листами, легаси и покрытием тестами). ➡️ Что мы предлагаем? ● Достойную оплату за подготовку материалов и проведение занятий. ● Мощное продвижение личного бренда через медиаресурсы Proglib (наша аудитория — 1 млн+ айтишников). ● Возможность публиковать свои экспертные материалы и статьи на наших площадках. ● Доступ к сильному и закрытому профессиональному сообществу. Также мы ищем консультанта программы. Если вы практикующий эксперт и готовы помочь нам отвалидировать программу курса, дать рекомендации по актуальности тем и финальным результатам обучения — мы вас очень ждем. ➡️ Как с нами связаться: Telegram: @alinaa_kh E-mail: alina@proglib.io

🔄 gcli v3.8.0, общие опции и генерация документации gcli это Go библиотека для консольных приложений. Она берёт на себя разбор команд и флагов, цвет в терминале, диалоги с пользователем, прогресс бары и генерацию автодополнения для bash и zsh. 🌻 Общие опции для подкоманд Раньше, если один и тот же флаг нужен нескольким подкомандам, его приходилось объявлять в каждой заново. В 3.8.0 появилась трёхслойная модель опций. Через Command.SharedOpts вы задаёте общие флаги один раз, и при разборе они подмешиваются в подкоманды. Слияние идемпотентно, локальная опция перекрывает общую, а проверка Required теперь смотрит на тип значения, а не просто на пустую строку. Рядом добавили примитив Parser.InheritOptsFrom для слияния наборов опций и аксессор CliOpt.TypeName() для доступа к имени типа. В выводе help унаследованные флаги теперь показываются отдельной группой Inherited Options, так что пользователю видно, откуда взялся флаг. 🌻 Генерация документации из команд Второе крупное изменение это пакет docgen. Из описания ваших команд можно собрать markdown через CmdMarkdown, AppMarkdown и MarkdownTree, либо man страницы через CmdMan и ManTree. Встроенная команда GenDoc экспортирует готовые md и man файлы без лишнего кода. Многострочные примеры в man теперь сохраняются как есть, а цветовые теги из примеров вычищаются перед записью. 🌻 Рефакторинг и фиксы Внутри вынесли AppOptions, и состояние разбора больше не висит на общем синглтоне для каждого приложения. Это убирает странности, когда в одном процессе крутится несколько приложений. Ещё поправили разворачивание анонимных встроенных структур неэкспортируемых типов в FromStruct. ➡️ Релиз 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека Go-разработчика #GoLive

💭 Топ-вакансий для Go-разработчиков за неделю Backend разработчик — от 450 000 ₽, удаленно. Руководитель группы разработки — офис в Москве. Go-разработчик — до 485 000 ₽, удаленно. Бустер — удалённо (не только Москва) ➡️ Еще больше топовых вакансий — в нашем канале Go jobs 🐸 Библиотека Go-разработчика #GoWork

Разрабатываете на Go больше четырёх лет? Сразу 7 наших команд ждут вас: Сервис Транспорта строит маршруты во всех продуктах 2
Разрабатываете на Go больше четырёх лет? Сразу 7 наших команд ждут вас:
Сервис Транспорта строит маршруты во всех продуктах 2ГИС, отображает пробки, дорожные события и автобусы на карте. Social сотворяет магию вокруг социального графа. Platform Backend Services превращает бизнес‑требования в платформенные сервисы: от идеи до запуска фич мобильными командами без лишних зависимостей. Web API решает все справочные задачи, управляет стилями карт и обратной связью от пользователей в продуктах. Сервис Рекламы создаёт техническую основу для продуктов рекламного направления. 2ГИС Логистика строит и пересчитывает маршруты с учётом пробок, погодных условий, типов транспорта, параметров груза и сложных логистических цепочек. ГеоПоток помогает бизнесу повышать прозрачность процессов и сокращать издержки.
Все вакансии на сайте Другие инженерные инсайты от 2ГИС → в Telegram-канале RnD

🤓 Паника уронила весь fan-out воркер История из боевых распределённых систем. Один кривой подписчик уронил доставку всем остальным, потому что паника в горутине без recover завершает весь процесс. Острая грань языка, про которую легко забыть. ➡️ Что случилось Был шлюз вебхуков, который параллельно рассылал события десяткам подписчиков. У одного оказался кривой URL, который в редком случае приводил к панике в HTTP-клиенте вместо ошибки. Раздача шла в голых горутинах без восстановления:
func fanOut(events []Event, subscribers []Subscriber) {
    var wg sync.WaitGroup
    for _, sub := range subscribers {
        wg.Add(1)
        go func(s Subscriber) {
            defer wg.Done()
            deliver(s, events) // паника здесь убивает весь процесс
        }(sub)
    }
    wg.Wait()
}
В Go паника в любой горутине, которую не перехватили через recover, завершает всю программу. Не важно, сколько других горутин в этот момент делают полезную работу. Про это легко забыть, потому что в туториалах паника обычно происходит в главной горутине, где последствия очевидны. Здесь же баг одного подписчика оборвал доставку всем. ➡️ Как починили Изоляция паники стала структурным паттерном, а не заплаткой по случаю. Появилась обёртка safeGo:
func safeGo(fn func()) {
    go func() {
        defer func() {
            if r := recover(); r != nil {
                log.Printf("recovered panic: %v\n%s", r, debug.Stack())
            }
        }()
        fn()
    }()
}
func fanOut(events []Event, subscribers []Subscriber) {
    var wg sync.WaitGroup
    for _, sub := range subscribers {
        wg.Add(1)
        safeGo(func() {
            defer wg.Done()
            deliver(sub, events)
        })
    }
    wg.Wait()
}
Теперь каждая долгоживущая или fan-out горутина проходит через обёртку вроде safeGo, и серверные gRPC-интерсепторы получили то же самое. Восстановление после паники переехало с верхнего HTTP-хендлера на каждую горутину, которую вы заводите сами. 📍 Навигация: ВакансииЗадачиСобесы 🐸 Библиотека Go-разработчика #GoToProduction