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 #7263 · Back to latest

Older Posts 20 shown
Post #7262 2.98K
🔥 Инженерная методичка по ИИ от Романа Барлоса (Team Lead в Yandex Cloud)

Продолжаем делиться экспертизой команды курса «Разработка ИИ-агентов».

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

🛠 Полезные инструменты:
• Understand Anything — граф знаний по коду и зависимостям.
• DeepTutor — open-source платформа для персонализированного обучения.
• Superpowers — набор практик для системной разработки с ИИ.
• Awesome Agent Skills — коллекция навыков для ИИ-агентов.


📚 Ключевые работы по LLM:
• Attention Is All You Need (2017) — архитектура Transformer.
• GPT-1 (2018) — начало эпохи GPT.
• GPT-2 (2019) — решение новых задач без дообучения.
• GPT-3 (2020) — обучение на примерах из запроса.
• InstructGPT (2022) — RLHF и современные чат-боты.


На курсе Роман выступает консультантом программы: помогает формировать содержание уроков с опорой на актуальные инженерные практики».

Занять свое место на потоке:

👉 Курс «Разработка ИИ-агентов»
Post #7261 2.93K
🤩 Разбор строк через strings.Cut

Часто строку нужно разрезать ровно один раз по разделителю. Так делят почту на имя и домен, заголовок на ключ и значение, путь на префикс и остаток.

Раньше для этого брали strings.Index или strings.Split, и оба варианта тянули за собой лишний код с проверкой границ. В Go 1.18 появился strings.Cut, который закрывает этот случай одной строкой.

strings.Index возвращает позицию разделителя или -1, если его нет. Дальше нужно вручную проверять результат и аккуратно нарезать строку по смещениям, не выходя за границы.

Старый способ:
i := strings.Index(email, "@")
if i < 0 {
return "", "", false
}
username := email[:i]
domain := email[i+1:]


Три строки на нарезку плюс отдельная проверка на -1. Если забыть про +1, в домен попадёт сам символ разделителя.

Современный способ:
username, domain, found := strings.Cut(email, "@")


Функция возвращает часть до разделителя, часть после него и флаг found. Если разделителя нет, found будет false, а первой частью вернётся вся исходная строка.

Чем отличается от `strings.Split`

strings.Split режет строку по всем вхождениям и возвращает срез. Когда разрез нужен один, приходится брать SplitN с лимитом и потом разбирать срез по индексам. strings.Cut сразу отдаёт две именованные части и не выделяет срез под результат, поэтому работает экономнее.

Практический пример

Разбор строки заголовка вида Content-Type: application/json выглядит так:
key, value, ok := strings.Cut(header, ": ")
if !ok {
return fmt.Errorf("invalid header: %q", header)
}
key = strings.TrimSpace(key)
value = strings.TrimSpace(value)


Флаг ok сразу отделяет корректные строки от мусора, без ручной возни с индексами.

Используйте strings.Cut везде, где строку нужно разделить ровно один раз, а Split оставьте для случаев с множеством вхождений. Код станет короче, а ошибок с границами среза не будет.

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

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

#GoToProduction
  • 👍 11
  • ❤ 2
Post #7260 2.95K
📰 Первый летний дайджест

Короткий, как и отпуск..

— Go закрыл три уязвимости

Вышли версии Go 1.26.4 и 1.25.11. Закрывают три уязвимости в стандартной библиотеке.

— Go может разрешить превращать функции в интерфейсы с одним методом

В трекере Go обсуждают предложение #47487. Оно разрешает приводить значение функции к интерфейсу с ровно одним методом, если сигнатура функции совпадает с сигнатурой этого метода.

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

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

#GoLive
Post #7259 2.8K
🧑‍💻 Конструктор, из которого собран Docker

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

Moby — это открытый проект, который Docker вынес в апстрим, набор готовых компонентов для сборки систем на контейнерах. Из этих кубиков собран сам Docker, и из них же вы можете собрать что-то своё.

➡️ Что внутри.

Moby это не одна программа, а коллекция компонентов с понятными API, среди них движок выполнения контейнеров, инструменты сборки образов, работа с реестром и оркестрация. Идея в том, чтобы каждый кусок имел чёткую функцию и при желании заменялся на другой.

