В прошлой статье мы оценили, что рынок ожидает от соискателей и можем ли мы на основании этих требований дать определение грейду. Теперь я предлагаю провести небольшой эксперимент и оценить возможность определить грейд уже на практике с помощью анализа кода.
Средства для анализа и оценки кода
Идея автоматизированной оценки качества кода не нова. Если сначала ориентировались на объем кода, количество занимаемой памяти и тп, то теперь внимание обращают на историю коммитов и «естественность кода» (Исследования научной школы Mining Software Repositories и A. Hindle и P. Devanbu). Если мы говорим о существующих системах, то все их можно разделить на три группы по используемому подходу.
Подход / Класс систем | Базовый алгоритм | Ключевая метрика | Корреляция с реальной работой | Основное ограничение |
Алгоритмическое тестирование | Black-box testing | Точность | 0.18 | Синтетичность задач, игнорирование архитектуры кода |
Статический анализ | AST Pattern Matching | Коэффициент технического долга | 0.3–0.4 | 30% ложных срабатываний |
MSR-аналитика | Statistical Analysis | Показатель бесполезного кода / Производительность | 0.4–0.6 | Уязвимость к манипуляциям |
Как мы видим, подобный анализ имеет низкую корреляцию с реальной работой и достаточно серьезные ограничения.
Кроме статического анализа мы можем использовать унимодальные методы, сочетающие в себе обработку естественного языка и интеллектуальный анализ кода. В современном мире существуют специализированные модели для обработки программного кода. Сравним и их между собой, используя метрику PSR (Parsing Success Rate, техническая устойчивость) — доля файлов, для которых удается построить векторное представление.
Модель | Представление кода | Учет потока данных | Техническая устойчивость | Точность классификации (POJ-104 Accuracy) |
Bi-LSTM | Токены | Нет | 100% | 84.6% |
Code2Vec | Пути в AST (Paths) | Нет | ~70% (Требует парсинг) | 91.2% |
CodeBERT | Токены (NL + PL) | Нет | 100% | 96.5% |
GraphCodeBERT | Граф (DFG) + Токены | Да | 100% | 98.4% |
Данные показывают, что модель GraphCodeBERT обеспечивает прирост точности на 1.9% по сравнению с CodeBERT за счет использования графовой структуры потока данных, что критично для оценки алгоритмической сложности.
Что мы будем проверять
Теперь я предлагаю проверить ряд гипотез:
Ориентируясь на описание и используемую архитектуру в репозитории, можно собрать кодовую базу для ее дальнейшей классификации по грейду
Ориентируясь на опыт и используемую архитектуру в репозитории, можно собрать кодовую базу для ее дальнейшей классификации по грейду
Анализ векторов репозиториев может быть достаточен для классификации репозиториев
Метрик кода может быть достаточно для классификации репозиториев
Объединение метрик и эмбенддингов векторов может дать более четкую классификацию репозиториев
Для проверки гипотез было собрано два набора репозиториев со следующими правилами классификации:
Ориентируемся на название и архитектуру проекта (датасет 1)
Запросы к гитхабу отправлялись следующие:
QUERIES = { "junior": 'language:C# "laboratory work" OR "лабораторная работа" OR "homework" fork:false pushed:>2024-01-01', "middle": 'language:C# "pet project" OR "pet-project" "WebAPI" stars:5..100 fork:false pushed:>2024-01-01', "senior": 'language:C# "DDD" OR "Clean Architecture" OR "Microservices" stars:>100 fork:false pushed:>2024-01-01' }
Ориентируемся на опыт и архитектуру (датасет 2)
В предыдущей статье мы сделали вывод, что можем по опыту и архитектурной ответственности определить грейд. Применялись следующие запросы:
SEARCH_CONFIG = { "junior": { "query": 'language:C# "laboratory" OR "homework" OR "task"', "min_stars": 0, "max_stars": 10, "exp_min": 1, "exp_max": 2 }, "middle": { "query": 'language:C# "WebAPI" OR "App" OR "Service" OR "Pet-Project"', "min_stars": 10, "max_stars": 100, "exp_min": 3, "exp_max": 5 }, "senior": { "query": 'language:C# "DDD" OR "Microservices" OR "Clean Architecture" OR "CQRS"', "min_stars": 50, "max_stars": 10000, "exp_min": 6, "exp_max": 10 } }
В итоге мы получили 16 и 21 репозиторий, заранее классифицированные по нашему усмотрению по грейду. Теперь я предлагаю выполнить следующее: собрать четыре датасета, которые будут включать в себя основные метрики кода и эмбендинги векторов собранные GrafCodeBert и UniXcoder. Таким образом мы сможем оценить качество классификации каждой из моделей и репрезентативность собранных данных.
Разведывательный анализ данных
Оценка распределения метрик
Думаю, не для кого не секрет что такое EDA – разведывательный анализ данных. Мы попробуем визуализировать наш датасет для понимания, с какими данными мы имеем дело и репрезентативны ли они. Для начала я предлагаю оценить, как распределились метрики.
Датасет 1

