🧑💻 Билдеры, которые не ломаются при встраивании
Продолжаем разбирать
Какую боль это решает
У билдера в Go давнее противоречие. Чтобы работала цепочка
Как это работает
Ограничение задаёт набор методов, а параметр типа
На вызове конкретный тип остаётся живым, поэтому специфичный метод вроде
Здесь важно разделение двух задач. Интерфейс задаёт ограничение, какие методы обязаны быть. Параметр типа несёт идентичность, какой именно конкретный билдер вернётся. Передача обеих задач одной переменной типа как раз и заставляет паттерн работать.
Самоссылка снимает старый компромисс между цепочкой вызовов и расширением билдера. Общие настройки выносятся в одну обобщённую функцию, а конкретный тип переживает всю цепочку, поэтому доменные методы остаются под рукой без приведений. Для конфигураторов, клиентов и любых флюентных API это делает код короче и безопаснее на этапе компиляции.
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
#GoToProduction
Продолжаем разбирать
[T Constraint[T]] в объявлениях обобщённых типов. Это чинит старую проблему флюентных билдеров, где цепочку вызовов ломало встраивание.Какую боль это решает
У билдера в Go давнее противоречие. Чтобы работала цепочка
.WithX().WithY().Build(), каждый метод возвращает *Builder. Пока билдер один, всё в порядке. Но как только вы расширяете его через встраивание, цепочка рвётся. Метод встроенного типа возвращает встроенный тип, а не внешний, поэтому после первого же вызова вы теряете доступ к методам расширения. Приходится либо дублировать методы, либо приводить типы вручную.Как это работает
Ограничение задаёт набор методов, а параметр типа
B несёт конкретный тип билдера через всю цепочку:type Builder[B any] interface {
WithTimeout(d time.Duration) B
WithRetry(attempts int, backoff time.Duration) B
Build() (Config, error)
}
// ApplyDefaults возвращает B, а не Builder[B].
func ApplyDefaults[B Builder[B]](b B, env Environment) B {
b = b.WithTimeout(env.DefaultTimeout())
b = b.WithRetry(env.MaxRetries(), env.RetryBackoff())
return b
}На вызове конкретный тип остаётся живым, поэтому специфичный метод вроде
WithHeader доступен прямо в цепочке:client, err := ApplyDefaults(NewHTTPClientBuilder("https://api.internal"), prodEnv).
WithHeader("X-Service-ID", serviceID). // всё ещё доступно
Build()Здесь важно разделение двух задач. Интерфейс задаёт ограничение, какие методы обязаны быть. Параметр типа несёт идентичность, какой именно конкретный билдер вернётся. Передача обеих задач одной переменной типа как раз и заставляет паттерн работать.
Самоссылка снимает старый компромисс между цепочкой вызовов и расширением билдера. Общие настройки выносятся в одну обобщённую функцию, а конкретный тип переживает всю цепочку, поэтому доменные методы остаются под рукой без приведений. Для конфигураторов, клиентов и любых флюентных API это делает код короче и безопаснее на этапе компиляции.
📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤2
📍 Навигация: Вакансии • Задачи • Собесы
#GoVisual
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤5⚡2
🧑💻 Четыре года разработки без документации
Код собирался. Тесты проходили. Но никто в команде не мог объяснить, зачем существуют три пакета. Инженер получил сервис на 47 000 строк Go. Зелёный CI, четыре года в проде, последние 14 месяцев без серьёзных инцидентов.
По всем видимым признакам здоровый проект. Но через три недели у него был список вещей, которые никто не мог объяснить. Не из-за слабой команды, а потому что люди, знавшие ответы, ушли, и ответы ушли вместе с ними.
Долг был не в качестве кода, а в понимании
Здесь ломается привычное определение. Обычно техдолг это плохой код. Спагетти, нет тестов, устаревшие зависимости. Но в этом проекте покрытие было 71%, стиль ровный, зависимости под контролем, сборка чистая. Метрики говорили, что всё в порядке.
Кодовая база это запись решений. Каждая граница пакета, каждый интерфейс, каждый слой абстракции когда-то были решением, принятым в конкретном контексте. Четыре года спустя решения остались, а причины исчезли.
Работать с кодом, чью логику структуры вы не можете восстановить, это не то же самое, что работать с плохим кодом. Вы не отличите «так сделано по хорошей причине, которую я пока не понял» от «так сделано из-за ограничения, которого давно нет». Каждое изменение несёт эту неопределённость.
➡️ Три пакета, которые никто не смог объяснить
➡️ Зелёные тесты врут
Тесты проверяют поведение. Они не проверяют, что поведение вообще нужно, что абстракция под ним правильная, что это тот код, который должен решать задачу. У
Вот показательный кусок. У этой функции было четыре теста, все зелёные, но никто не знал, что значит «legacy» в контексте:
Комментарию TODO было два года. Миграция, на которую он ссылался, нигде не задокументирована.
Налог на онбординг
Время на эту археологию не бесплатное. По заметкам автора счёт вышел такой:
• 11 часов на три загадочных пакета
• 9 часов на разбор кэша против Redis
• 7 часов на цепочку middleware, которая протаскивала значение сквозь четыре слоя ради трейсинга. Библиотеку трейсинга заменили годами раньше, а значение осталось
• 4 часа на разбор
Итого около 31 часа, три четверти рабочей недели. И этот счёт оплатит заново каждый следующий инженер, который придёт в проект.
➡️ Оригинал
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
#GoDeep
Код собирался. Тесты проходили. Но никто в команде не мог объяснить, зачем существуют три пакета. Инженер получил сервис на 47 000 строк Go. Зелёный CI, четыре года в проде, последние 14 месяцев без серьёзных инцидентов.
По всем видимым признакам здоровый проект. Но через три недели у него был список вещей, которые никто не мог объяснить. Не из-за слабой команды, а потому что люди, знавшие ответы, ушли, и ответы ушли вместе с ними.
Долг был не в качестве кода, а в понимании
Здесь ломается привычное определение. Обычно техдолг это плохой код. Спагетти, нет тестов, устаревшие зависимости. Но в этом проекте покрытие было 71%, стиль ровный, зависимости под контролем, сборка чистая. Метрики говорили, что всё в порядке.
Кодовая база это запись решений. Каждая граница пакета, каждый интерфейс, каждый слой абстракции когда-то были решением, принятым в конкретном контексте. Четыре года спустя решения остались, а причины исчезли.
Работать с кодом, чью логику структуры вы не можете восстановить, это не то же самое, что работать с плохим кодом. Вы не отличите «так сделано по хорошей причине, которую я пока не понял» от «так сделано из-за ограничения, которого давно нет». Каждое изменение несёт эту неопределённость.
sync2 повторял куски x/sync из стандартной библиотеки. Семафоры, errgroup, пул. Из 11 мест импорта семь тянули только Pool, у которого в стандартной библиотеке есть почти идентичный sync.Pool. Скорее всего, когда сервис писали, стандартных примитивов не хватало, sync2 был затычкой. Потом стандартная библиотека догнала, а затычка осталась.cache это локальный кэш в памяти с TTL. При этом в сервисе был Redis. Часть данных шла в Redis, часть в локальный кэш, часть в оба, и нигде не было записано, что куда должно идти. По коммитам картина восстанавливается. Redis добавили на втором году из-за проблемы с задержками, а миграция осталась частичной.transport определял кастомный HTTP-транспорт с ретраями и настройкой пула. Исходящих вызовов было три, два использовали transport, один обычный http.DefaultClient. Никакой причины для разницы. Это было не решение, а накопление. Разные инженеры добавляли вызовы, кто-то не знал про transport, кто-то не посчитал, что он подходит.Тесты проверяют поведение. Они не проверяют, что поведение вообще нужно, что абстракция под ним правильная, что это тот код, который должен решать задачу. У
transport было 23 теста, все проходили. Они проверяли, что ретраи работают и пул настраивается. Они не проверяли, нужен ли этот транспорт вообще.Вот показательный кусок. У этой функции было четыре теста, все зелёные, но никто не знал, что значит «legacy» в контексте:
func (c *Client) fetchWithLegacyFallback(ctx context.Context, req Request) (*Response, error) {
resp, err := c.primary.Fetch(ctx, req)
if err == nil {
return resp, nil
}
// If primary fails, try legacy endpoint
// TODO: remove when migration complete
return c.legacy.Fetch(ctx, req)
}Комментарию TODO было два года. Миграция, на которую он ссылался, нигде не задокументирована.
legacy-клиент всё ещё вызывался при каждой ошибке. Никто не знал, завершилась миграция или идёт до сих пор.Налог на онбординг
Время на эту археологию не бесплатное. По заметкам автора счёт вышел такой:
• 11 часов на три загадочных пакета
• 9 часов на разбор кэша против Redis
• 7 часов на цепочку middleware, которая протаскивала значение сквозь четыре слоя ради трейсинга. Библиотеку трейсинга заменили годами раньше, а значение осталось
• 4 часа на разбор
utils/Итого около 31 часа, три четверти рабочей недели. И этот счёт оплатит заново каждый следующий инженер, который придёт в проект.
📍 Навигация: Вакансии • Задачи • Собесы
#GoDeep
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4😁4🤩1👾1
Снова поговорим про
[T Constraint[T]], ведь оно работает и для объявлений обобщённых типов. Это удобно для коллекций, которые сортируют доменные типы по их собственному правилу сравнения.Стандартный
constraints.Ordered покрывает числа и строки, но не богатые доменные типы вроде Priority, SemanticVersion или Money. Для них порядок задаётся своей логикой, а не оператором <. Обычно это решают через sort.Slice с замыканием на каждый вызов или через хранение значений как interface{} с приведением при каждом сравнении. Первый вариант рассыпает логику сравнения по коду, второй теряет типобезопасность и добавляет работу рантайму.Ограничение требует, чтобы тип умел сравнивать себя с себе подобным:
type Ordered[T any] interface {
Less(other T) bool
}
// T обязан реализовать Less(T) bool.
type SortedSlice[T Ordered[T]] struct {
items []T
}
func (s *SortedSlice[T]) Insert(v T) {
i := sort.Search(len(s.items), func(i int) bool {
return !s.items[i].Less(v)
})
s.items = append(s.items, v)
copy(s.items[i+1:], s.items[i:])
s.items[i] = v
}Доменный тип реализует
Less для себя:type Priority int
func (p Priority) Less(other Priority) bool {
return p < other
}
set := &SortedSlice[Priority]{}
set.Insert(High)
set.Insert(Low)
SortedSlice[Priority] на этапе компиляции знает, что хранит именно Priority. Вызов .Less прямой, без интерфейсной прослойки в рантайме. Если попытаться вставить тип, который не реализует Ordered[T] для себя, компилятор отклонит его прямо на месте вызова, а не оставит падать под нагрузкой.Паттерн переносит правило сравнения внутрь самого типа и даёт коллекцию, которая держит порядок для любого доменного значения. Логика
Less живёт в одном месте, типобезопасность сохраняется, приведений нет. Для приоритетных очередей, версий и денежных сумм это чище, чем замыкания на каждый вызов сортировки.📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
1❤8👍3🔥2
Широкая вилка 250–450, а на руки 270? Бонусы до 100%, которых никто в глаза не видел? Оффер, который «горит до конца дня»? Всё это не совпадения, а рабочие схемы, на которых компании экономят прямо сейчас.
👉 Читайте, пока вам снова не пообещали «пересмотрим через полгода»
📍 Навигация: Вакансии • Задачи • Собесы
Please open Telegram to view this post
VIEW IN TELEGRAM
❤1
Снова про
[T Constraint[T]]. На этом можно построить событийную шину, где обработчик получает конкретный тип события, а не interface{}.Событийные системы в Go обычно строят одним из двух способов. Либо типизированные каналы, и тогда нужен отдельный канал на каждый тип события, что быстро разрастается. Либо диспатч через
interface{} с приведением типа в каждом обработчике. Второй путь гибче, но каждое приведение это место, где код упадёт в рантайме, если тип не совпал. Компилятор при этом молчит.Как это работает
Событие несёт свой тип полезной нагрузки через самоссылку, а обработчик и шина параметризуются этим же типом:
type Event[T any] interface {
EventType() string
Payload() T
}
type Handler[T Event[T]] func(ctx context.Context, event T) error
type TypedBus[T Event[T]] struct {
mu sync.RWMutex
handlers []Handler[T]
}
func (b *TypedBus[T]) Subscribe(h Handler[T]) {
b.mu.Lock()
b.handlers = append(b.handlers, h)
b.mu.Unlock()
}
func (b *TypedBus[T]) Publish(ctx context.Context, event T) error {
b.mu.RLock()
handlers := b.handlers
b.mu.RUnlock()
for _, h := range handlers {
if err := h(ctx, event); err != nil {
return err
}
}
return nil
}На стороне вызова обработчик получает конкретный тип, а не пустой интерфейс:
bus := &TypedBus[*UserEvent]{}
bus.Subscribe(func(ctx context.Context, e *UserEvent) error {
log.Println(e.UserID) // реальные поля, без приведения
return nil
})Добавить новый тип события значит создать новый
TypedBus[NewEventType]. Если типы обработчика и шины не сойдутся, компилятор сообщит об этом до выката, текстом вроде *UserEvent does not implement Event[*UserEvent]. Сообщение разбирается не с первого взгляда, но оно появляется на сборке, а не в проде.Самоссылка даёт третий путь между отдельным каналом на каждый тип и универсальным диспатчем через
interface{}. Обработчик работает с настоящими полями события, а несоответствие типов ловится компилятором. Для событийных систем это убирает целую категорию рантайм-багов с приведением типов и делает добавление нового события предсказуемым.📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9
🔥 ИИ-выходные: Собери своего AI-агента за 2 дня
Хватит просто читать про ИИ, пора собирать автономные системы руками! 1–2 августа пройдет хардкорный онлайн-интенсив для junior-middle разработчиков.
Формат: теория ➡️ сразу практика. Вы будете управлять AI-инструментами.
🛠️ Как всё пройдет:
Для кого: Вы пишете на Python, работаете с Git и терминалом. (С нуля не подойдет, темп очень быстрый!)
Артефакт на выходе: Рабочий репозиторий с вашим агентом, который не стыдно показать команде.
👨💻 Спикер: Алексей Жиряков (Сбер, GenAI).
Места ограничены!
👉 Изучить программу и занять место
Хватит просто читать про ИИ, пора собирать автономные системы руками! 1–2 августа пройдет хардкорный онлайн-интенсив для junior-middle разработчиков.
Формат: теория ➡️ сразу практика. Вы будете управлять AI-инструментами.
🛠️ Как всё пройдет:
День 1 (Суббота): разбираем анатомию агента, подключаем инструменты (shell, GitHub, Postgres) и дебажим трейсы.
День 2 (Воскресенье): собираем собственного агента под вашу задачу, настраиваем Eval + Guardrails (чтобы бот не галлюцинировал) и проводим демо.
Для кого: Вы пишете на Python, работаете с Git и терминалом. (С нуля не подойдет, темп очень быстрый!)
Артефакт на выходе: Рабочий репозиторий с вашим агентом, который не стыдно показать команде.
👨💻 Спикер: Алексей Жиряков (Сбер, GenAI).
Места ограничены!
👉 Изучить программу и занять место
🧑💻 Как не оставить археологию после себя
В прошлом посте была история про кодовую базу на Go, где код собирался и тесты проходили, но никто не мог объяснить, зачем половина всего этого существует. Такой долг это не грязный код, а решения, чья причина потерялась. Здесь разберём, что с ним делать и как писать, чтобы не плодить его самому.
Что автор сделал с унаследованным кодом
Он не стал чистить всё подряд, потому что 47 000 строк за пару недель не разберёшь параллельно с обычными задачами. Подход был такой.
• Middleware с мёртвым значением. Удалил сразу. ID запроса протаскивался сквозь четыре слоя ради библиотеки трейсинга, которой давно нет. Правка маленькая и однозначная, поведение не меняется.
• Несогласованность
• Избыточность
• Разделение
Это честный подход к чужому долгу. Чините то, в чём уверены. Документируйте то, в чём нет. И не поддавайтесь желанию вычистить всё, потому что чистка без понимания создаёт новый долг быстрее, чем гасит старый.
Пишите комментарии про «почему», а не про «что»
Go читается достаточно, чтобы понять, что делает код. Ценность в комментарии, который объясняет, почему выбран этот путь, на какое ограничение он отвечает и что должно измениться, чтобы подход перестал быть нужным.
Вот как выглядел бы тот самый legacy-фолбэк, если бы автор оставил после себя контекст:
Такой комментарий пишется три минуты и экономит полчаса тому, кто откроет функцию следующим.
Ставьте дату у технических решений
Комментарий
Трение на онбординге это сигнал долга
Если новому инженеру нужно больше дня, чтобы разобраться в пакете или границе системы, это трение несёт информацию. Код, в который тяжелее всего въехать, обычно и есть тот, где накопилось больше всего необъяснённых решений.
Убирайте абстракции, которые не можете объяснить
Если вы не можете в одном-двух предложениях сказать, зачем существует абстракция и какую проблему она решает, это пассив. Либо разберитесь и задокументируйте, либо удалите. Абстракция без объяснения это цена, которую платят при каждом следующем контакте с кодом.
Самое интересное в той истории было не то, что нашлась необъяснимая часть. А то, что разбор этой части показал историю системы. Где она стабильна, где её латали много раз, какие компромиссы в архитектуре были сделаны осознанно, а какие просто накопились. Чистая кодовая база такого знания не даёт. Археология медленная, но уроки складываются.
➡️ Оригинал
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
#GoToProduction
В прошлом посте была история про кодовую базу на Go, где код собирался и тесты проходили, но никто не мог объяснить, зачем половина всего этого существует. Такой долг это не грязный код, а решения, чья причина потерялась. Здесь разберём, что с ним делать и как писать, чтобы не плодить его самому.
Что автор сделал с унаследованным кодом
Он не стал чистить всё подряд, потому что 47 000 строк за пару недель не разберёшь параллельно с обычными задачами. Подход был такой.
• Middleware с мёртвым значением. Удалил сразу. ID запроса протаскивался сквозь четыре слоя ради библиотеки трейсинга, которой давно нет. Правка маленькая и однозначная, поведение не меняется.
• Несогласованность
transport. Задокументировал, не трогал. Чтобы починить, нужно понять исходные настройки ретраев и актуальны ли они. Вместо правки он написал в пакете подробный комментарий. Что это, когда сделано, под что настроено, какой вопрос открыт. • Избыточность
sync2. Убрал частично. Четыре из семи мест с Pool спокойно заменялись на sync.Pool. Три остальных имели тонкие отличия, в которых он не был уверен. Убрал те, где был уверен, остальные пометил. • Разделение
cache и Redis. Оставил как есть. Самый запутанный кусок с наименьшим пониманием. Менять поведение, не зная, зачем оно, слишком рискованно.Это честный подход к чужому долгу. Чините то, в чём уверены. Документируйте то, в чём нет. И не поддавайтесь желанию вычистить всё, потому что чистка без понимания создаёт новый долг быстрее, чем гасит старый.
Пишите комментарии про «почему», а не про «что»
Go читается достаточно, чтобы понять, что делает код. Ценность в комментарии, который объясняет, почему выбран этот путь, на какое ограничение он отвечает и что должно измениться, чтобы подход перестал быть нужным.
Вот как выглядел бы тот самый legacy-фолбэк, если бы автор оставил после себя контекст:
// fetchWithLegacyFallback существует, потому что основной эндпоинт
// был нестабилен во время миграции БД в 2021 году.
// Legacy-эндпоинт только на чтение и медленнее, но стабилен.
//
// Статус (2023-06). Миграция завершена, но снятие фолбэка требует
// проверки, что стабильность основного эндпоинта закрепилась
// на новой инфраструктуре. Отслеживается в issue #1847.
func (c *Client) fetchWithLegacyFallback(ctx context.Context, req Request) (*Response, error) {
Такой комментарий пишется три минуты и экономит полчаса тому, кто откроет функцию следующим.
Ставьте дату у технических решений
Комментарий
// TODO: remove when migration complete почти бесполезен, если не знать, когда он написан. Вариант // TODO (2021-03): убрать после миграции, tracked in #1204 говорит, когда решение принято и где искать контекст. Через два года вы просто проверяете, закрыт ли issue #1204.Трение на онбординге это сигнал долга
Если новому инженеру нужно больше дня, чтобы разобраться в пакете или границе системы, это трение несёт информацию. Код, в который тяжелее всего въехать, обычно и есть тот, где накопилось больше всего необъяснённых решений.
Убирайте абстракции, которые не можете объяснить
Если вы не можете в одном-двух предложениях сказать, зачем существует абстракция и какую проблему она решает, это пассив. Либо разберитесь и задокументируйте, либо удалите. Абстракция без объяснения это цена, которую платят при каждом следующем контакте с кодом.
Самое интересное в той истории было не то, что нашлась необъяснимая часть. А то, что разбор этой части показал историю системы. Где она стабильна, где её латали много раз, какие компромиссы в архитектуре были сделаны осознанно, а какие просто накопились. Чистая кодовая база такого знания не даёт. Археология медленная, но уроки складываются.
📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4
Представьте: есть слайс на 10 MB, а нужны только первые 5 байт. Вы пишете
bigSlice = bigSlice[:5] и идёте дальше. Освободит ли сборщик мусора оставшуюся память после этой операции?Подсказка
[:5]📍 Навигация: Вакансии • Задачи • Собесы
#ReadySetGo
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🥱1
🤨 Как заставить AI-агента не ломать архитектуру, а нормально писать код?
Мы привыкли общаться с ИИ промптами, но для автономных систем это путь к бесконечным циклам и сливу бюджета. Чтобы убрать хаос, инженеры переходят на Spec-Driven Development (SDD).
Вот как этот подход меняет работу агента на практике:
🔹Контракт вместо текста. Сначала пишется строгая спецификация (JSON-схема/OpenAPI) и автотесты. Агент зажат в рамки интерфейсов, за которые физически не может выйти.
🔹Контроль на шагах. Внутри петли Think-Act-Observe агент сверяет действия со спецификацией.
🔹Саморефлексия. Если ИИ нарушил типы или «додумал» лишнее, тест падает. Агент получает ошибку в контекст и сам правит код, не выходя за рамки ТЗ.
Это лишь база того, как укротить ИИ-разработку. Если вы хотите глубоко внедрить эту методологию, научиться проектировать архитектурные контракты и собирать отказоустойчивые системы — оставляйте заявку на наш новый курс по Spec-Driven Development. Стартуем совсем скоро🙂
Мы привыкли общаться с ИИ промптами, но для автономных систем это путь к бесконечным циклам и сливу бюджета. Чтобы убрать хаос, инженеры переходят на Spec-Driven Development (SDD).
Вот как этот подход меняет работу агента на практике:
🔹Контракт вместо текста. Сначала пишется строгая спецификация (JSON-схема/OpenAPI) и автотесты. Агент зажат в рамки интерфейсов, за которые физически не может выйти.
🔹Контроль на шагах. Внутри петли Think-Act-Observe агент сверяет действия со спецификацией.
🔹Саморефлексия. Если ИИ нарушил типы или «додумал» лишнее, тест падает. Агент получает ошибку в контекст и сам правит код, не выходя за рамки ТЗ.
Это лишь база того, как укротить ИИ-разработку. Если вы хотите глубоко внедрить эту методологию, научиться проектировать архитектурные контракты и собирать отказоустойчивые системы — оставляйте заявку на наш новый курс по Spec-Driven Development. Стартуем совсем скоро🙂
📍 Навигация: Вакансии • Задачи • Собесы
#GoVisual
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4
Media is too big
VIEW IN TELEGRAM
🔥 Не называйте любого продвинутого чат-бота «ИИ-агентом»!
Дмитрий Юдин, руководитель AI/ML-направления в Сloud․ru, наглядно разбирает уровни абстракции ИИ-продуктов и убирает путаницу в понятиях.
Что внутри видео:
- Почему ассистент и агент принципиально разные сущности;
- Как ассистент может быть просто «обёрткой» над LLM, а может скрывать под капотом сложнейшую мультиагентную систему;
- Как понимать архитектуру глубже, чтобы проектировать сильные решения.
Начните создавать агентные системы с нашего бесплатного демо-урока. Всего за 2 часа разберем внутреннее устройство агента, научим его чинить свой собственный код и сохранять контекст после перезапуска.
🔗 Забрать бесплатный демо-урок
Дмитрий Юдин, руководитель AI/ML-направления в Сloud․ru, наглядно разбирает уровни абстракции ИИ-продуктов и убирает путаницу в понятиях.
Что внутри видео:
- Почему ассистент и агент принципиально разные сущности;
- Как ассистент может быть просто «обёрткой» над LLM, а может скрывать под капотом сложнейшую мультиагентную систему;
- Как понимать архитектуру глубже, чтобы проектировать сильные решения.
Начните создавать агентные системы с нашего бесплатного демо-урока. Всего за 2 часа разберем внутреннее устройство агента, научим его чинить свой собственный код и сохранять контекст после перезапуска.
🔗 Забрать бесплатный демо-урок
🔮 Прогноз для IT-рынка после 2027 года
Кажется, что рынок умер: вакансий меньше на 20-35%, зарплаты растут медленнее инфляции, hh-индекс в IT перевалил за 14. Но если наложить сегодняшний спад на три предыдущих цикла — доткомы, 2008-й и пандемию — картина выглядит иначе.
Что будет дальше: где ждать взрывного роста вакансий в 2028-2030, почему разрыв в зарплатах может достичь 50-120% и что делать в ближайшие полтора года, чтобы оказаться на правильной стороне этого разрыва.
➡️ Ответы в статье
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
Кажется, что рынок умер: вакансий меньше на 20-35%, зарплаты растут медленнее инфляции, hh-индекс в IT перевалил за 14. Но если наложить сегодняшний спад на три предыдущих цикла — доткомы, 2008-й и пандемию — картина выглядит иначе.
Что будет дальше: где ждать взрывного роста вакансий в 2028-2030, почему разрыв в зарплатах может достичь 50-120% и что делать в ближайшие полтора года, чтобы оказаться на правильной стороне этого разрыва.
📍 Навигация: Вакансии • Задачи • Собесы
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔2❤1🥱1
🛩 FlightRecorder в проде
В Go 1.25 в стандартную библиотеку завезли
Пара примеров:
▪️ Ловля хвостовых задержек
Мидлварь замеряет время обработки запроса, и если оно перевалило за исторический p95 (или прилетела 500-ка) — асинхронно дёргается снапшот.
Бинарный дамп улетает в S3, ссылка логируется в Grafana. Сработал алерт → кликнули по ссылке → скачали дамп →
▪️ Проблемы шедулера и GC
Всё, что живёт между сэмплами профайлера: залипания горутин, GC assist, вытеснение горутин с P — на трассе это видно, в профиле нет.
Но есть и обратная сторона: формат данных не дружит с Pyroscope и Datadog, поэтому дамп нельзя просто открыть в дашборде — приходится вручную гонять
💬 А вы пробовали FlightRecorder? Для чего включали, что нашли такого, чего не показал pprof, и как вытаскивали трейсы с прода?
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
#GoToProduction
В Go 1.25 в стандартную библиотеку завезли
trace.FlightRecorder — кольцевой буфер трассировки, который постоянно крутится в памяти, а по команде Snapshot() отдаёт последние N секунд событий рантайма. Идея: сэмплирующий профайлер видит «среднюю температуру», а тут вы получаете полную картину именно того момента, когда всё пошло не так.Пара примеров:
▪️ Ловля хвостовых задержек
Мидлварь замеряет время обработки запроса, и если оно перевалило за исторический p95 (или прилетела 500-ка) — асинхронно дёргается снапшот.
Бинарный дамп улетает в S3, ссылка логируется в Grafana. Сработал алерт → кликнули по ссылке → скачали дамп →
go tool trace slow-trace.out. ▪️ Проблемы шедулера и GC
Всё, что живёт между сэмплами профайлера: залипания горутин, GC assist, вытеснение горутин с P — на трассе это видно, в профиле нет.
Но есть и обратная сторона: формат данных не дружит с Pyroscope и Datadog, поэтому дамп нельзя просто открыть в дашборде — приходится вручную гонять
go tool по конкретному поду. А когда подов больше тысячи, удовольствие ниже среднего. Инструмент мощный, эргономика — пока нет.💬 А вы пробовали FlightRecorder? Для чего включали, что нашли такого, чего не показал pprof, и как вытаскивали трейсы с прода?
📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7🔥2🥰1😢1
Предложение №12854 висело в трекере с 2015 года и наконец получило статус accepted.
Теперь тип литерала можно будет опускать везде, где компилятор выводит его из контекста:
var x []string = {"a", "b", "c"}
return {}, err
compress.NewReader(r, {Level: compress.BestSpeed})
items = append(items, {id, obj, false})Запись
a := {1, 2, 3} останется ошибкой, слева нет явного типа и выводить нечего.Выигрывают табличные тесты, структуры опций и код с protobuf, где имена типов повторяются десятками. Спор о читаемости команда закрыла так.
gofmt не будет принудительно удалять типы, стиль сложится сам, а gopls получит подсказки, которые покажут пропавшее имя типа прямо в редакторе.В Go 1.27 фича не попадёт. Изменение в компиляторе простое, но нужно обновить экосистему инструментов, поэтому ориентир это Go 1.28.
📍 Навигация: Вакансии • Задачи • Собесы
Please open Telegram to view this post
VIEW IN TELEGRAM
🙏5❤3
📰 Дайджест недели
Здесь нет дождей, здесь только новости
— Go теперь компилирует TypeScript
Microsoft выкатила TypeScript 7.0 — компилятор целиком переписан с TS на Go. Итог: сборки быстрее в 8-12 раз при меньшем потреблении памяти на 6-26%
— Ускорение in-memory кеша до 8 раз
— Фреймворк, где файловая система это карта маршрутов
— Результат джуна близок к нулю
— Бонусы-призраки и грейды из параллельной вселенной
📍 Навигация: Вакансии • Задачи • Собесы
🐸 Библиотека Go-разработчика
#GoLive
Здесь нет дождей, здесь только новости
— Go теперь компилирует TypeScript
Microsoft выкатила TypeScript 7.0 — компилятор целиком переписан с TS на Go. Итог: сборки быстрее в 8-12 раз при меньшем потреблении памяти на 6-26%
— Ускорение in-memory кеша до 8 раз
— Фреймворк, где файловая система это карта маршрутов
— Результат джуна близок к нулю
— Бонусы-призраки и грейды из параллельной вселенной
📍 Навигация: Вакансии • Задачи • Собесы
#GoLive
Please open Telegram to view this post
VIEW IN TELEGRAM
Сервис сохраняет заказ в БД и сразу публикует событие
OrderCreated. Выглядит логично, но БД и очередь это две отдельные системы.Что может произойти: БД коммитилась, но публикация упала. Заказ есть, события нет. Клиент повторяет запрос из за таймаута. Событие публикуется еще раз. Нижестоящий сервис видит два события и создает два заказа вместо одного.
Сохраняем заказ и событие в одной БД транзакции. Отдельный диспетчер читает неотправленные события и публикует их позже. Если диспетчер упадет или повторится, событие уже в БД и не потеряется.
// В одной транзакции
BEGIN
INSERT INTO orders ...
INSERT INTO outbox ...
COMMIT
// Отдельно
func dispatchOutbox() {
events := store.GetUnsent()
for _, e := range events {
publisher.Send(e)
store.MarkSent(e.ID)
}
}
Каждое событие должно иметь уникальный ID. Потребители должны быть идемпотентные (игнорировать дубликаты). Outbox паттерн гарантирует, что событие не потеряется, но не гарантирует ровно одну доставку. Это нормально, если потребители правильно обрабатывают дубликаты.
Добавьте таблицу outbox. Вставляйте заказ и событие одной транзакцией. Запустите диспетчер, который периодически отправляет события из outbox. Добавьте уникальный event_id потребителям для фильтрации дубликатов.
Граница между микросервисами это самое опасное место. Outbox паттерн это простой способ сделать ее безопаснее.
📍 Навигация: Вакансии • Задачи • Собесы
#GoToProduction
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3👍3🥱3
🚀 Уже завтра стартует новый поток курса «ИИ-агенты»!
Мы собрали мощнейший состав преподавателей. Учить вас проектировать архитектуру и собирать продакшн-агентов будут инженеры и исследователи из топовых IT-компаний.
Старт уже завтра!
Сомневаетесь, подойдет ли вам программа и подача? Начните с бесплатного демо-урока!
Всего за 2 часа вы заглянете под капот ИИ-агента, поймете, чем мышление модели отличается от ее ответа, и научите систему чинить собственный код. Это идеальный способ протестировать нашу платформу перед покупкой.
🔗 Пройти демо-урок и занять место на курсе
Мы собрали мощнейший состав преподавателей. Учить вас проектировать архитектуру и собирать продакшн-агентов будут инженеры и исследователи из топовых IT-компаний.
Старт уже завтра!
Сомневаетесь, подойдет ли вам программа и подача? Начните с бесплатного демо-урока!
Всего за 2 часа вы заглянете под капот ИИ-агента, поймете, чем мышление модели отличается от ее ответа, и научите систему чинить собственный код. Это идеальный способ протестировать нашу платформу перед покупкой.
🔗 Пройти демо-урок и занять место на курсе
Собрали в карточках ошибки, которые можно отловить на код-ревью.
📍 Навигация: Вакансии • Задачи • Собесы
#GoVisual
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4❤3😢1😍1