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

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

@goproglib

Все самое полезное для Go-разработчика в одном канале.

Учиться у нас: clc.to/qaSdww

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

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

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

Заявка: 8137752389
Subscribers
24.1K
Photos
2.8K
Videos
54
Links
5.4K

Showing posts older than #7144 · Back to latest

Older Posts 20 shown
Post #7143 3.62K
👩‍💻 git reset vs git revert: когда что использовать

Оба инструмента отменяют изменения, но делают это по-разному. Путаница между ними приводит к потере коммитов или конфликтам в общей ветке.

Что делает git reset

git reset перемещает указатель ветки назад. Он буквально переписывает историю коммитов — те коммиты, которые были после указанной точки, исчезают.

Три режима работы:

• git reset --soft HEAD~1 — отменяет коммит, но сохраняет изменения в индексе. Файлы готовы к новому коммиту.

• git reset --mixed HEAD~1 — отменяет коммит и убирает файлы из индекса. Изменения остаются в рабочей директории. Это режим по умолчанию.

• git reset --hard HEAD~1 — удаляет коммит и все изменения без возможности восстановления.

Пример: написали коммит с опечаткой в сообщении или забыли добавить файл.
git reset --soft HEAD~1
git add forgotten-file.js
git commit -m "правильное сообщение"


Что делает git revert

git revert создаёт новый коммит, который отменяет изменения из указанного коммита. История не переписывается — в ней появляется дополнительная запись об отмене.

git revert a1b2c3d


Git создаст коммит вида Revert "название вашего коммита". В нём будут обратные изменения — то, что было добавлено, удалится, и наоборот.

Если нужно отменить несколько коммитов подряд:
git revert HEAD~3..HEAD


В чём принципиальная разница

git reset меняет историю. Если вы уже отправили ветку на сервер и потом сделали reset, вам придётся делать git push --force. Это создаёт проблемы для всех, кто работает с этой веткой — их локальные копии окажутся не синхронизированы с удалённой.

git revert историю не трогает. Новый коммит просто добавляется поверх. Это безопасно для общих веток.

Как выбрать

Работаете в локальной ветке, которую ещё не пушили — используйте git reset. Он чище, не засоряет историю лишними коммитами.

Нужно отменить изменения в ветке, с которой работает команда — только git revert. Даже если очень хочется сделать reset и force push, это плохая идея.

Что делать, если всё сломалось

Если случайно сделали git reset --hard и потеряли коммиты — не паникуйте. Git хранит ссылки на все объекты в reflog ещё 30 дней.
git reflog
git reset --hard HEAD@{2}


reflog покажет историю всех перемещений HEAD. Найдите нужный коммит и восстановитесь на него.

git reset — для локальной правки истории до того, как она ушла на сервер. git revert — для безопасной отмены в общих ветках. Правило простое: если ветка уже запушена и её видят другие, используйте revert.

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

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

#GoToProduction
  • 👍 16
  • ❤ 2
  • 🥱 1
Post #7140 3.74K
😊 Топ-вакансий для Go-разработчиков за неделю

Golang-разработчик (eHealth) — до 200 000 ₽, удаленно по Москве

Middle+ Software Engineer — удаленно + помогут с переездом

Tech Lead / Senior Backend Developer (Golang) — до 4 000 $ , удалёнка вне РФ и РБ

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

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

#GoWork
Post #7139 3.99K
🌐 HTTP-клиент для Go с имитацией браузера

surf — продвинутый HTTP-клиент для Go. Он поддерживает имитацию браузерных отпечатков, HTTP/2 и HTTP/3, сессии, middleware, стриминг и многое другое.

Зачем это нужно

Стандартный net/http выдаёт себя по TLS-отпечатку. Большинство защищённых сайтов различают настоящий браузер и скрипт именно по нему. surf эмулирует не только заголовки, но и низкоуровневые параметры TLS-рукопожатия, порядок заголовков и настройки QUIC.

Как это работает