Датасет 2

Объем кода
Анализ объема кода (LOC) показывает, что в Датасете 1 (сформированном по архитектурным тегам) наблюдается строгая линейная зависимость: Senior проекты объемнее. Однако в Датасете 2 (сформированном по стажу) медианы Middle и Senior практически неразличимы. Это опровергает гипотезу о том, что мастерство эквивалентно количеству написанного кода. Малый разброс у Junior в Датасете 2 гово��ит о шаблонности учебных задач, в то время как огромный разброс у Senior подтверждает, что экспертность может проявляться как в малых высокооптимизированных утилитах, так и в огромных фреймворках.
Глубина наследования
С ростом грейда в Датасете 1 виден рост глубины наследования, что характерно для классических архитектурных паттернов. Однако в реальном секторе (Датасет 2) мы видим «выбросы» большой глубины даже у Junior. Вероятно, это связано с буквальным следованием учебникам. Ключевое отличие Senior-кластера не в «максимальной» глубине, а в стабильно высоком межквартильном размахе: опытные разработчики используют наследование осознанно, там, где это диктует архитектура (DDD/Frameworks), а не по шаблону.
Цикломатическая сложность
Здесь зафиксирован «парадокс мастерства». В идеализированном Датасете 1 сложность методов падает с ростом грейда. Это прямое следствие принципа единственной ответственности: Senior пишет более простые, атомарные методы. В Датасете 2 границы размыты, но сохраняется тенденция: Senior-код имеет меньше экстремальных выбросов сложности, чем Junior-код. Это позволяет использовать низкую среднюю сложность метода как один из косвенных маркеров высокого грейда при автоматической оценке.
Коэффициент зацепления
Графики подтверждают нелинейную природу зацепления. Пик Coupling приходится на уровень Middle: разработчик уже создает сложные системы, но еще не владеет техниками декомпозиции и инверсии зависимостей в полной мере. Senior-код показывает снижение зацепления относительно Middle, что свидетельствует об умении изолировать модули. Размытость данных в Датасете 2 говорит о том, что Coupling сильно зависит от домена (например, интеграционные сервисы всегда будут иметь высокий Coupling независимо от грейда).
Небольшой итог:
Такие показатели, как объем кода и глубина наследования, демонстрируют значительную корреляцию с грейдом только в «эталонных» проектах. В реальных условиях эти границы размываются.
Выявлена инверсия цикломатической сложности при переходе к высшим уровням квалификации. В то время как Junior-код характеризуется высокой связностью и запутанностью внутри отдельных методов, Senior-код демонстрирует снижение средней сложности метода.
Анализ коэффициента зацепления (Coupling) зафиксировал локальный пик на уровне Middle-разработчиков. Это свидетельствует о переходном этапе, когда сложность решаемых задач уже возросла, но навыки управления зависимостями и инверсии управления (IoC) еще не достигли экспертного уровня. Снижение зацепления в Senior-кластере является ключевым количественным маркером способности инженера проектировать изолированные, слабосвязанные модули
Высокий разброс метрик в сегменте Junior (особенно по количеству технологий и сложности) свидетельствует о феномене «входного шума». Начинающие специалисты склонны использовать избыточные конструкции и следовать паттернам формально, без учета их архитектурной целесообразности, что создает ложное впечатление сложности кода
Традиционные метрики статического анализа являются необходимыми, но недостаточными индикаторами для определения грейда. Они эффективно описывают синтаксическую сложность, но не учитывают семантическую зрелость и архитектурную логику, для анализа которых требуется переход к мультимодальным нейросетевым моделям
Оценка семантической плотности
Теперь стоит оценить наши вектора репозиториев с помощью семантической плотности кода (его смысловую наполненность). Здесь нужно обратить внимание уже на два фактора: запросы для получения репозиториев и используемую модель. GraphCodeBERT ориентируется на граф потоков данных, что позволяет работать с «грязными» репозиториями, а UniXcoder смотрит на абстрактное синтаксическое дерево, что позволяет определять паттерны, семантический стиль и смысл.
Для начала сравним семантическую плотность, собранную UniXcoder.