➡️ Как с ним работать.

Чаще всего вы общаетесь с движком через Docker CLI, но из Go можно дёргать тот же Engine API напрямую.

Вот аналог docker ps, который читает настройки из переменных окружения вроде DOCKER_HOST и сам согласует версию API с демоном:
package main

import (
"context"
"fmt"
"log"

"github.com/moby/moby/client"
)

func main() {
apiClient, err := client.New(client.FromEnv)
if err != nil {
log.Fatal(err)
}
defer apiClient.Close()

result, err := apiClient.ContainerList(context.Background(), client.ContainerListOptions{
All: true,
})
if err != nil {
log.Fatal(err)
}

for _, ctr := range result.Items {
fmt.Printf("%s %s %s\n", ctr.ID, ctr.Status, ctr.Image)
}
}

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

➡️ Кому это нужно.

Moby рассчитан на инженеров и энтузиастов, которым интересно ковыряться в открытом коде, чинить баги и строить системы на контейнерах.

Если коротко, Moby это фундамент Docker, открытый и модульный. Брать его стоит тогда, когда вам мало готового движка и хочется собрать или допилить свой.

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

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

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

#GoToProduction
  • 👍 10
  • ❤ 2
Post #7257 2.76K
📎 Чего не дают логи, метрики и трейсы

Идея «три столпа наблюдаемости» звучит убедительно. Соберите логи, метрики и трейсы, отправьте в одну платформу, и поймёте систему. На практике у команды есть все три, а баг в проде она всё равно не находит. Ниже главные мысли статьи о том, почему так выходит, с примерами на Go.

🖇 Данные есть, понимания нет

Три столпа дают сигналы, но не объяснение. На инциденте вы коррелируете то, что собрали заранее, часто в спешке, и этого набора не хватает под конкретный сбой.

🖇 Высокая кардинальность бьёт первой

Как только вам нужен разрез по user_id, арендатору или связке эндпоинт плюс регион, метрики ломаются.

Так делать не стоит, потому что число рядов взрывается:
// Плохо. user_id и tenant дают почти неограниченное число комбинаций.
httpRequests := prometheus.NewCounterVec(
prometheus.CounterOpts{Name: "http_requests_total"},
[]string{"endpoint", "region", "user_id", "tenant"},
)
httpRequests.WithLabelValues(endpoint, region, userID, tenant).Inc()


Безопаснее держать в метках только ограниченный набор значений, а детали уносить в трейс или лог:
// Лучше. В метках только то, у чего мало возможных значений.
httpRequests := prometheus.NewCounterVec(
prometheus.CounterOpts{Name: "http_requests_total"},
[]string{"endpoint", "region", "status"},
)
httpRequests.WithLabelValues(endpoint, region, status).Inc()


🖇 Инструментация видит только ожидаемое

Вы измеряете то, что предусмотрели заранее. Новый тип сбоя не попадает ни в один график, и в этот момент вы слепы.

🖇 Худшие баги живут между сервисами

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

🖇 Зелёные метрики не значат правильный результат

Три столпа описывают инфраструктуру, а не бизнес. Сервис может держать отличную задержку и нулевые ошибки, при этом считать цену неверно.

Поэтому полезно мерить сам бизнес-результат, а не только технику:
// Считаем не задержку, а расхождение цены. Это и есть семантическая наблюдаемость.
span.SetAttributes(
attribute.String("order.id", order.ID),
attribute.Int64("order.expected_cents", expected),
attribute.Int64("order.charged_cents", charged),
)
if charged != expected {
priceMismatchTotal.Inc()
}


🖇 Лучшее улучшение делается после инцидента

Самый сильный приём звучит скучно. После разбора спросите, какой информации не хватило, и добавьте её.

Часто это пара полей в структурном логе через slog, которых раньше не было:
// После разбора добавили request_id и tenant. Без них инцидент искали вслепую.
slog.Error("payment declined",
"request_id", reqID,
"tenant", tenant,
"provider", provider,
"code", declineCode,
)