Простой GET-запрос выглядит так:
resp := surf.NewClient().Get("https://api.github.com/users/github").Do()
if resp.IsErr() {
log.Fatal(resp.Err())
}
fmt.Println(resp.Ok().Body.String().Unwrap())


Имитация Chrome с сессией:
client := surf.NewClient().
Builder().
Impersonate().Chrome().
Session().
Build().
Unwrap()

resp := client.Get("https://example.com").Do()


Firefox со случайной ОС:
client := surf.NewClient().
Builder().
Impersonate().
RandomOS().
Firefox().
Build().
Unwrap()


HTTP/3 с полным QUIC-отпечатком:
client := surf.NewClient().
Builder().
Impersonate().Chrome().
ForceHTTP3().
Build().
Unwrap()

resp := client.Get("https://cloudflare-quic.com/").Do()
fmt.Println(resp.Ok().Proto) // HTTP/3.0


Декодирование JSON-ответа:
type User struct {
Name string `json:"name"`
Company string `json:"company"`
}

resp := surf.NewClient().Get("https://api.github.com/users/github").Do()
if resp.IsOk() {
var user User
resp.Ok().Body.JSON(&user)
}


Middleware для логирования:
client := surf.NewClient().
Builder().
With(func(resp *surf.Response) error {
fmt.Printf("статус: %d, время: %v\n", resp.StatusCode, resp.Time)
return nil
}).
Build().
Unwrap()


Что ещё умеет

• поддерживает прокси SOCKS5 с UDP для HTTP/3, DNS-over-TLS
• привязку к сетевому интерфейсу
• Unix-сокеты
• кэширование тела ответа
• стриминг, Server-Sent Events

Клиент совместим со стандартным net/http через метод Std() — это позволяет использовать его с любыми сторонними библиотеками, ожидающими *http.Client.

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

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

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

#GoToProduction
  • ❤ 16
  • 👍 9
  • 👾 2
Post #7137 3.61K
🍵 Почтовый клиент прямо в терминале

Каждый раз, когда нужно проверить почту, вы переключаетесь в браузер, ждёте загрузки, отвечаете на письмо и возвращаетесь обратно. Если большую часть дня вы проводите в терминале, это раздражает.

matcha это TUI-клиент для работы с email, написанный на Go с использованием библиотеки Bubble Tea. Работает прямо в консоли, поддерживает несколько аккаунтов, вложения, черновики и даже отображение изображений в письмах, если терминал это позволяет.

Что умеет

Читать и отправлять письма, отвечать с цитированием, скачивать вложения, искать по входящим. Поддерживает Gmail, iCloud и любой кастомный IMAP/SMTP.

Если пишете письмо в Markdown клиент сам конвертирует его в HTML перед отправкой. Черновики сохраняются автоматически при выходе из редактора.

Навигация в стиле Vim: j/k для движения по списку, r для обновления входящих, / для поиска, d для удаления.

Настройка

При первом запуске клиент попросит указать email, пароль и провайдера. Конфиг хранится в ~/.config/matcha/config.json. Для Gmail и iCloud нужен app-specific password, обычный пароль от аккаунта не подойдёт.

Важно знать: пароли хранятся в конфиге в открытом виде. Авторы сами об этом предупреждают.

Если терминал это ваша основная рабочая среда, matcha позволяет не выходить из неё ради почты.

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

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

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

#GoToProduction
  • ❤ 4
  • 👍 2
Post #7136 3.76K
👨‍💻 Менеджер SSH-туннелей, который не мешает работать

Вы настроили SSH-туннель, отошли на час, вернулись, а соединение упало. Поднимаете снова вручную. Через день история повторяется. Если туннелей несколько, это превращается в рутину.

boring решает именно это. Он держит туннели живыми, переподключает их автоматически и управляет всем через один TOML-файл.

Что умеет

Поддерживает три режима проброса портов: локальный, удалённый и динамический через SOCKS5. Работает с ~/.ssh/config и ssh-agent, если хост уже описан там, достаточно указать его имя. Умеет работать с Unix-сокетами. Есть автодополнение для bash, zsh и fish.

