«Что не так с этим кодом»

Учимся читать чужой код и за минуту называть его дефекты — самый частый формат практической части собеседования по 1С.

«Что не так с этим кодом» — задание, где интервьюер показывает готовый фрагмент и просит найти проблемы: не переписать сразу, а именно увидеть дефекты и объяснить, чем они опасны.

Этот формат ценят, потому что он проверяет не зубрёжку, а инженерное чутьё. На боевом проекте вы больше читаете код, чем пишете: правите чужое, разбираетесь в наследии. Умение быстро сказать «вот здесь запрос в цикле, а тут обращение к базе с клиента» отличает джуна, который просто «пишет как-то работает», от того, кого можно пускать в прод.

Вопрос-крючок

«Посмотрите на процедуру. Она работает и выдаёт правильный результат. Что бы вы в ней исправили и почему?»

&НаКлиенте
Процедура ПересчитатьСкидки(Команда)

    Для Каждой СтрокаТЧ Из Объект.Товары Цикл

        Запрос = Новый Запрос;
        Запрос.Текст =
            "ВЫБРАТЬ Цены.Цена КАК Цена
            |ИЗ РегистрСведений.Цены.СрезПоследних(&Дата, Номенклатура = &Номенклатура) КАК Цены";
        Запрос.УстановитьПараметр("Дата", ТекущаяДата());
        Запрос.УстановитьПараметр("Номенклатура", СтрокаТЧ.Номенклатура);

        Результат = Запрос.Выполнить().Выбрать();
        Если Результат.Следующий() Тогда
            СтрокаТЧ.Цена = Результат.Цена;
        КонецЕсли;

    КонецЦикла;

КонецПроцедуры

Код работает. Но в нём как минимум три классических дефекта, и хороший интервьюер ждёт, что вы назовёте их без подсказок.

Дефект 1: запрос в цикле

Внутри Для Каждой ... Цикл на каждой строке создаётся и выполняется новый запрос. Если в документе 200 строк — это 200 обращений к базе вместо одного. Это самый известный антипаттерн 1С: он незаметен на трёх строках в тесте и кладёт систему на реальном документе.

Как объяснить: «Запрос нужно вынести из цикла — получить все цены одним запросом по списку номенклатуры (через параметр-массив и условие Номенклатура В (&СписокНоменклатуры)), а в цикле только раскладывать результат». Детальный разбор именно этого антипаттерна — в разделе про запросы; здесь важно его опознать.

Дефект 2: обращение к базе с клиента

Директива &НаКлиенте означает, что процедура выполняется на стороне пользователя. А Новый Запрос и Выполнить() обращаются к базе — это серверная операция. В управляемом приложении так писать нельзя: запрос на клиенте либо не скомпилируется, либо каждый его вызов превратится в отдельный дорогой рейс на сервер.

// Тяжёлую работу с базой выносим на сервер без контекста
&НаСервереБезКонтекста
Функция ПолучитьЦены(МассивНоменклатуры, Дата)
    // здесь один запрос по всему массиву
КонецФункции

Директива &НаСервереБезКонтекста — самая дешёвая: она не тащит на сервер всю форму, только переданные параметры. Правило простое: работа с базой — на сервере, интерфейс — на клиенте.

Дефект 3: ТекущаяДата() вместо даты документа

Цена берётся срезом последних на ТекущаяДата() — системное время сервера прямо сейчас. Но пересчитывать позиции документа надо на дату самого документа (Объект.Дата), иначе при перепроведении вчерашнего документа подставятся сегодняшние цены. Плюс ТекущаяДата() — это дата сервера, а не сеанса; в распределённой базе это отдельный источник ошибок.

Скрытый дефект: нет блокировки

Если бы этот код не читал цены, а списывал остатки перед проверкой «хватает ли товара», всплыл бы четвёртый дефект — отсутствие блокировки. Без управляемой блокировки два пользователя одновременно прочитают один и тот же остаток и оба спишут — получится минус на складе. Для чтения цен блокировка не нужна, но на собеседовании стоит показать, что вы держите этот риск в голове (подробно — в разделе про блокировки).

Как отвечать

Не бросайтесь переписывать. Сначала перечислите дефекты по важности: «Первое и главное — запрос в цикле, это про производительность. Второе — работа с базой под &НаКлиенте, это архитектурная ошибка. Третье — ТекущаяДата() вместо Объект.Дата, это про корректность данных». Затем в двух словах — как исправить каждый. Такой структурированный ответ ценится выше, чем сразу вываленный «правильный» код: он показывает, что вы видите картину целиком.

Как это работает: почему интервьюер даёт рабочий код

Код специально сделан работающим — на маленьком тесте он выдаёт верный результат. Проверяют, отличаете ли вы «работает» от «сделано правильно». Джун радуется зелёному результату; инженер видит, что на 200 строках и трёх пользователях этот код развалится. Именно этот разрыв между «проходит тест» и «выдержит прод» и прощупывает задание.

Частые ошибки на собеседовании

  • Молча начинают переписывать. Интервьюер не видит хода мысли и не знает, поняли ли вы, почему плохо. Сначала назовите дефекты словами.
  • Находят один и останавливаются. Увидели запрос в цикле — и молчат про клиент/сервер. Ищите несколько проблем: их обычно закладывают три-четыре.
  • Путают уровни проблем. Смешивают «медленно» (производительность) и «неверно» (корректность). Разведите: запрос в цикле — про скорость, ТекущаяДата() — про правильность.
  • Придираются к стилю вместо сути. «Я бы переименовал переменную» — это не то, что спрашивают. Сначала архитектура и производительность, косметика потом.

Итоги-шпаргалка

  • Формат «что не так» проверяет умение читать код и видеть разрыв между «работает» и «правильно».
  • Три главных подозреваемых в 1С: запрос в цикле, работа с базой на клиенте, отсутствие блокировки при изменении данных.
  • Отдельно ловите корректность: дата документа против ТекущаяДата(), пустые проверки, NULL.
  • Отвечайте структурно: сначала перечислите дефекты по важности, потом коротко — как чинить.
  • Не начинайте с переписывания и не останавливайтесь на первом найденном дефекте.
Проверьте себя
1. Почему нельзя выполнять «Новый Запрос ... Выполнить()» в процедуре с директивой &НаКлиенте?
AЗапрос на клиенте всегда возвращает NULL
BРабота с базой — серверная операция, на клиенте она недоступна или превращается в дорогой вызов сервера
CНа клиенте запросы выполняются медленнее из-за подсветки синтаксиса
DКлиент не поддерживает язык запросов 1С в принципе
2. Какой дефект в примере относится к КОРРЕКТНОСТИ данных, а не к производительности?
AЗапрос внутри цикла по строкам табличной части
BИспользование ТекущаяДата() вместо Объект.Дата для среза цен
CДиректива &НаКлиенте у процедуры
DСоздание нового объекта Запрос на каждой итерации