Эффект накапливается, и через год полтора команда отлаживает прод заметно быстрее.

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

Наша рассылка даёт максимум за минимум.

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

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

#GoDeep
  • 👍 9
  • ❤ 1
Post #7256 2.71K
😎 Знакомьтесь с экспертом Proglib.academy: Senior Software Engineer и Team Lead в Yandex Cloud Роман Барлос

Роман — консультант нашего курса «Разработка ИИ-агентов». Он работает на стыке cloud-native архитектуры и AI, активно внедряя современные ИИ-подходы в реальные процессы разработки.

За что его ценит IT-комьюнити?


🟣 Team Lead и AI-евангелист в команде UX Yandex Cloud
14-лет в разработке. Занимается AI-адопшеном в команде Yandex Cloud, проводит мастер-классы и продвигает лучшие практики для повышения эффективности разработчиков.


🟣 Техлид Sourcecraft Code Assistant
С сильным практическим бэкграундом принимал участие как технический лид в создании мощного AI-расширения для VS Code.


🟣 Создатель полезного Open Source
Разрабатывает утилиты, которые позволяют быстро начать эксперименты с инференсом и агентами в локальном окружении: например, набор скриптов vllm-setup для быстрого запуска окружения и mini-proxy — минималистичный прокси для OpenAI API провайдеров.


🟣 Автор интерактивных ML-визуализаций
Объясняет сложные концепции наглядно. Создал серию залипательных обучающих материалов, где можно вживую пощупать работу сетей Хопфилда, машин Больцмана и VC-размерности.


Роман регулярно делится инженерными наработками, инсайтами и экспертизой в своем авторском Telegram-канале

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

Узнать больше о программе и разработке автономных систем:
👉 Курс «Разработка ИИ-агентов»

Так, продолжаем знакомить вас с командой?
👍 — Да, ждем новых лиц
🔥 — Жду полезные материалы от Романа
  • 🤔 2
  • ❤ 1
  • 🔥 1
  • 😢 1
Post #7255 2.99K
❓ Можно ли использовать слайс как ключ в мапе

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

Прежде чем лезть в документацию — подумайте сами. Вот что стоит вспомнить:

🔹 В Go ключ мапы должен быть comparable — то есть поддерживать операции == и !=

🔹 Спросите себя: можно ли сравнить два слайса через ==? Что вообще значит «два слайса равны»?

➡️ Ответ

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

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

#ReadySetGo
Post #7254 3.21K
📎 Обёртывание ошибок через %w

В Go 1.13 появился %w, который сохраняет исходную ошибку внутри обёртки и позволяет проверять её дальше по коду через errors.Is и errors.As.

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

Старый способ:
fmt.Errorf("failed to fetch user: %v", err)


%v подставляет текст ошибки и выбрасывает сам объект. Узнать, что под капотом лежала sql.ErrNoRows, после этого уже нельзя.

Современный способ:
fmt.Errorf("failed to fetch user: %w", err)


%w оборачивает ошибку и сохраняет ссылку на оригинал, поэтому цепочку потом можно размотать.

Как проверять причину

errors.Is идёт по всей цепочке обёрток и сравнивает каждое звено с конкретным значением:
if errors.Is(err, sql.ErrNoRows) {
// пользователь не найден
}


Не важно, сколько слоёв обёрток сверху, проверка доберётся до нужной ошибки.

Когда нужен сам объект

Если требуется не просто факт совпадения, а доступ к полям ошибки, помогает errors.As. Он находит в цепочке ошибку нужного типа и записывает её в переменную:
var pathErr *os.PathError
if errors.As(err, &pathErr) {
slog.Error("ошибка пути", "path", pathErr.Path)
}


Своя ошибка в цепочке

Чтобы ваш тип распознавался через errors.Is и errors.As, достаточно реализовать метод Unwrap:
type AppError struct {
Code int
Err error
}

func (e *AppError) Unwrap() error { return e.Err }


%w заменил хрупкое сравнение строк на надёжную проверку по значению и по типу. Оборачивайте ошибки этим глаголом везде, где добавляете контекст, и используйте errors.Is вместо сравнения текста.

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

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

