На Хабре любят хейтить менеджеров, которые «забыли, как кодить». Мол, оторвались от реальности, не понимают сроков, не чувствуют боль разработчика. Я раньше тоже так думал. А потом попал в команду к человеку, который три года не открывал IDE, и за полгода понял, что был неправ.

Контекст: что было до

До Серёги (это нынешний тимлид) у нас был Андрей. Андрей — зверь в техническом смысле. Кодовую базу знал так, что мог в голове прокрутить стек вызовов уровней на пять. Каждый PR ревьюил лично. Сам писал кучу кода.

И команда его в итоге ненавидела. Не сразу — сначала было восхищение, потом привыкание, потом тихое раздражение.

Один случай, он показательный. Февраль прошлого года, я три дня делал кэширование для дашбордов. Написал, тестами покрыл, отправил на ревью. Андрей вернул с 36 комментариями. Тридцать шесть — я потом реально посчитал. Половина стилистика, четверть «тут можно красивее», четверть — он хотел другую архитектуру. Переделал — ещё под тридцать. Ещё раз — ещё двадцать. На четвёртый круг я сдался и попросил: покажи, как ты видишь. Он сел и за полтора часа переписал 70% моего кода. Объективно стало лучше. И абсолютно чужим.

После года работы с Андреем я стал сомневаться в каждой строчке, которую пишу. Не преувеличиваю. Открываешь IDE, начинаешь писать и сразу слышишь в голове: «А Андрей бы сказал, что тут...»

Из шестерых четверо ушли за полтора года. Маша ушла в тестирование, сказала «разработка не моё». Я почти уверен, что дело не в разработке.

Важное: Андрей не злой человек. Он реально хотел помочь, реально считал, что учит нас писать лучше. Когда я показал черновик Серёге, он первым делом сказал: «Ты Андрея злым описал, он нормальный». Может, и правда. А может, я до сих пор обижен за те комментарии, хрен знает. С Андреем мы потом нормально общались, он даже помогал, когда мы застревали в архитектуре — но это уже было после, когда он перестал быть над нами.

Серёга

Назначили, когда Андрей ушёл в архитекторы. Первая встреча, дословно: «Слушайте, я код не открывал года полтора. В вашем стеке не шарю. Ревьюить не буду, архитектуру предлагайте сами. Но если кто-то будет вас дёргать по мелочам — приходите, разберёмся».

Я тогда подумал: ну всё, приехали.

Через полгода понял, что ошибся.

Я решил посчитать

Мне в какой-то момент стало интересно — не в смысле «докажу, что он бездельник», к тому моменту уже было видно, что команда поехала быстрее. Просто хотелось понять: куда уходит время человека, который не пишет код?

Договорился с Серёгой, что месяц он будет скидывать мне в конце дня список дел. Без деталей, категории и примерное время. Параллельно поднял метрики из GitLab за два периода — последние полгода Андрея и первые полгода Серёги.

Написал скрипт, кому интересно:

import gitlab
from collections import defaultdict
from datetime import datetime, timedelta, timezone

# ============================================================
# НАСТРОЙКИ 
# ============================================================
GITLAB_URL = "https://gitlab.com"          # ← ваш GitLab-инстанс
PRIVATE_TOKEN = "glpat-xxxxxxxxxxxx"       # ← Settings → Access Tokens
PROJECT_ID = 123                           # ← ID проекта 
PERIOD_DAYS = 180

# ============================================================
# ПОДКЛЮЧЕНИЕ
# ============================================================
gl = gitlab.Gitlab(GITLAB_URL, private_token=PRIVATE_TOKEN)
gl.auth()  # проверяем, что токен рабочий
project = gl.projects.get(PROJECT_ID)

since = datetime.now(timezone.utc) - timedelta(days=PERIOD_DAYS)

# ============================================================
# КОММИТЫ
# ============================================================
author_commits = defaultdict(int)
author_lines_added = defaultdict(int)
author_lines_removed = defaultdict(int)

print("⏳ Загружаю коммиты...")
commits = project.commits.list(since=since.isoformat(), get_all=True)
print(f"   Найдено коммитов: {len(commits)}")

for i, commit in enumerate(commits):
    author = commit.author_name
    author_commits[author] += 1

    try:
        diff = commit.diff()

        for d in diff:
            for line in d.get('diff', '').split('\n'):
                if line.startswith('+') and not line.startswith('+++'):
                    author_lines_added[author] += 1
                elif line.startswith('-') and not line.startswith('---'):
                    author_lines_removed[author] += 1

    except Exception as e:
        print(f"   ⚠️  Не удалось получить diff для {commit.short_id}: {e}")

    # Прогресс (каждые 100 коммитов)
    if (i + 1) % 100 == 0:
        print(f"   Обработано {i + 1}/{len(commits)}...")

# ============================================================
# MERGE REQUESTS
# ============================================================
print("\n⏳ Загружаю Merge Requests...")