Анализ результатов, полученных с помощью модели UniXcoder, подтверждает её высокую чувствительность к структурным паттернам и авторскому стилю написания кода. Что мы можем отметить:
Модель чётко идентифицирует Junior-сегмент по аномально высокой семантической плотности (избыточность токенов в коде новичков)
UniXcoder эффективно фиксирует переход к уровню Senior через изменение топологии векторов в сторону декларативного программирования (использование интерфейсов, атрибутов и обобщений)
Модель в большей степени ориентирована на «форму» кода (его синтаксическую структуру). В реальных рыночных условиях (Датасет 2) это приводит к небольшому пересечению кластеров Middle и Senior, так как Middle-разработчики уже успешно имитируют форму «чистого кода», не всегда соблюдая его глубинную логику
Теперь посмотрим на результаты от GraphCodeBERT:


Что мы можем отметить по последним двум графикам:
Модель GraphCodeBERT идеально классифицирует код, если он написан в рамках строгих парадигм (DDD/Clean Arch). Мы видим четкое снижение семантической плотности: мастерство здесь проявляется в упрощении связей переменных.
На реальных рыночных данных модель выявила пик семантической плотности на уровне Middle. Это доказывает, что при переходе от Junior к Middle сложность и запутанность потоков данных растет быстрее, чем навыки их структурирования.
Огромный разброс значений у Senior-раз��аботчиков во втором случае подтверждает, что длительный стаж не гарантирует архитектурную чистоту кода.
Итак, на чем мы все же остановимся?
Для составления «эталонной» модели грейдирования необходимо использовать Датасет №1 (сформированный по архитектурным паттернам и типам проектов). Если мы хотим создать систему, которая определяет мастерство, мы должны обучать её на «чистых» примерах архитектурных стилей. Датасет №1 позволяет модели зафиксировать математический стандарт Senior-кода, который в реальном мире (Датасет 2) встречается реже, чем ожидают рекрутеры.
Несмотря на то, что UniXcoder показал неплохие результаты в детекции «синтаксического шума» у Junior, GraphCodeBERT является более глубоким и надежным инструментом для финальной оценки. Он более устойчив к «имитации» грейда. Middle-разработчик может выучить сложные слова и синтаксис (что может обмануть UniXcoder), но он не может обмануть граф потока данных — GraphCodeBERT увидит лишние связи, неоптимальные передачи переменных и отсутствие архитектурной лаконичности
Кластеризация
А теперь попробуем кластеризовать наши репозитории, чтобы понять, правильно ли они распределены по грейдам. И вот результаты:

Для начала стоит отметить, что почти все Junior собрались в левой части графика. Т е их код математически однороден и сильно отличается от остальных. Алгоритм без труда отделил их.
В средней же секции наблюдается смешанная зона. Здесь Middle, некоторые Junior и Senior перемешаны в одну группу. Что это может значить? Во-первых, большинство Middle и Senior разработчиков пишут структурно похожий код. Это подтверждает гипотезу об инфляции грейдов: люди, называющие себя Senior, часто пишут код на уровне Middle (или наоборот — Middle уже достиг уровня Senior). Ну и аномалии: Junior, код которых математически уже соответствует уровню Middle.
И, наконец, система выделила эталонный Senior-код. Этот проект настолько сильно отличается по вектору абстракции и чистоте от «общей массы» бирюзового кластера, что ИИ выделил его в отдельный вид. Это своеоразный «Архитектурный прыжок»: когда код становится экстремально лаконичным и высокоуровневым.
Все эти выводы свидетельствует о том, что накопление стажа не всегда ведет к качественному переходу в структуре кода. При этом система успешно идентифицировала единичные случаи "архитектурной зрелости" (Кластер 2), где семантический вектор кода радикально отличается от среднего Middle-уровня в сторону увеличения абстракции.
Портреты грейдов
Теперь можно попытаться объединить результаты исследований и сформировать портрет репозитория для каждого грейда.