#GoToProduction
  • 👍 15
  • ❤ 2
Post #7253 3.12K

Forwarded from Азбука айтишника

🫳 Типичная микросервисная архитектура 🫳

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

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

• API Gateway
Единая точка входа для всех клиентов. Итог: если гейтвей "прилег" или перегружен, твои крутые микросервисы внизу превращаются в бесполезный набор кода, до которого никто не может достучаться.

• Load Balancer
Раскидывает трафик, чтобы один сервер не закипел. Итог: всё работает плавно, пока балансировщик правильно настроен. Ошибка в конфиге и весь трафик летит в один сервис, убивая его за секунды.

• Service Registry & Discovery
Сервисы должны как-то находить друг друга. Итог: если эта штука заглючит, сервис А просто не узнает, где искать сервис Б. Система развалится на куски, которые не умеют общаться.

• Разделение по доменам (Domain A / Domain B)
У каждого сервиса своя база. Итог: ты получаешь независимость, но платишь за это адом при попытке собрать общую аналитику или сделать сложный транзакционный запрос между доменами.

Уже переезжали на микросервисы?
❤️ — да, это было больно, но оно того стоило
🔥 — сидим на монолите и в ус не дуем

🔹 Практический интенсив «Архитектуры и шаблоны проектирования»
🔹 Получить консультацию менеджера
🔹 Сайт Академии 🔹 Сайт Proglib

🏃‍♀️ Азбука айтишника

#ликбез
  • ❤ 16
  • 🔥 15
  • 🌚 3
Post #7252 2.8K
💰 Деньги в Go без потери копеек

Как работать с деньгами в Go

Дробные типы в Go хранят значения в двоичном виде, и многие десятичные дроби в нём не представимы точно. Из-за этого простые суммы дают неожиданный результат, а ошибки копятся с каждой операцией. Ниже разберём, как считать деньги без потери копеек.

ℹ️ Почему float64 не подходит

Классический пример:
fmt.Println(0.1 + 0.2) // 0.30000000000000004


На одной операции это незаметно, но в отчёте на тысячи позиций расхождение в копейки превращается в расхождение в рубли. Сравнивать такие суммы через == тоже нельзя, потому что равные на бумаге значения в памяти отличаются.

📎 Способ первый. Целые числа в минимальных единицах

Самый надёжный приём хранить сумму в наименьшей единице валюты, то есть в копейках или центах, как int64.

Целочисленная арифметика точна, сложение и вычитание не теряют ничего:
type Money int64 // сумма в копейках

func (m Money) String() string {
sign := ""
if m < 0 {
sign = "-"
m = -m
}
return fmt.Sprintf("%s%d.%02d", sign, m/100, m%100)
}


Цену 199 рублей 99 копеек вы храните как Money(19999), а метод String собирает её обратно в читаемый вид. Этого подхода хватает для подавляющего большинства задач с фиксированной валютой.

📎 Способ второй. Библиотека shopspring/decimal

Целые числа неудобны там, где много делений и процентов, например при расчёте НДС или распределении суммы между участниками.

Тут помогает пакет decimal, который хранит число как набор цифр с десятичной точкой и считает в десятичной системе:
import "github.com/shopspring/decimal"

price := decimal.RequireFromString("199.99")
tax := price.Mul(decimal.RequireFromString("0.20"))
total := price.Add(tax).Round(2)

fmt.Println(total.StringFixed(2)) // 239.99


Обратите внимание, что значение задаётся строкой через RequireFromString, а не из float64. Так в число не попадёт двоичная погрешность ещё на входе.

📎 Округление и деление

При делении суммы остаток никуда не девается, и его нужно осознанно куда-то отнести. decimal позволяет задать правило округления явно:
total := decimal.RequireFromString("100.00")
share := total.Div(decimal.NewFromInt(3)).Round(2)
fmt.Println(share.StringFixed(2)) // 33.33


Сумма трёх таких долей даст 99.99, и одну копейку придётся добавить к одной из частей вручную. Это не баг библиотеки, а свойство самих денег, и решать его нужно по правилам вашей предметной области.