Бинарник появится в папке dist. Его нужно переложить в любую директорию из $PATH.

Пример конфига с двумя туннелями:
# туннель к dev-серверу, хост берётся из SSH config
[[tunnels]]
name = "dev"
local = "9000"
remote = "localhost:9000"
host = "dev-server"

# туннель к prod с явными параметрами
[[tunnels]]
name = "prod"
local = "5001"
remote = "localhost:5001"
host = "prod.example.com"
user = "root"
identity = "~/.ssh/id_prod"


Каждый туннель описывается несколькими полями. Обязательные: name, local, remote, host. Остальное опционально, boring подтянет нужное из SSH config или использует значения по умолчанию.

Основные команды:
boring list          # показать все туннели
boring open dev # открыть туннель по имени
boring open -a # открыть все туннели сразу
boring close dev # закрыть туннель
boring edit # открыть файл конфигурации


Поддерживаются glob-паттерны. Можно открыть сразу несколько туннелей командой вида boring open prod*.

Keep-alive

По умолчанию boring отправляет keep-alive каждые 120 секунд. Значение можно переопределить глобально или на уровне конкретного туннеля:
keep_alive = 60  # в секундах

[[tunnels]]
name = "flaky-server"
keep_alive = 30
...


boring не требует демона, не тянет зависимостей и не лезет в системные настройки. Один бинарник, один TOML-файл и туннели просто работают в фоне.

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

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

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

#GoToProduction
  • 👍 8
  • ❤ 3
  • 👾 1
Post #7135 3.19K
🔄 oapi-codegen v2.7.0

Вышел релиз oapi-codegen v2.7.0. Это Go-инструмент, который генерирует серверный и клиентский код из OpenAPI-спецификаций. Версия исправляет проблемы с allOf/anyOf/oneOf, внешними $ref, перечислениями и обходом схем.

Три изменения, которые затронут существующий код

1. Strict-server с внешними $ref на ответы

Если strict-server использует внешний $ref на components/responses/... из другой спецификации, эта спецификация теперь тоже должна генерироваться с strict-server: true.

Добавьте в конфиг источника:
generate:
models: true
strict-server: true


2. Переименование анонимных схем

Анонимные схемы внутри oneOf, anyOf и additionalProperties теперь генерируются по единому шаблону:
GetPets_200_Data_Item            →  GetPets200JSONResponseBody_Data_Item
GetPets200JSONResponse_Data_Item → GetPets200JSONResponseBody_Data_Item


3. Инлайн middleware-типы в strict-server

StrictHandlerFunc и StrictMiddlewareFunc больше не являются алиасами к типам из пакета runtime/strictmiddleware/<framework>.

Теперь они определяются прямо в сгенерированном коде:
// было
import strictecho "github.com/oapi-codegen/runtime/strictmiddleware/echo"
type StrictHandlerFunc = strictecho.StrictEchoHandlerFunc

// стало
type StrictHandlerFunc func(ctx echo.Context, request any) (any, error)


Если вы ссылались на типы вида strictecho.StrictEchoHandlerFunc напрямую, замените их на StrictHandlerFunc из сгенерированного пакета.

Что нового

Разрешение коллизий имён. Новая опция output-options.resolve-type-name-collisions: true позволяет генерировать код, когда два разных определения претендуют на одинаковое Go-имя.

Коллизия разрешается детерминированно через суффикс:
type Status string          // из components.schemas.Status
type StatusParameter = string // из components.parameters.Status


Опция отключена по умолчанию.

Полная матрица параметров. Теперь поддерживаются все комбинации style × explode × type для параметров пути, запроса, заголовков и куки. Если раньше падала ошибка unsupported style или поведение отличалось в разных бэкендах, регенерация должна это исправить. Требуется github.com/oapi-codegen/runtime версии v1.4.0 или выше.

