TGViewer
Библиотека Go-разработчика | Golang Библиотека Go-разработчика | Golang @goproglib · 24.1K subscribers
Post #7369 2.86K
🧑‍💻 Четыре года разработки без документации

Код собирался. Тесты проходили. Но никто в команде не мог объяснить, зачем существуют три пакета. Инженер получил сервис на 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 часа, три четверти рабочей недели. И этот счёт оплатит заново каждый следующий инженер, который придёт в проект.

➡️ Оригинал

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

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

#GoDeep
  • 😁 6
  • 👍 4
  • 🤩 1
  • 👾 1
More from @goproglib
  1. Sep 25, 2026💡 Код работает. А data race уже есть В Go можно записать значение в одной горутине, прочи…
  2. Sep 23, 2026💥 TCP/IP: что происходит с данными в сети Когда Go-приложение отправляет данные по сети,…
  3. Sep 22, 2026🔴 Встроенные функции len, make, panic — встроенные функции Go, которые можно использовать…
  4. Sep 21, 2026🤩 Go с нуля: серия базовых шпаргалок Собрали путь от Hello, World! до goroutines, channel…
  5. Sep 20, 2026🤔 Вопрос с собеседования по Go Что выведет программа? ❤️ — [1 2 3 4 5] / [1 2 10] 🔥 — [1…
  6. Sep 19, 2026🛠 Rune — open-source IDE и terminal multiplexer на Go Rune объединяет IDE, терминал и AI-…
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 →