Для денег в Go не используйте float64 и точка.

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

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

#GoDeep
  • 👍 20
  • ❤ 5
Post #7251 2.96K
🌐 Пакет из стандартной библиотеки Go, про который почти никто не знает

net/http/httptrace даёт хуки внутрь исходящего HTTP-запроса — туда, куда снаружи не заглянуть:

• DNS lookup: начало и конец
• TCP connect
• TLS handshake
• Момент получения первого байта ответа
• Факт переиспользования соединения

Вся магия через контекст: трейс кладётся в ctx, транспорт сам его достаёт. Никаких интерфейсов, никакого middleware.

Что можно собрать за пару строк:

1. curl-like тайминги

fmt.Printf("DNS: %v\n", dnsDone.Sub(dnsStart))
fmt.Printf("TLS: %v\n", tlsDone.Sub(tlsStart))
fmt.Printf("TTFB: %v\n", firstByte.Sub(gotConn))


2. Логирующий RoundTripper

Оборачиваете транспорт, в каждом запросе автоматически пишутся тайминги. Одна инициализация и всё.

Лайфхак: GotConnInfo.Reused = false на каждом запросе к одному хосту = где-то не закрывается тело ответа.

➡️ Пакет

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

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

#GoToProduction
  • 👏 10
  • ❤ 3
Post #7249 3.06K
👨‍💻Структурное логирование в Go через slog

До версии 1.21 в Go не было единого подхода к логированию. Команды выбирали между сторонними библиотеками вроде zap, logrus и zerolog, поэтому кодовая база разных проектов выглядела неодинаково.

Стандартный пакет log умел писать только обычные текстовые строки, которые тяжело парсить и собирать в системах вроде Loki или ELK. В Go 1.21 появился slog, который принёс структурное логирование прямо в стандартную библиотеку. Если из всего списка новинок вы захотите взять только одну, берите эту.

Какую боль решает

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

Старый способ выглядел так:
log.Printf("[ERROR] User %d login failed from IP %s", userID, ip)


Здесь данные смешаны с текстом, и чтобы вытащить userID, нужно писать регулярки.

Современный способ:
import "log/slog"

slog.Error("Login failed", "userID", userID, "ip", ip)


Сообщение остаётся читаемым, а данные лежат в отдельных полях.

Что он делает

slog строится вокруг трёх частей. Logger принимает вызовы вроде Info и Error. Handler решает, куда и в каком формате писать. Attr это пара ключ значение. В комплекте идут два обработчика, TextHandler для человекочитаемого вывода и JSONHandler для машинного.

Как настроить JSON вывод

Чтобы получать логи в JSON, достаточно подменить обработчик у глобального логгера:
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
slog.SetDefault(logger)

slog.Info("server started", "port", 8080)


На выходе получится строка JSON с полями time, level, msg и port. Её сразу можно отправлять в сборщик логов без дополнительной обработки.

Группировка и контекст

Часто к каждому запросу удобно прикреплять одни и те же поля. Метод With создаёт логгер с заранее заданными атрибутами:
reqLogger := slog.With("requestID", reqID, "userID", userID)
reqLogger.Info("processing request")
reqLogger.Warn("slow response", "ms", 320)


Теперь requestID и userID попадут в каждую запись без повторного перечисления.

Переводить проект можно постепенно, начав с замены log на slog в новых модулях. Старый код при этом продолжит работать, а новые записи сразу станут пригодными для машинного разбора.

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

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

#GoToProduction
  • 👍 5
  • 🥱 4
  • ❤ 3
Post #7246 3.43K
🔄 Вышли Go 1.26.4 и 1.25.11 с исправлениями безопасности

Команда Go выпустила минорные версии 1.26.4 и 1.25.11. Это патч-релизы, которые закрывают три уязвимости в стандартной библиотеке. Если вы используете одну из этих веток, обновиться стоит сразу.

➡️ Проблема в пакете mime.

Метод WordDecoder.DecodeHeader имел квадратичную сложность, поэтому специально собранный заголовок с множеством некорректных encoded-word мог съесть процессор. Это CVE-2026-42504.