Опциональные заголовки ответов. В strict-server необязательные заголовки теперь генерируются как указатели. Если поле nil, заголовок не отправляется:
// было
type GetFoo200ResponseHeaders struct {
XOptional string // всегда в ответе, даже пустым
}

// стало
type GetFoo200ResponseHeaders struct {
XOptional *string // nil — заголовок не отправляется
}


Старое поведение возвращается через compatibility.headers-implicitly-required: true.

Echo v5. Добавлена поддержка Echo v5 через флаг generate.echo5-server: true. Echo v4 не затронут.

Middleware на уровне операций в Echo. В RegisterHandlersWithOptions появился OperationMiddlewares:
api.RegisterHandlersWithOptions(e, server, api.RegisterHandlersOptions{
OperationMiddlewares: map[string][]echo.MiddlewareFunc{
"createPet": {authMiddleware, auditMiddleware},
},
})


Middleware на уровне хендлеров в Fiber. В FiberServerOptions появился слайс HandlerMiddlewares []HandlerMiddlewareFunc.

Обработчики ошибок в strict-server для Gin. Теперь Gin поддерживает RequestErrorHandlerFunc и ResponseErrorHandlerFunc в StrictServerOptions, как это уже было в Echo.

Требования к окружению

Для сборки и запуска oapi-codegen теперь нужен Go 1.24.4+. Сгенерированный код по-прежнему может работать на более старых версиях. Из-за транзитивных зависимостей может потребоваться зафиксировать версии:
github.com/speakeasy-api/jsonpath v0.6.3
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936


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

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

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

#GoLive
  • 👍 4
  • ❤ 2
  • 🔥 2
  • 🤩 2
  • 👾 1
Post #7134 3.45K
🦾 Почему ваши AI-продукты на базе LLM ломаются (и как это чинить)?

Выкатили ИИ-фичу в прод, а она галлюцинирует, падает или выдает мусор? Приглашаем на открытый вебинар, где разберем реальную боль внедрения LLM-агентов и научимся делать так, чтобы «всё работало».

🗓️ Когда: 14 мая в 19:00 МСК
⏱️ Формат: 60 минут мяса + 30 минут ответов на ваши вопросы

🧑🏻‍💻 Кто вещает: Эмиль Сатаев — Backend Platform Developer (8+ лет в разработке). Человек, который своими руками внедряет LLM и агентные системы в реальные коммерческие сервисы.

🎁 Главный бонус для онлайна:
Только участникам прямого эфира подарим уникальный промокод на скидку 10.000 ₽ на большой курс AgentOps.

👉 Занять место на вебинаре
  • 🥱 5
  • 👍 1
Post #7133 3.56K
⚡️ Быстрый JSON от ByteDance

Стандартная библиотека encoding/json в Go работает медленно на больших объёмах данных. Если сервис обрабатывает тысячи запросов в секунду с тяжёлыми JSON-структурами, это становится заметным узким местом.

Sonic это библиотека для сериализации и десериализации JSON, которая использует JIT-компиляцию и SIMD-инструкции процессора. Она полностью совместима с API encoding/json, то есть замена происходит в одну строку.

Что под капотом

Sonic генерирует машинный код под конкретную схему данных прямо во время выполнения, а не в момент компиляции. SIMD позволяет обрабатывать несколько байт одновременно на уровне железа.

В итоге на средних JSON (13 КБ, 300+ ключей) скорость кодирования достигает 2468 МБ/с против 792 МБ/с у стандартной библиотеки.

Установка:
go get github.com/bytedance/sonic


Базовое использование

Замена encoding/json буквально в одну строку:
import "github.com/bytedance/sonic"

var data YourSchema

// Кодирование
output, err := sonic.Marshal(&data)

// Декодирование
err := sonic.Unmarshal(output, &data)


Стриминг

Sonic поддерживает потоковое чтение из io.Reader и запись в io.Writer:

var o = map[string]interface{}{}
var r = strings.NewReader(`{"a":"b"}{"1":"2"}`)
var dec = sonic.ConfigDefault.NewDecoder(r)
dec.Decode(&o)
dec.Decode(&o)
fmt.Printf("%+v", o)
// map[1:2 a:b]