Метрика / Грейд | Junior (Кластер 0) | Middle (Кластер 1) | Senior (Кластер 2) |
Связность | 4.07 | 19.41 | 21.09 |
Наследование | 0 | 2.73 | 7.00 |
Сложность | 3.75 | 2.33 | 1.94 |
Объем | 236 | 10320 | 92673 |
Вектор структуры | -10.20 | 2.73 | 10.04 |
Вектор смысла | 17.05 | -5.20 | -7.30 |
Под векторами здесь понимаются проекции многомерного пространства эмбеддингов на 2D-плоскость, где одна ось отвечает за структурную сложность, а вторая — за семантическую (смысловую) близость к эталонам
На основании данных портретов можно будет построить систему оценки грейда, объединив ее с картой компетенций, рассмотренной в предыдущей статье.
Итоги
Проведенный эксперимент позволил нам не только построить рабочий прототип системы оценки квалификации, но и вскрыть фундаментальные проблемы современной индустрии разработки. Объединение классического статического анализа с нейросетевыми моделями (GraphCodeBERT) привело к следующим ключевым результатам:
Математическое доказательство инфляции грейдов
Мы наглядно увидели разрыв между тем, как практика маркирует разработчиков (по стажу и звездам на GitHub), и качеством их кода. «Реальный» Senior на рынке статистически почти неотличим от Middle-разработчика. Это означает, что стаж перестает быть валидным метрикой квалификации после 3-4 лет работы.
Портрет «Истинного Сеньора» (Кластер 2)
Система смогла выделить так называемый «Золотой стандарт» (Cluster 2). Это не просто «очень опытный мидл». Это качественно иная категория инженеров, чей код характеризуется:
Сниженной цикломатической сложностью при огромных объемах проекта (мастерство декомпозиции)
Высокой глубиной наследования (осознанное применение архитектурных паттернов, а не копирование учебных примеров)
Уникальным семантическим вектором, который нейросеть четко отделяет от общей массы
Эффективность гибридного подхода
Ни метрики, ни ИИ по отдельности не дают полной картины: Метрики хороши для отсева явных Junior-ов (высокая сложность, хаотичная структура). Эмбеддинги (ИИ) необходимы для различения Middle и Senior, так как разница между ними лежит не в плоскости «как написано» (синтаксис), а в плоскости «зачем написано» (семантика и архитектурный замысел).
Так же в начале статьи мы выделили несколько гипотез, посмотрим теперь, какие заключения можно сделать:
Подтвержденная гипотеза №1: Ориентируясь на описание и используемую архитектуру в репозитории, можно собрать кодовую базу для ее дальнейшей классификации по грейду.
Сбор данных на основе строгих архитектурных маркеров (DDD, Clean Architecture) в Датасете №1 показал высокую чистоту выборки. Код в этих репозиториях действительно обладает отличимыми метриками и семантикой, позволяя модели GraphCodeBERT выявлять «эталонный» уровень Senior
Частично подтвержденная гипотеза №2: Ориентируясь на опыт и используемую архитектуру в репозитории, можно собрать кодовую базу для ее дальнейшей классификации по грейду
Технически сбор данных возможен, однако анализ Датасета №2 вскрыл проблему инфляции грейдов. Опыт (стаж) и самопозиционирование разработчика на реальном рынке часто не коррелируют с качеством кода. «Практический Senior» статистически часто неотличим от Middle, что делает классификацию на основе только внешних атрибутов (звезд, стажа) ненадежной.
Опровергнутая гипотеза 3: Метрик кода может быть достаточно для классификации репозиториев.
Традиционные метрики (LOC, Cyclomatic Complexity, Inheritance) эффективны только для фильтрации Junior-разработчиков (эффект «входного шума»). Для различения Middle и Senior они непригодны, так как рост мастерства нелинеен: Senior может писать меньше кода с меньшей сложностью, что простые метрики могут интерпретировать как код новичка.
Частично подтвержденная гипотеза 4: Анализ векторов репозиториев может быть достаточен для классификации репозиториев.
Векторные представления (особенно GraphCodeBERT) показали себя значительно лучше метрик, сумев выделить семантические паттерны зрелого кода. Однако без контекста «физических» параметров проекта (объем, связность) вектора могут давать ложноположительные результаты на имитации стиля без соблюдения архитектуры.
Подтвержденная гипотеза №5: Объединение метрик и эмбеддингов векторов может дать более четкую классификацию репозиториев.
Именно гибридный подход позволил построить наиболее точную кластеризацию. Сочетание «физики» (метрики) и «смысла» (эмбеддинги) позволило выделить уникальный кластер (Cluster 2) — «Истинных Сеньоров», отделив их от общей массы.
Буду рада услышать ваше мнение о подобном подходе к оценке грейда и о результатах данного подхода. На что бы вы смотрели, оценивая код соискателя? Хотели бы оценить свой код в сравнении с остальной кодовой базой Github?
В дальнейшем я планирую объединить свои статьи, чтобы портрет каждого грейда был дополнен рыночными требованиями технологий, объединяя в себе теорию и практику.