➡️ Уязвимость в net/textproto.

Функции пакета подставляли входные данные в текст ошибки без экранирования, а вход часто приходит от внешней стороны, например когда net/http разбирает заголовки ответа сервера.

Через это можно было протащить в чужие логи управляющие байты терминала и вводящий в заблуждение текст. Это CVE-2026-42507.

➡️ Фикс в crypto/x509.

Метод VerifyHostname вызывал strings.Split по точке в цикле для каждой записи DNS SAN, и при длинном списке имён проверка росла квадратично, причём даже для недоверенных сертификатов. Это CVE-2026-27145.

➡️ Источник

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

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

#GoLive
  • 👍 4
  • ❤ 2
Post #7245 2.96K
⚡️ Продолжаем знакомить вас с экспертами курса AgentOps!

— Сергей Нотевский расскажет, как выстроить FinOps для AI-продуктов: оптимизировать затраты на разработку и продакшен, внедрить model routing, semantic cache и систему алертов для контроля расходов
— Эмиль Сатаев разберет Context Engineering: управление контекстом, защиту от prompt injection, работу с длинными контекстами и построение безопасного пайплайна входа для AI-систем
— Михаил Бондаревский покажет, как подготовить инфраструктуру для AI-агентов: Docker, sandboxing, streaming, docker-compose и воспроизводимое окружение для разработки и продакшена
— Мурат Хажгериев расскажет про Enterprise Integrations & MCP: когда MCP действительно нужен, как подключать внешние сервисы и реализовывать интеграции с OAuth2 delegation
— Герман Сабиров разберет Governance & Compliance для AI-систем: data flow, audit logs, требования 152-ФЗ, локализацию данных и построение compliance-подхода на уровне архитектуры

Курс для backend-разработчиков, тимлидов и LLM инженеров о том, как внедрять AI-логику в бэкенд IT-продуктов и сохранять стабильность сервиса.

👉 Изучить обновленную программу AgentOps и занять место.
  • 👍 2
  • 👾 2
Post #7244 2.92K
🧑‍💻 Один middleware режет ответ в десять раз

Большой JSON без сжатия это лишние мегабайты по сети. На быстром канале незаметно, а пользователь на мобильном интернете ждёт лишние секунды. Лечится одним middleware, который сжимает ответ перед отправкой.

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

GZIP хорошо жмёт текст с повторяющимися структурами, а JSON именно такой. Клиент говорит, что умеет принимать сжатое, через заголовок Accept-Encoding: gzip. Сервер сжимает тело, ставит Content-Encoding: gzip и отдаёт. Клиент распаковывает сам, его код менять не нужно.

Middleware

Проверяем заголовок клиента, оборачиваем ResponseWriter в gzip.Writer и пропускаем через него ответ хендлера:
func gzipMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
next.ServeHTTP(w, r)
return
}

w.Header().Set("Content-Encoding", "gzip")
gz := gzip.NewWriter(w)
defer gz.Close()

gzr := gzipResponseWriter{Writer: gz, ResponseWriter: w}
next.ServeHTTP(gzr, r)
})
}

type gzipResponseWriter struct {
http.ResponseWriter
Writer *gzip.Writer
}

func (g gzipResponseWriter) Write(b []byte) (int, error) {
return g.Writer.Write(b)
}


Подключаем к нужному эндпоинту:
mux.Handle("/data", gzipMiddleware(http.HandlerFunc(dataHandler)))


Проверяем размер ответа:
curl http://localhost:8080/data --output raw.json
curl -H "Accept-Encoding: gzip" http://localhost:8080/data --output gz.json
ls -lh raw.json gz.json


Когда не стоит

Уже сжатые форматы вроде JPEG, PNG или ZIP жать заново смысла нет, размер почти не упадёт, а CPU потратите. Совсем мелкие ответы тоже лучше не трогать, накладные расходы перевесят.

GZIP в Go это одно middleware и пара заголовков. Клиент менять не надо, а текстовый ответ ужимается в разы. В продакшене удобно взять готовый gziphandler или включить сжатие на reverse proxy, но понимать, как оно устроено, полезно в любом случае.

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

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