Частичный парсинг через ast.Node

Если нужно вытащить одно поле из большого JSON, не парся всё целиком:
import "github.com/bytedance/sonic"

node, err := sonic.GetFromString(twitterJson, "statuses", 3, "user")
var user User
err = sonic.UnmarshalString(node.Raw(), &user)


Pretouch для тяжёлых схем

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

Чтобы этого избежать, компилируем заранее при старте приложения:
import (
"reflect"
"github.com/bytedance/sonic"
)

func init() {
var v HugeStruct
err := sonic.Pretouch(reflect.TypeOf(v))
}


Готовые конфиги

• sonic.ConfigDefault — быстрый режим по умолчанию

• sonic.ConfigStd — совместимый со стандартной библиотекой

• sonic.ConfigFastest — максимальная скорость, без дополнительных проверок

На платформах без поддержки JIT Sonic автоматически откатывается к encoding/json.

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

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

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

#GoToProduction
  • 👍 14
  • ❤ 9
  • 🔥 4
Post #7132 3.49K
🤖 ИИ-интервью, архетипы египетских богов и симуляция роли гендиректора

Сбер тестирует кандидатов по архетипам Маат, Исиды и Бастет. Яндекс ввёл обязательный fit-интервью и единую систему оценки. А ГигаРекрутер в Telegram уже проводит первичные интервью вместо живого HR.

Что реально происходит на рынке IT-найма и как к этому готовиться ➡️ читайте в статье

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

🐸 Библиотека Go-разработчика
  • ❤ 3
  • 👾 3
  • 😁 2
Post #7130 3.43K
🖇 Новый пакет для поддержки JIT-компиляторов и встроенных VM

В трекере Go появился proposal #78189. Он предлагает добавить стандартный пакет runtime/jit, который позволит Go-рантайму корректно работать с динамически генерируемым кодом.

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

Go компилирует код заранее. Стек-анвайндер рантайма устроен так, что знает только о функциях, зарегистрированных в moduledata на этапе линковки.

Если на стеке оказывается счётчик команд из динамически сгенерированного кода — JIT-компилятора, WASM-рантайма или встроенной VM — рантайм падает с фатальной ошибкой:
runtime: unexpected return pc for somefunc called from 0x7f4a12340010
fatal error: unknown caller pc


Это затрагивает любую программу, которая встраивает JIT, например, база данных компилирует SQL в нативный код, хостит WASM, использует FFI с трамплинами или встраивает скриптовые VM вроде Lua.

Единственный обходной путь сегодня — не вызывать Go-функции из динамического кода вообще, либо использовать промежуточные C-фреймы через cgo, что добавляет около 40 наносекунд накладных расходов и требует cgo-тулчейна.

Что предлагают

Новый пакет runtime/jit с минимальным API. Ключевые части:

Region — структура, описывающая регион исполняемой памяти. Содержит колбэки для анвайндинга (Next), описания фреймов в трейсбеке (Describe) и сканирования корней GC (ScanStack).

Три режима анвайндинга:

UnwindStop
— трейсбек обрывается на JIT-фрейме
UnwindSkip — рантайм пропускает пользовательские фреймы и продолжает
UnwindDeclare — пропускает и описывает фреймы в трейсбеке.

handle := jit.Register(jit.Region{
Lo: uintptr(codeStart),
Hi: uintptr(codeEnd),
Unwind: jit.UnwindDeclare,
Next: myUnwindNext,
Describe: myDescribe,
ScanStack: myScanStack,
})
defer handle.Unregister()


Для кооперации со шедулером — функция Preempt(). JIT-код вызывает её в точках безопасности (заголовки циклов, входы в функции) и возвращает управление Go, если шедулер или GC запросили остановку.

Как это работает внутри