mr_authors = defaultdict(int)

mrs = project.mergerequests.list(
    created_after=since.isoformat(),
    state='merged',
    get_all=True
)
print(f"   Найдено MR: {len(mrs)}")

merge_times = []

for mr in mrs:
    # Считаем авторов MR
    if mr.author and 'name' in mr.author:
        mr_authors[mr.author['name']] += 1

    if mr.merged_at is None:
        continue

    created = datetime.fromisoformat(mr.created_at.replace('Z', '+00:00'))
    merged = datetime.fromisoformat(mr.merged_at.replace('Z', '+00:00'))
    merge_times.append((merged - created).total_seconds() / 3600)

# ============================================================
# ВЫВОД РЕЗУЛЬТАТОВ
# ============================================================
print("\n" + "=" * 60)
print("📊 РЕЗУЛЬТАТЫ")
print("=" * 60)

print(f"\n📅 Период: последние {PERIOD_DAYS} дней")
print(f"   ({since.strftime('%Y-%m-%d')} — {datetime.now(timezone.utc).strftime('%Y-%m-%d')})")

# Коммиты по авторам
print(f"\n🔨 Коммиты по авторам:")
for author, count in sorted(author_commits.items(), key=lambda x: -x[1]):
    added = author_lines_added[author]
    removed = author_lines_removed[author]
    print(f"   {author:<30} {count:>5} коммитов  (+{added} / -{removed} строк)")

# MR по авторам
print(f"\n🔀 Merge Requests по авторам:")
for author, count in sorted(mr_authors.items(), key=lambda x: -x[1]):
    print(f"   {author:<30} {count:>5} MR")