#GoDeep
  • 👍 9
  • ❤ 3
  • 🌚 2
Post #7243 2.88K
❤️‍🔥 Причина полюбить Go

В командах время уходит не только на логику, но и на споры о стиле. Табы или пробелы, где ставить скобку, чей конфиг линтера правильнее. Ценности это не приносит, а силы на ревью отнимает. Go закрывает вопрос на уровне инструментов.

Один стиль на всех

В Go есть стандартный форматтер gofmt. Он не настраивается, опций про отступы и скобки у него нет, потому что стиль уже определён за вас. Любой код после gofmt выглядит одинаково в любом проекте. Обсуждать нечего.

Неаккуратный, но рабочий код:
func main(){
x:=10
if x>5{
fmt.Println("big")
}
}


После gofmt:
func main() {
x := 10
if x > 5 {
fmt.Println("big")
}
}


Запуск по всему проекту:
gofmt -w .


Импорты делает goimports

Рядом есть goimports. Он форматирует так же, но ещё правит импорты. Убирает то, что вы перестали использовать, и добавляет нужное. В Go это важно, потому что неиспользуемый импорт не предупреждение, а ошибка компиляции. То есть инструмент реально помогает коду собраться.
goimports -w .


Хороший опыт в редакторе

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

Похожие форматтеры есть и в других языках, тот же Prettier или Black. Разница в том, что в Go это часть стандартного подхода, а не выбор команды среди десятка настроек. Один инструмент, один стиль, ноль споров.

💬 За что вы любите Go? Подписаны на нашу рассылку?

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

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

#GoTalk
  • 👍 17
  • ❤ 7
  • 😁 2
  • 😢 1
Post #7242 3.03K
💡 Go может разрешить превращать функции в интерфейсы с одним методом

В трекере Go обсуждают предложение #47487. Оно разрешает приводить значение функции к интерфейсу с ровно одним методом, если сигнатура функции совпадает с сигнатурой этого метода. На момент обсуждения предложение помечено как likely accept. Это значит, что склонны принять, но сначала хотят прототип и эксперименты.

Сейчас, чтобы замыкание реализовало интерфейс, приходится заводить отдельный тип с методом.

Классический пример из стандартной библиотеки это http.HandlerFunc. Шаблон выглядит так:
type FooerFunc func(A, B) (C, D)

func (f FooerFunc) Foo(a A, b B) (C, D) {
return f(a, b)
}

Когда нужен, например, io.Writer, который считает записанные байты, обычно пишут структуру с полем и методом Write. Кода становится больше, чем сути, а единственная важная строка теряется среди обвязки.

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

Предложение даёт писать то же самое через приведение функции к интерфейсу напрямую:
var N int64
cw := io.Writer(func(p []byte) (n int, err error) {
n, err = os.Stdout.Write(p)
N += int64(n)
return n, err
})


Важная деталь в том, что это именно явное приведение, а не неявное присваивание. У io.Reader и io.Writer одинаковая сигнатура метода, поэтому неявное превращение было бы опасным. Явное приведение снимает двусмысленность, ведь вы сами указываете, чем должна стать функция.

По задумке компилятор сам сгенерирует скрытый именованный тип с неэкспортируемым именем, который и несёт метод. Менять reflect, инструменты или работу с type assertion при этом не нужно. Более того, функцию можно положить прямо в таблицу методов, и тогда лишних накладных расходов на обёртку не будет.

Альтернатива

В обсуждении всплыл и более общий вариант. Это интерфейсные литералы из #25860, где интерфейс собирают из нескольких замыканий, по одному на каждый метод:
cw := io.Writer{
Write: func(p []byte) (n int, err error) {
// ...
},
}

Такой вариант не ограничен одним методом, но его заметно сложнее встроить в текущий язык.

Часть участников переживает, что появится ещё один способ писать одно и то же, и это размывает консистентность Go, за которую язык и ценят. Решение пока не финальное. Если предложение примут, обвязочных типов вроде http.HandlerFunc в коде станет заметно меньше.

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

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

#GoLive
  • ❤ 4
  • 🤔 3
  • 👍 2
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 →