Реестр регионов построен на copy-on-write с атомарным свапом указателя. Анвайндер получает доступ к нему без блокировок. Патчи в traceback.go проверяют пользовательские регионы перед тем, как бросить ошибку на неизвестный счётчик команд. GC получает хук в markroot() для вызова ScanStack.

Горутина остаётся в состоянии _Grunning пока выполняется JIT-код — никакого переключения состояний, никакого освобождения. Это принципиальное отличие от cgocall, который обращается с чужим кодом как с чёрным ящиком.

Статус

Proposal открыт в марте 2026 года. Реализация около 500 строк кода, 15 тестов, трамплины для всех 13 Linux-архитектур. Проверена в продакшне в движке базы данных MemCP.

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

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

#GoLive
  • 👍 18
  • ❤ 4
  • 👾 1
Post #7129 3.51K
🛠 CLI для PostgreSQL с автодополнением

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

pgxcli это интерактивный CLI для PostgreSQL, написанный на Go. Он добавляет подсветку SQL прямо в терминале, автодополняет ключевые слова и команды, сохраняет историю сессий и поддерживает стандартные команды PostgreSQL: \d, \l, \dt, \q.

Как подключиться

Несколько вариантов на выбор:
# Позиционные аргументы
pgxcli mydb myuser

# Флаги
pgxcli --host localhost --port 5432 --user postgres --dbname postgres

# URI
pgxcli postgres://user:password@localhost:5432/dbname

# Интерактивная форма подключения
pgxcli -i


Установка:
go install github.com/balaji01-4d/pgxcli/cmd/pgxcli@latest


Конфигурация

При первом запуске создаётся конфиг по пути ~/.config/pgxcli/config.toml. Там можно настроить стиль промпта и цветовую схему.

Стоит ли использовать вместо pgcli

pgxcli пока молодой проект, версия v0.1.0 вышла в конце апреля 2026 года. Преимущество одно — это единый бинарник без внешних зависимостей с быстрым стартом.

В планах стриминг результатов для больших таблиц, экспорт в CSV, Excel и SQL, просмотр таблиц через браузер.

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

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

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

#GoToProduction
  • ❤ 13
Post #7127 3.72K
❓ Почему в Go невозможно поставить открывающую фигурную скобку на новой строке

Многие думают, что это просто линтер или code style. Но причина куда глубже и она спрятана в самом сердце компилятора, на уровне, до которого добираются немногие.

// Так написать не получится — будет ошибка компиляции
func foo()
{
fmt.Println("hello")
}


Подсказка: подумайте, что происходит до того, как парсер вообще увидит ваш код 👀

Ответ: вы сами знаете где

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

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

#ReadySetGo
  • 🥱 6
  • ❤ 3
  • 👍 2
Post #7126 3.54K
🔒 CORS-мидлвар для Go, который сложно настроить неправильно

CORS это тема, где легко допустить ошибку в конфигурации и не заметить этого. Разрешить все origins через *, забыть проверить preflight, или написать паттерн, который пропускает лишнее. Библиотека jub0bs/cors построена так, чтобы минимизировать такие сценарии.

Что это такое

jub0bs/cors — мидлвара для стандартного net/http, которая реализует CORS-протокол строго по спецификации Fetch standard. Автор позиционирует её как более безопасную альтернативу популярной rs/cors: API спроектирован так, что небезопасные конфигурации просто не дают себя создать.

Как это работает

Создаём мидлвару через cors.NewMiddleware, передаём конфиг, оборачиваем нужный ServeMux:
package main

import (
"io"
"log"
"net/http"

"github.com/jub0bs/cors"
)

func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /hello", handleHello) // CORS здесь не настроен

corsMw, err := cors.NewMiddleware(cors.Config{
Origins: []string{"https://example.com"},
Methods: []string{http.MethodGet, http.MethodPost},
RequestHeaders: []string{"Authorization"},
})
if err != nil {
log.Fatal(err) // плохая конфигурация — ошибка сразу, не в рантайме
}
corsMw.SetDebug(true) // включить debug-режим при разработке