# Среднее время до мержа
print(f"\n⏱️  Время до мержа:")
if merge_times:
    avg = sum(merge_times) / len(merge_times)
    median = sorted(merge_times)[len(merge_times) // 2]
    print(f"   Среднее:  {avg:.1f} часов")
    print(f"   Медиана:  {median:.1f} часов")
    print(f"   Мин:      {min(merge_times):.1f} часов")
    print(f"   Макс:     {max(merge_times):.1f} часов")
else:
    print("   Нет данных �� мержах за указанный период")

print("\n" + "=" * 60)

Скрипт писал ночью, продакшн-качество не обещаю. Для разовой аналитики хватило.

Что получилось

Метрики по коду (GitLab, 6 месяцев):

Метрика

При Андрее

При Серёге

Коммиты тимлида

847 (58%)

0

Коммиты остальных

612

1340

PR от команды / мес

23

41

Среднее время ревью

34 часа

8 часов

PR отклонено / переписано

31%

7%

Story points за спринт (среднее): при Андрее 34, при Серёге 48. Плюс 41%.

Я проверил — может, мы стали проще оценивать? Нет, средний размер PR даже немного вырос. Просто мы перестали ждать ревью по полтора дня, перестали переписывать каждый PR по четыре раза, перестали бояться предлагать решения. Через три месяца пересобрал данные — цифры те же, ±5%, не аномалия.

Аналогичных данных по Андрею у меня нет — он не вёл дневник, и я не додумался попросить. Так что сравнение хромает: по GitLab — яблоки к яблокам, по распределению времени — только Серёга. Имейте в виду.

Время Серёги по Notion за месяц:

Категория

Часы

%

Митинги с другими командами

28

18%

Согласования, эскалации

24

15%

Защита от входящих запросов

22

14%

1-1

16

10%

Планирование

14

9%

Стейкхолдеры

12

8%

Найм

10

6%

Документация

8

5%

Политика

8

5%

Разное

15

10%

Когда показал ему табличку, Серёга удивился: «У меня ощущение, что я целыми днями на митингах, а тут всего 18%». Ну да — потому что остальные 82% тоже работа, просто её не видно.

Кстати, про «видно по комментариям»

Небольшое отступление, но оно в тему.

Недавно я обучил модель на 10 000 комментариев к код-ревью — хотел отделить полезные от мусора. Классификатор получился нормальный, точность 87%. Но потом я полез смотреть, где модель ошибается, и заметил странное: она систематически сомневалась на одних и тех же людях. Их комментарии выглядели нормально, но что-то было не так. Поднял историю коммитов — все они уволились через месяц-два.

Модель видела угасание. Комментарии сжимаются, вопросы исчезают, «ок» вместо абзацев, тон выравнивается в стерильную линию. Из тех, кто реально ушёл, модель заметила почти три четверти. За шесть недель. По комментариям к коду.

Я потом думал — а что бы она показала на нашей команде при Андрее? Когда четверо из шести ушли за полтора года? Подозреваю, красные флаги были бы по всем, включая тех, кто остался. Потому что были и такие: паттерн угасания есть, а увольнения нет. Человек приходит, закрывает задачи, кивает на созвонах. Год, два. Формально — работает. Фактически — присутствует. По метрикам всё в порядке, а внутри давно пусто.

Тот, кто уходит — хотя бы делает выбор.

Подробный разбор с кодом и паттернами — на Хабре. А если интересна тема на стыке LLM и всего, что обычно не измеряют — я про это пишу в «токены на ветер».

Ладно, вернёмся к Серёге.

Несколько случаев

Про демо. Июнь, большое демо для инвесторов, real-time аналитика. За день до показа прод лёг — конкретно та часть, которую показывать. Мы втроём — я, Костя, Димка — почти трое суток в коде. Серёга не написал ни строчки. Он пошёл к CEO и сказал: переносим на два дня. CEO орал — инвесторы летят, репутация, сроки. Серёга: можем показать сырое, репутационные потери будут больше, решай. CEO перенёс. Мы за 48 часов починили. Демо прошло не идеально, косяки были, но сделка закрылась.

Про безопасников. Интеграция со сторонним сервисом, работала год. Потом аудит: всё переделывать, сертификаты не те, протокол старый. Переделка — три недели работы плюс заморозка фич. Серёга собрал встречу: мы, безопасники, сторонний сервис, юристы. Спросил одно: какой конкретно риск и какая вероятность? Оказалось — риск есть, вероятность низкая, есть временный workaround. Workaround сделали за два дня. Переделку кинули в бэклог. Три недели работы не случились.

Про Олега. Был у нас разработчик, технически сильный, но на ревью не комментировал, а унижал. Типа «ты серьёзно не видишь O(n²)?». Все терпели — Олег закрывал сложные задачи. «Ну он такой». Серёга полгода терпел, потом — нет. Деталей не знаю. Олег ушёл «по собственному», с рекомендациями. После этого люди стали спокойнее предлагать идеи, ревью перестало быть минным полем. Не знаю, как это выразить в метриках, но я это видел каждый день.

Как Серёга попробовал написать код

Март. Баг — кнопка не работает в Safari. Серёга видит тикет: «О, это просто CSS, сам пофикшу».

Два часа: как у вас локально фронт запустить? Ещё час: почему npm install ругается на peer dependency? Полчаса: поменял CSS, изменения не появляются.

Костя пофиксил за десять минут.

Не потому что Серёга тупой. Контекст — штука, которую нельзя получить за полдня. Три года прошло, всё поменялось. Мы потом шутили: «Серёг, хочешь помочь — не помогай». Он нормально отнёсся, без обид.

А кто принимает технические решения?

Команда. У нас есть штука, которую мы называем RFC — по факту гуглдок на пару страниц. Проблема, варианты, плюсы-минусы. Все читают, комментируют. Серёга на обсуждениях работает фасилитатором — следит, чтобы Костя не задавил Лену (он громче), чтобы не ушли в дебри.

Работает не идеально. Но при Андрее решения тоже были не идеальные, просто это были решения Андрея, и когда они оказывались хреновыми — виноват Андрей, а мы ни при чём. Сейчас косячим сами и сами разгребаем. По-моему, так честнее.

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

Чего не хватает

Врать не буду — не хватает технического менторства. Серёга не может провести ревью, от которого чему-то научишься. Не может подсказать паттерн или сказать «тут лучше стратегия, а не цепочка if-ов». Компенсируем: ходим к архитекторам (к Андрею, кстати — ирония), внутренние митапы, читаем. Для сеньоров нормально. Для джунов было бы тяжело.

Ещё иногда хочется, чтобы кто-то просто сказал «делаем так», без обсуждений на полчаса. Серёга принципиально так не делает.

Одна вещь, которая меня удивила

Помните «защита от входящих запросов», 14% времени? Я попросил скинуть примеры за неделю. Вот что он отбил:

— Соседняя команда хотела, чтобы мы «быстро глянули их API». Нет, у нас свой роадмап. — HR звал человека на хакатон в субботу. Нет. — Платформенная команда предлагала перейти на их новый фреймворк. Не сейчас. — CTO хотел синк по единой архитектуре. Серёга: через два месяца, сейчас не время. — Продакт прибежал со «срочным багом от партнёра». Серёга проверил — не срочный, в бэклог.

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

Как-то спросил Серёгу: тебе не обидно, что ты не делаешь ничего технического? Он задумался и сказал: «Раньше думал, что код — это работа, а остальное шум. Потом попробовал и то, и другое. Сгорел. Понял, что шум — тоже работа. Кто-то должен его гасить, иначе вы...» — и махнул рукой, не договорил.

Я понял.

Вместо итога

Хороший тимлид — не обязательно лучший инженер. Иногда это тот, кто даёт инженерам спокойно работать. Банально звучит, на практике — редкость.

По большинству опросов разработчиков, тимлид должен кодить. По моим данным — команда без кодящего тимлида выдаёт на 41% больше. Может, мой случай — исключение, и мне просто повезло с Серёгой. Но мне кажется, мы неправильно понимаем, что такое лидерство в разработке.

Ваш тимлид пишет код? Помогает это команде или мешает?