Можно бесконечно ходить на самые разные митапы, знакомиться с людьми… И совсем не видеть эффекта
А всё потому, что всем нам нужна не встреча на 3 часа, а постоянное и активное коммьюнити, чтобы обсуждать наболевшее. Именно это сейчас хорошо получается у AvitoTech — регулярно у них в канале видим анонсы интересных встреч, отдельных подкастов для SRE, Go-разработчиков, тим и техлидов и какой-то бесконечный поток прикладных статей и советов.
Если находили ещё что-то похожее, делитесь в комментах!
Реклама. Рекламодатель ООО «Авито Тех». erid: 2VtzqwWdsH9
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱6❤4😁1🌚1
🔄 Фиксы безопасности Go
Вышли патч-релизы Go 1.26.3 и 1.25.10. Оба содержат только security-фиксы, никаких новых фич.
Что исправили
Самая серьёзная уязвимость в
Проблема была в том, что если база контрольных сумм возвращала пустой ответ без записи о модуле, команда
Важно: установка
Если вы использовали нестандартный
Остальные исправления затрагивают стандартную библиотеку.
➡️ Источник
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
#GoLive
Вышли патч-релизы Go 1.26.3 и 1.25.10. Оба содержат только security-фиксы, никаких новых фич.
Что исправили
Самая серьёзная уязвимость в
cmd/go. Вредоносный прокси мог подсунуть изменённую версию тулчейна, обойдя проверку через базы контрольных сумм. Проблема была в том, что если база контрольных сумм возвращала пустой ответ без записи о модуле, команда
go считала валидацию успешной. Теперь проверяется наличие нужной подписи, а не просто корректность той, что пришла.Важно: установка
GOTOOLCHAIN в фиксированную версию не защищает от этой уязвимости. Нужно обновить сам базовый тулчейн.Если вы использовали нестандартный
GOPROXY, проверить, не были ли затронуты зависимости, можно так:rm go.sum && go mod tidy && go mod verify
Остальные исправления затрагивают стандартную библиотеку.
net/http/httputil: ReverseProxy мог пробрасывать параметры запроса, которые превышали лимит urlmaxqueryparams, скрывая их от функции Rewrite.net/mail: два отдельных бага в consumePhrase и consumeComment приводили к квадратичным аллокациям при парсинге email-адресов — классический вектор для DoS.net/http: HTTP/2 транспорт уходил в бесконечный цикл при получении SETTINGS_MAX_FRAME_SIZE равного нулю. Это позволяло серверу положить клиент.html/template: два XSS-бага. Первый — пустой или содержащий пробелы атрибут type ломал экранирование в блоке script. Второй — пробелы вокруг = в атрибуте мета-тега обходили URL-экранирование.net: паника в Dial и LookupPort на Windows при NUL-байте в строке. Теперь возвращается ошибка.net: двойное освобождение C-памяти при обработке очень длинного CNAME-ответа через cgo.cmd/go: команда go bug писала файлы с предсказуемыми именами во временную директорию. Атакующий мог создать симлинк и получить перезапись произвольного файла. Исправлено через os.MkdirTemp.cmd/go: go tool pack не проверял пути при извлечении архива, что позволяло записывать файлы в произвольные места. Теперь имена с компонентами пути отклоняются.📍 Навигация: Вакансии • Задачи • Собесы
#GoLive
Please open Telegram to view this post
VIEW IN TELEGRAM
❤6🔥3
На собеседованиях по Go этот вопрос про оператор
% звучит неожиданно. На всякий случай готовимся заранее:Можно ли применить оператор % к значениям типа float64?
📍 Навигация: Вакансии • Задачи • Собесы
#ReadySetGo
Please open Telegram to view this post
VIEW IN TELEGRAM
cobra встречается почти в каждом Go-проекте с CLI. Но для большинства инструментов он создаёт лишнюю сложность там, где её не нужно.Чтобы добавить один подкоманд в Cobra, нужно пройти пять шагов: создать
*cobra.Command, настроить RunE, зарегистрировать флаги на команде, добавить команду в корневую, вызвать Execute(). Флаги регистрируются через init(), что делает порядок инициализации неочевидным.Что использовать вместо
github.com/urfave/cli/v3 делает то же самое, но проще. Команды — это обычные структуры с полем Action. Флаги объявляются рядом с командой. Никакого глобального состояния, никакой рефлексии, никакого init().Пример CLI для аудита AWS-ресурсов:
app := &cli.App{
Commands: []*cli.Command{
{
Name: "audit",
Usage: "Audit AWS resources",
Flags: []cli.Flag{
&cli.StringFlag{Name: "region", Value: "us-east-1"},
},
Action: func(c *cli.Context) error {
return runAudit(c.String("region"))
},
},
},
}Структура сама по себе является документацией. Всё явно, всё рядом.
Когда Cobra всё же оправдан
Для действительно крупных CLI с десятками подкоманд, сложными деревьями команд и автогенерацией документации Cobra может быть уместен. Для типичных утилит, деплой-скриптов и DevOps-инструментов
urfave/cli/v3 справится чище.📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10😁5❤2🌚2
Если вы пишете на Go и вам нужна низкоуровневая работа с памятью или вызов C-библиотек без оверхеда от Cgo — Solod именно для этого.
Что это такое
Solod (сокращённо
So) — системный язык программирования. Синтаксис почти полностью совпадает с Go, но код транспилируется в C и компилируется в нативный бинарник через GCC или Clang. Никакого рантайма, никакого GC, никаких пауз.Выпущена версия v0.1 с портированной стандартной библиотекой Go и улучшенным интеропом с C.
Портированы пакеты из стандартной библиотеки Go:
io, bufio, fmt — ввод-вывод общего назначенияbytes, strings, strconv, unicode/utf8 — работа со строками и байтамиslices, maps — обобщённые структуры данныхcrypto/rand, math/rand — генерация случайных данныхflag, os, path — CLI и файловая системаlog/slog — структурированные логиtime — работа со временемПлюс два собственных пакета:
mem — управление памятью с подключаемыми аллокаторами, и c — хелперы для интеропа с C.Как выглядит интероп с C
Вот как подключить SQLite через его C API с помощью директивы
so:include и инструмента sobind:package main
import "solod.dev/so/c"
//so:include <sqlite3.h>
//so:extern SQLITE_OK
const sqliteOK = 0
//so:extern
type sqlite3 struct{}
func sqlite3_open(filename string, ppDb **sqlite3) int32
func sqlite3_exec(arg0 *sqlite3, sql string, ...) int32
Функции без тела транспилятор сам считает extern-декларациями. Go-строка в вызове
sqlite3_exec автоматически превращается в const char* — без ручного преобразования.Память: явная, но управляемая
Подход к аллокациям похож на Zig — всё выделяется явно через интерфейс
mem.Allocator. Например, при чтении значения из SQLite:name, err := m.Get(mem.System, "name")
// ...
mem.FreeString(mem.System, name)
Можно использовать
mem.System (libc malloc/free) или арену на стеке:var buf [1024]byte
arena := mem.NewArena(buf[:])
name, _ := m.Get(&arena, "name")
Нет пауз GC, нет моста Cgo при вызове C-библиотек. Платить за это приходится ручным управлением памятью.
Solod v0.1 подходит для домашних проектов и CLI-утилит — пакет
flag работает так же, как в Go. Примеры cat, head, sort, wc есть в репозитории.📍 Навигация: Вакансии • Задачи • Собесы
#GoLive
Please open Telegram to view this post
VIEW IN TELEGRAM
❤13👍7😁2👾1
This media is not supported in your browser
VIEW IN TELEGRAM
Тема: Почему AI-продукты на базе LLM ломаются и как сделать, чтобы работало.
В кружке выше Эмиль Сатаев рассказал, какие именно проблемы с LLM в проде будем разбирать.
Что в программе:
- Разберем реальные кейсы стартапов и ограничения LLM.
- Обсудим рабочие архитектуры: RAG, human-in-the-loop, контроль качества.
- Ответим на ваши вопросы и разберем кейсы участников.
🎁 Бонусы: в конце вебинара подарим промокод на скидку 10.000 ₽ на курсы и разыграем подписки на полезные AI-сервисы.
👉 Зарегистрироваться на вебинар
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱3🔥1
Когда пишете GraphQL API на Go, рано или поздно сталкиваетесь с проблемой N+1. Резолвер запрашивает пользователя для каждого поста отдельно — и вместо одного запроса к базе получаете сотню.
graph-gophers/dataloader решает именно это.Что делает библиотека
Она собирает отдельные запросы в батч и выполняет их одним вызовом. Параллельно кэширует результаты на время жизни загрузчика. Это реализация Facebook DataLoader для Go, с поддержкой дженериков начиная с v7.
Как это работает
Вы описываете одну батч-функцию, которая принимает список ключей и возвращает список результатов. Загрузчик сам накапливает ключи из параллельных вызовов
Load(), вызывает вашу функцию один раз и раздаёт ответы обратно через thunk.Thunk — это функция, которую возвращает
Load(). Она блокирует выполнение до тех пор, пока батч не завершится, и отдаёт значение или ошибку.Пример использования
Допустим, нужно загружать пользователей по ID. Без батчинга каждый резолвер делает свой запрос. С
dataloader все запросы в рамках одного окна объединяются:batchFn := func(ctx context.Context, ids []int) []*dataloader.Result[*User] {
users := fetchUsersByIDs(ctx, ids) // один запрос к БД
results := make([]*dataloader.Result[*User], len(ids))
for i, id := range ids {
results[i] = &dataloader.Result[*User]{Data: users[id]}
}
return results
}
loader := dataloader.NewBatchedLoader(batchFn)
// вызывается из разных горутин или резолверов
thunk := loader.Load(ctx, 42)
user, err := thunk()Все вызовы
Load() в одном временном окне попадут в один батч — fetchUsersByIDs выполнится один раз.По умолчанию используется встроенный in-memory кэш. Он рассчитан на короткий цикл жизни — один HTTP-запрос. Если кэш не нужен совсем, есть
NoCache:loader := dataloader.NewBatchedLoader(batchFn, dataloader.WithCache(&dataloader.NoCache[int, *User]{}))Можно передать свою реализацию интерфейса
Cache, например, на основе Redis.Если вы пишете GraphQL-сервер на Go и у вас есть резолверы, которые загружают связанные сущности — библиотека избавит от N+1 без ручного переписывания логики. Работает и без GraphQL: подходит для любых сценариев, где нужно объединять параллельные запросы за данными.
📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
viper — самая частая рекомендация для конфигурации в Go. Он умеет читать файлы, переменные окружения, удалённые источники и устанавливать дефолты. Звучит удобно, но на практике создаёт проблемы.В чём проблема
Viper тянет большое дерево зависимостей. Методы
GetString, GetInt и подобные не типизированы — значения возвращаются в рантайме без проверки на этапе компиляции. Ключи конфига разбросаны по коду как строковые литералы. При нетривиальном использовании поведение при слиянии и приоритетах конфигов требует чтения исходников.Что использовать
Структура с тегами и
yaml.NewDecoder:type Config struct {
DatabaseURL string `yaml:"database_url"`
Kafka KafkaConfig `yaml:"kafka"`
Server ServerConfig `yaml:"server"`
}
func Load(path string) (*Config, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
var cfg Config
if err := yaml.NewDecoder(f).Decode(&cfg); err != nil {
return nil, err
}
return &cfg, nil
}Переменные окружения добавляются вручную через
os.Getenv там, где это нужно.Что получаем
Полностью типизированный конфиг. Никаких строковых ключей. Граф зависимостей, который можно проверить за секунды. Код, который вы полностью контролируете.
Для JSON-конфига подход тот же, только с
encoding/json. Пакет gopkg.in/yaml.v3 — единственная внешняя зависимость, и та минимальная.📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12👾3
Библиотека Go-разработчика | Golang
📍 Навигация: Вакансии • Задачи • Собесы 🐸 Библиотека Go-разработчика #GoGiggle
📰 Дайджест недели
Кто прочитал, тот завтра на работу не пойдёт!
— ИИ-интервью, архетипы египетских богов и симуляция роли гендиректора
— oapi-codegen v2.7.0
— Почтовый клиент прямо в терминале
— Вышли патч-релизы Go 1.26.3 и 1.25.10
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
#GoLive
Кто прочитал, тот завтра на работу не пойдёт!
— ИИ-интервью, архетипы египетских богов и симуляция роли гендиректора
— oapi-codegen v2.7.0
— Почтовый клиент прямо в терминале
— Вышли патч-релизы Go 1.26.3 и 1.25.10
📍 Навигация: Вакансии • Задачи • Собесы
#GoLive
Please open Telegram to view this post
VIEW IN TELEGRAM
🛠 mise для Go-разработчиков: версии, окружение и задачи в одном файле
В Go-проектах часто встречается такая ситуация: один сервис требует Go 1.21, другой уже на 1.22, а на CI стоит что-то третье. Переключаться руками неудобно,
Установка
Для zsh и fish команда активации аналогична, только меняется шелл.
Пример mise.toml для Go-проекта
Установить Go нужной версии и сразу запустить сборку:
Как переключаться между версиями Go
Глобально поставить одну версию, а в конкретном проекте использовать другую:
Внутри папки проекта это переопределяется через
Проверить, какая версия активна прямо сейчас:
Почему это лучше Makefile
С
Если в проекте несколько сервисов
➡️ Репозиторий
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
#GoToProduction
В Go-проектах часто встречается такая ситуация: один сервис требует Go 1.21, другой уже на 1.22, а на CI стоит что-то третье. Переключаться руками неудобно,
GOPATH и GOROOT легко запутать, а Makefile со временем превращается в набор магических команд, которые понимает только автор.mise решает это без лишних движений: один файл mise.toml в корне проекта фиксирует версию Go, переменные окружения и команды сборки.Установка
curl https://mise.run | sh
echo 'eval "$(~/.local/bin/mise activate bash)"' >> ~/.bashrc
Для zsh и fish команда активации аналогична, только меняется шелл.
Пример mise.toml для Go-проекта
[tools]
go = "1.22"
[env]
GOFLAGS = "-mod=vendor"
APP_ENV = "development"
DATABASE_URL = "postgres://localhost/myapp_dev"
[tasks.build]
description = "Собрать бинарник"
run = "go build -o bin/app ./cmd/app"
[tasks.test]
description = "Запустить тесты"
run = "go test ./..."
[tasks.lint]
description = "Запустить golangci-lint"
run = "golangci-lint run"
[tasks.run]
description = "Запустить сервер"
depends = ["build"]
run = "./bin/app"
Установить Go нужной версии и сразу запустить сборку:
mise install
mise run build
Как переключаться между версиями Go
Глобально поставить одну версию, а в конкретном проекте использовать другую:
mise use --global go@1.22
Внутри папки проекта это переопределяется через
mise.toml. mise сам выставит правильный PATH при входе в директорию — никаких export GOROOT руками.Проверить, какая версия активна прямо сейчас:
mise current go
Почему это лучше Makefile
Makefile не знает ничего про версии инструментов и переменные окружения. Разработчик клонирует репозиторий, запускает make build и получает ошибку, потому что у него Go 1.20, а проект требует 1.22.С
mise картина другая: mise install сам скачивает нужную версию Go, выставляет переменные и после этого mise run build работает так же, как на машине соседа и на CI.Если в проекте несколько сервисов
mise поддерживает вложенные конфиги. В каждом подкаталоге со своим mise.toml будет своя версия Go и своё окружение. Глобальный конфиг при этом остаётся как запасной вариант.📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤3👾1
🐳 Контейнер от root — это не норма
Пользователь в Docker-контейнере по умолчанию root.
Если сервис скомпрометирован и атакующий получает доступ внутрь контейнера, запуск от root существенно расширяет поверхность атаки.
Как исправить
В distroless-образах пользователь
В Alpine-образах пользователя нужно создать вручную:
Дополните защиту read-only корневой файловой системой — в pod spec Kubernetes или в Docker Compose:
Это лишает атакующего возможности записывать файлы в контейнер даже при наличии RCE-уязвимости.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
#GoDeep
Пользователь в Docker-контейнере по умолчанию root.
Если сервис скомпрометирован и атакующий получает доступ внутрь контейнера, запуск от root существенно расширяет поверхность атаки.
Как исправить
В distroless-образах пользователь
nonroot уже есть из коробки:FROM gcr.io/distroless/static-debian12
COPY --from=builder /app/server /server
USER nonroot:nonroot
CMD ["/server"]
В Alpine-образах пользователя нужно создать вручную:
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
Дополните защиту read-only корневой файловой системой — в pod spec Kubernetes или в Docker Compose:
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
Это лишает атакующего возможности записывать файлы в контейнер даже при наличии RCE-уязвимости.
📍 Навигация: Вакансии • Задачи • Собесы
#GoDeep
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8❤2👍2
Миграции в проекте рано или поздно превращаются в хаос. Кто-то забыл накатить скрипт, у кого-то локально другая версия схемы, а в проде вообще непонятно что.
goose — это инструмент для миграций на Go, который работает и как CLI, и как библиотека.Что умеет
goose поддерживает Postgres, MySQL, SQLite, ClickHouse, MSSQL, Spanner, YDB и ещё несколько баз. Миграции пишутся на SQL или на Go. Есть поддержка embed.FS, чтобы вшивать миграции прямо в бинарник. Можно применять миграции не по порядку, подставлять переменные окружения в SQL-файлы и запускать отдельные миграции без версионирования.
Если бинарник получается слишком большим, можно собрать без лишних драйверов:
go build -tags='no_postgres no_mysql no_sqlite3 no_ydb' -o goose ./cmd/goose
Как работать
Создаём первую миграцию:
goose create add_users_table sql
Goose сгенерирует файл вида
20250511120000_add_users_table.sql. Внутри два блока:-- +goose Up
CREATE TABLE users (
id int NOT NULL,
name text,
email text,
PRIMARY KEY(id)
);
-- +goose Down
DROP TABLE users;
Всё, что после
-- +goose Up, выполняется при накатывании миграции. Всё после -- +goose Down — при откате.Применяем все миграции:
goose postgres "user=postgres dbname=mydb sslmode=disable" up
Откатываем последнюю:
goose postgres "user=postgres dbname=mydb sslmode=disable" down
Смотрим статус:
goose postgres "user=postgres dbname=mydb sslmode=disable" status
Чтобы не передавать драйвер и строку подключения каждый раз, можно задать переменные окружения или положить их в
.env файл:GOOSE_DRIVER=postgres
GOOSE_DBSTRING=postgres://admin:admin@localhost:5432/mydb
GOOSE_MIGRATION_DIR=./migrations
Миграции на Go
Если SQL не хватает, миграции можно писать на Go:
package migrations
import (
"database/sql"
"github.com/pressly/goose/v3"
)
func init() {
goose.AddMigration(Up, Down)
}
func Up(tx *sql.Tx) error {
_, err := tx.Exec("UPDATE users SET username='admin' WHERE username='root';")
return err
}
func Down(tx *sql.Tx) error {
_, err := tx.Exec("UPDATE users SET username='root' WHERE username='admin';")
return err
}
Встраивание миграций в бинарник
С Go 1.16 можно вшить SQL-файлы миграций прямо в приложение через
embed.FS:package main
import (
"database/sql"
"embed"
"github.com/pressly/goose/v3"
)
//go:embed migrations/*.sql
var embedMigrations embed.FS
func main() {
var db *sql.DB
// настройка подключения к БД
goose.SetBaseFS(embedMigrations)
if err := goose.SetDialect("postgres"); err != nil {
panic(err)
}
if err := goose.Up(db, "migrations"); err != nil {
panic(err)
}
}
📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🔥5