api := http.NewServeMux()
api.HandleFunc("GET /users", handleUsersGet)
api.HandleFunc("POST /users", handleUsersPost)
mux.Handle("/api/", http.StripPrefix("/api", corsMw.Wrap(api)))

if err := http.ListenAndServe(":8080", mux); err != http.ErrServerClosed {
log.Fatal(err)
}
}


Маршрут /hello не попадает под CORS — мы явно применяем мидлвару только к /api/. Это позволяет настраивать CORS точечно, а не на всё приложение сразу.

Что выделяет библиотеку

Валидация конфига происходит при старте. Если что-то не так — NewMiddleware возвращает ошибку сразу, не в момент обработки запроса. Никаких сюрпризов в продакшне.

Есть debug-режим: при включённом SetDebug(true) мидлвара добавляет в ответ заголовки с объяснением, почему preflight провалился. Это удобно при разработке и легко отключается в продакшне.

Мидлвару можно перенастраивать на лету через метод Reconfigure без перезапуска сервера. Это потокобезопасно.

Ошибки конфигурации можно обрабатывать программно через пакет cfgerrors — например, для формирования ответа в формате RFC 9457 (Problem Details).

По бенчмаркам автора библиотека быстрее rs/cors, хотя для большинства проектов это не принципиально.

Чего библиотека не умеет

Она намеренно ограничена. Нельзя задать произвольный паттерн для origins — только конкретные значения из заранее определённого набора форматов. Нельзя логировать каждый запрос через мидлвару. Нельзя менять статус-код preflight-ответа (всегда 204). Автор объясняет эти ограничения соображениями безопасности и соответствия спецификации.

Строгий по умолчанию, с валидацией на старте, debug-режимом и возможностью перенастройки без рестарта.

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

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

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

#GoToProduction
  • 👍 7
  • ❤ 2
  • 🔥 2
Post #7125 3.65K
🧑‍💻 Валидация данных в Go

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

Идея

Определяем интерфейс Validator. Любой тип, который его реализует, умеет сам себя проверять:
type Validator interface {
Validate() map[string]string
}


Вместо сырой map[string]string удобнее завести отдельный тип Problems:
type Problems map[string]string

func (p Problems) Add(field, msg string) {
p[field] = msg
}

func (p Problems) String() string {
if len(p) == 0 {
return ""
}
msgs := make([]string, 0, len(p))
for field, msg := range p {
msgs = append(msgs, fmt.Sprintf("%s: %s", field, msg))
}
return strings.Join(msgs, "; ")
}


Пример с реальным типом

Допустим, у нас есть запрос на регистрацию периодической задачи:
type TaskCreateRequest struct {
Name string
Run func(ctx context.Context) error
Interval time.Duration
Deadline time.Duration
}


Правила простые: имя обязательно, функция не может быть nil, интервал и дедлайн больше нуля, дедлайн меньше интервала. Реализуем Validate прямо на этом типе:
func (r *TaskCreateRequest) Validate() validation.Problems {
problems := make(validation.Problems)

if r.Name == "" {
problems.Add("Name", "is required")
}
if r.Run == nil {
problems.Add("Run", "is required")
}
if r.Interval <= 0 {
problems.Add("Interval", "must be greater than 0")
}
if r.Deadline <= 0 {
problems.Add("Deadline", "must be greater than 0")
}
if r.Deadline >= r.Interval {
problems.Add("Deadline", "must be less than Interval")
}

return problems
}


Функция `Check` вместо прямого вызова `Validate`

validation.Problems не реализует интерфейс error, поэтому вызывающий код не должен знать об этом типе. Для этого есть функция Check:
func Check(v Validator) error {
if v == nil {
return errors.New("value is nil, cannot validate")
}
problems := v.Validate()
if len(problems) == 0 {
return nil
}
return fmt.Errorf("validation failed for %s: %s",
typeName(v),
problems.String(),
)
}


Использование выглядит так:
func RegisterTask(req *TaskCreateRequest) error {
if err := validation.Check(req); err != nil {
return err
}
// ...
}


Если передать неполный запрос, получим понятное сообщение:
validation failed for TaskCreateRequest: Deadline: must be greater than 0; Name: is required


Где размещать валидацию

В проектах с архитектурой controller/service/repository валидация живёт на уровне сервиса. Сервис проверяет всё, что к нему приходит — будь то HTTP-запрос, gRPC или stdin.

Удобно держать Validate рядом с определением типа. Когда что-то идёт не так — плохие данные прошли до базы или хорошие данные были отклонены — сразу понятно, куда смотреть.

Подход работает за счёт трёх вещей: единый интерфейс Validator, тип Problems для читаемых ошибок и функция Check, которая возвращает стандартный error. Валидация остаётся рядом с типом, код не размазывается по проекту, и добавить новую проверку дело одной строки.

➡️ Оригинал

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

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

#GoDeep
  • 👍 18
  • ❤ 5
  • 🔥 4
Post #7124 3.77K
🐹 Дружеское напоминание

Всегда вызывайте wg.Add() до запуска горутины, а не внутри неё:
// Гонка
go func() {
wg.Add(1)
defer wg.Done()
}()
wg.Wait()

// Всё чисто
wg.Add(1)
go func() {
defer wg.Done()
}()
wg.Wait()


Если добавить счётчик внутри горутины, wg.Wait() может отработать раньше, чем горутина успеет инкрементировать счётчик. Программа завершиться, а горутина так и не началась.

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

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

#GoToProduction
  • 👍 11
  • 🥱 9
  • ❤ 4
  • 😢 2
  • 😁 1
Post #7123 3.76K
🧐 Топ-вакансий для Go-разработчиков за неделю

Стажер Go-разработчик — гибрид в Нижнем Новгороде

Разработчик Go (Ozon Банк) — от 365 000 ₽ на удалёнку по Москве

Middle+ / Senior Golang-разработчик (LLM/AI/Analytics) — до 420 000 ₽ и снова удалёнка по Москве

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

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

#GoWork
  • ❤ 2
Post #7122 3.8K
🔥 4 привычки кодеров

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

но, я собрал 4 привычки адептов «чистого кода», (которые обычно все практикуют) 🤡

• Бесконечный рефакторинг рабочего кода.
Кажется, что так ты делаешь продукт лучше. Итог: жестко падаешь в перфекционизм. Переписываешь функцию по три раза, а бизнес ждет релиз. Закрываешь вкладку и в голове абсолютная пустота, время потрачено, а новых фичей ноль.

• Упарывание в сложную архитектуру

Сеньоры на ютубе обещают золотые горы, если внедрить микросервисы куда угодно. Итог: получаешь красивый overengineering-проект для мамы и 0 запущенных продуктов в срок, пока конкуренты клепают MVP на коленке.

• Душные споры на ревью
Неплохо, но как итог: ты пишешь полотна текста и тратишь часы на поиск глупой придирки к стилю, потому что банально фокус сместился с реальной задачи на эго.

• Ручная микро-оптимизация
Классика для тех, кто любит алгоритмы из универа. Итог: убиваешь дни жизни и выжимаешь миллисекунды, хотя бизнесу нужен был просто грязный, но рабочий скрипт еще вчера.

Проблема в том, что ни один из этих путей не дает самого главного - скорости и проверки гипотез. Реальному рынку плевать на твой идеальный код за 3 дня. Бизнес предпочтет код от ИИ-агента за 5 минут, который уже завтра начнет приносить деньги.

Хочешь обкатанный на нас лично и 100х учениках метод, как перестать кодить руками и начать делегировать задачи автономным системам?

👉 Заходи сюда, но у нас осталось всего 4 места, набор идет до завтрашнего дня.

P. S. Если интересно еще что-нибудь почитать от меня, то заходите в «Азбуку Айтишника», там я рассказываю об айти-базе, также у меня там есть бесплатный гайд на 15 глав по ии-агентам
  • 😁 6
  • ❤ 4
  • 🤔 1
Older posts →
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →