Статья будет длинная, располагайтесь поудобнее. Статья написана на основании доклада с конференции Infostart Event.
Введение
Меня зовут Артур Аюханов, я технический директор Инфостарта.
Я давно занимаюсь программированием – у меня 23 года опыта разработки на разных языках.
Примерно столько же лет я занимаюсь 1С и ревностно отношусь к соблюдению стандартов по написанию кода – часто их пересматриваю и стараюсь применять.
Написал много правил статического анализа кода – все, кто пользуются статическим анализом, скорее всего, знают об том, что много моих правил в проекте BSL LS – Реализация Language Server Protocol для языка 1C (BSL). И так же немало моих правил в известном платном плагине статического анализа кода 1С для SonarQube.
Читаю много кода. Люблю его читать, люблю находить неожиданные вещи. Учусь на этом коде.
Часто провожу ревью кода соискателей, которых мы принимаем на работу к себе или в соседние отделы. Мы даем код соискателю: «Посмотри и исправь – сделай выданный код максимально похожим на тот код, который ты обычно пишешь». Человек меняет, мы обсуждаем, находим какие-то проблемы или, наоборот, замечательные вещи.
Наверняка слышали шутку для разработчиков: «Пишите код так, чтобы его не стыдно было показать маньяку, который знает ваш адрес». На одном из собеседований я задал соискателю вопрос: «А вы готовы этот код маньяку отдать?.. Мне )» Получилось смешно.
Со мной на собеседовании был мой товарищ Александр Кунташов, он это запомнил и в какой-то момент оформил стикеры в Телеграме. На слайде – один из таких стикеров. Другие стикеры из этого стикер-пака я буду приводить по ходу моей статьи.
Пример пользы код-ревью для передачи знаний.
Немного расскажу о той пользе, которую я лично получил при выполнении ревью-кода
Не так давно, просматривая код, который был в одном из решений в Инфостарте по интеграции с Avito, я увидел нестандартное использование метода ЧтениеXML.ОткрытьФайл().
Оказывается, этот метод умеет читать не только локальный файл, но и прекрасно читает файл через интернет-ссылку.
Я заинтересовался – как так? Не может быть! Автор ошибся!
Просмотрел синтакс-помощник, поискал информацию в книге «Технологии интеграции 1С:Предприятия 8.3», на интернет-ресурсах, изучил описания обновлений платформы 1С – не обнаружил никакой информации. Всюду написано, что метод читает только локальные файлы.
Потом я подумал, что у 1С еще много мест, где используется метод ОткрытьФайл(). Но это уже недоиследовал, к сожалению. Если кто-то проверит и остальные объекты, у которых возможен вызов метода ОткрытьФайл, я буду очень рад.
Как видите, читая код, мы можем узнавать что-то новое.
Для меня эта история с ОткрытьФайл() стала открытием. Я много лет с 1С – писал и на 7.7, и на 8.х, начиная с 8.0, но не знал о такой возможности. Когда она появилась, мне также неизвестно.
Во время своего доклада на конференции я опросил зал на знание этой фичи, по пальцам одной руки можно было посчитать тех, кто знал о ней )
Я примерно представляю, каким образом обсуждаемая функциональность пролезла – наверняка разработчик 1С использовал какую-то библиотеку С++, в которой и реализована подобная возможность единым образом для файлов и интернет-ссылок. Аналогичные реализации есть и для других языков.
Мое предложение к фирме «1С» – не убирайте обсуждаемую функциональность, а просто добавьте описание в руководство разработчика и синтакс-помощник.
Про Инфостарт и новый сервис 1С-Store
Немного напомню, что такое Инфостарт.
Инфостарт – это большой объем данных, много авторов, много кода. Раньше этот код появлялся фактически только у пользователя – у того, кто скачал или оплатил. И все проблемы, которые возникали при использовании авторского решения, пользователь решал самостоятельно.
Сейчас у Инфостарта появился в пилотном варианте новый сервис 1С-Store, который развивается совместно с фирмой «1С» и работает на мощностях площадки Инфостарт.
Сервис 1С-Store работает в базах у конечных пользователей, но низом там идет демо-площадка, с которой, возможно, многие из вас на Инфостарте знакомы. По специальной кнопке «Демо» можно зайти и вживую посмотреть продукт на типовой конфигурации 1С в режиме веб-клиента.
Преимущество сервиса 1C-Store в том, что весь ваш код, который попадает в базы к конечным пользователям, предварительно проверяется на соответствие функциональным и нефункциональным требованиям. Когда я говорю «ваш код», я подразумеваю, что кто-то из вас, читателей, является автором решений на Инфостарт. У нас есть:
-
Набор автоматических тестов, которые проверяют ваши решения – пока проверяются только внешние обработки и отчеты.
-
Статический анализ кода, который проверяет ваш код на соответствие набору стандартов 1С – не всем стандартам, естественно, потому что готовых правил не так много. Плюс есть проверки по безопасности – чтобы код, который приходит к пользователю, не нарушал требования по безопасности. Часть используемых правил мы выложили в открытый доступ, сделав очередной вклад в опенсорс-проект BSL LS
-
Ручной аудит кода. Очень много кода сейчас проходит именно через глаза сотрудников Инфостарта. Мы изучаем, смотрим, нам помогают правила статического анализа. И мы видим большую часть кода, хотя раньше мы на него не смотрели. Я по себе сужу – раньше я выборочно проверял какие-то решения. Зато сейчас уже любой файл, который автор обновляет на сайте, приходит к нам, и мы видим код ваших решений.
Расскажу непосредственно о коде и о результатах его проверки.
Требования к решениям
Разработка всегда начинается с требований. Каждый должен представить хотя бы в голове – что требуется от его решения.
Как правило, авторы Инфостарта в своих разработках реализуют функциональные требования. Выложенные у нас на сайте решения в подавляющем большинстве случаев работают – выполняют функции, которые в них заложены: показывают печатную форму, изменяют данные, интегрируются с каким-то маркетплейсом и так далее.
Но очень часто авторы забывают про нефункциональные требования – те, которые определяют качество системы, производительность и безопасность. Причин этому множество – чаще всего автору просто некогда наводить порядок в решении, которое и так выступает в виде полуфабриката. Или кто-то попросил выложить готовое, а автор выкладывает с уверенностью, что все и так хорошо, по принципу: «я так вижу».
Несоблюдение нефункциональных требований обнаруживается при аудите в виде замечаний к коду.
Типовые замечания к коду решений
Есть набор типовых проблем, которые встречаются практически у всех авторов.
-
Любимый всеми нами копипаст – мы все его используем.
-
Закомментированный код – действительно тонны кода. Иногда у нас есть какой-то блок кода, который нас не устраивает, но мы его не удаляем. Думаем, что он нам еще пригодится, чтобы потом оттуда что-то скопировать. Или оставляем т.н. «авторские комментарии».
-
Все эти подходы уже устаревшие – используйте Git, и вам не понадобятся авторские комментарии.
-
Я тоже раньше писал много комментированного кода. Я тоже не люблю, когда приходится что-то вспоминать. Но постепенно, за годы разработки, я обнаружил, что у меня практически никогда не возникает необходимости смотреть старый закомментированный код, т.к. требуется очень редко.
-
А чтобы посмотреть историю изменений, лучше использовать Git.
-
-
Опечатки – как обычно, всякие «внтрений», «мссив», «тблица». «Граммар-наци» (с) в моем лице часто грустит и возмущается.
-
Отступы в коде – частенько не соблюдаются, или есть путаница с пробелами и табуляциями при их одновременном использовании.
-
или разработчики не любят ставить пробелы перед операторами вида «+», «=» и т.п.
-
-
Неиспользуемые функции. Часто пишутся функции, которые никогда никем не используются. Написали и забыли про них. В коде решения и так много строк, и это пропускается.
-
Ну и аналогично пишутся функции, которые являются процедурами – зачем-то их делают функциями. Зачем – непонятно.
Таких типовых ошибок очень много, «имя им легион». Я даже скриншоты здесь не буду приводить.
Простой способ создания внешних файлов для типовых конфигураций
Практически все разработчики знают о возможности подключения внешних отчетов и обработок к типовым конфигурациям.
Нужно просто создать в модуле объекта экспортную функцию СведенияОВнешнейОбработке по формату, описанному на слайде.
Есть шаблон для создания внешних отчетов и обработок, которые подключаются в базу данных по стандартам БСП. Шаблон очень простой – всего 13 строк.
Казалось бы, все просто. Но почему-то многие авторы уходят от этого шаблона и вместо него придумывают какие-то свои не всегда работоспособные вариации. Часто мы видим огромное количество копипаста, закомментированного кода и т.п.
Если вы хотите прочитать об этом шаблоне подробнее, есть статья в документации к БСП.
Сначала я думал привести здесь ссылку на какую-нибудь статью на Инфостарте, но затем, вчитавшись в текст статьи 1С:ИТС о программном интерфейсе БСП, увидел, что там отлично разобраны все виды внешних отчетов и обработок. Можно оттуда черпать знания и писать правильно.
Главное – не стоит копировать весь код, который указан в этой статье, весь этот мега-набор комментариев. Лучше всего использовать короткий шаблон со слайда, дополнив его назначениями, разрешениями и т.д.
И добавлю пояснение небольшого нюанса, о котором авторы часто забывают – в структуре, возвращаемой в СведенияОВнешнейОбработке, есть два поля:
-
Наименование
-
Комментарий
Оба этих поля являются служебными, их можно увидеть только в справочнике «ДополнительныеОтчетыИОбработки». Оба поля можно не заполнять – они автоматически будут заполнены из «представления объекта метаданных» и «комментария объекта метаданных» внешней обработки\отчета.
О безопасности
Напомню, что по умолчанию внешние отчеты и обработки подключаются в безопасном режиме. В этом режиме 1С не дает возможности выполнить часть операций – сохранить файл, обратиться к интернету, FTP и т.п. Режим полезен для простого исключения опасных действий.
Но очень часто авторы зачем-то отключают безопасный режим во внешних печатных формах или обработках, даже если их решения работают только с базой – не обращаются к внешним сервисам, не читают и не записывают никакие, даже временные, файлы. Все равно отключают в сведениях о внешней обработке безопасный режим. Зачем? Непонятно. Возможно, по привычке.
По таким решениям у администраторов систем и аудиторов кода возникают вопросы и увеличивается недоверие к решению.
Причем аудитором здесь выступает любой разработчик, который скачивает обработку автора и хочет загрузить ее в систему для использования пользователями.
Чтобы развеять подозрения, нужны дополнительные усилия. Но люди, мы все, не любим делать много усилий и выполнять дополнительную работу. В итоге просто уменьшаются шансы, что ваше замечательное решение будут использовать, что плохо для авторов, верно?
Для помощи командам разработки и аудиторам решений на языке 1С я сделал специальное правило «Отключение безопасного режима», которое находит несколько вариантов отключения безопасного режима для внешних файлов. Пока это правило не в релизе BSL LS – до сих пор, с июля 2023 года оно находится на стадии код-ревью. У нас в Инфостарте это правило работает примерно с мая-июня 2023 и мы активно его используем для аудита решений Инфостарт для сервиса 1C-Store, ссылка на мой пул-реквест с правилом.
Самое правильное – это вообще не включать безопасный режим. Или включать точечно, если вы работаете с файлами или с интернетом. Для этого нужно использовать «РаботаВБезопасномРежиме.РазрешениеНаХХХ».
Например:
Разрешение = РаботаВБезопасномРежиме.РазрешениеНаИспользованиеВнешнейКомпоненты(
"ОбщийМакет.КомпонентаПечатиШтрихкодов",
"Печатная форма со штрихкодом");
ПараметрыРегистрации.Разрешения.Добавить(Разрешение);
Самое важное – нужно всего лишь задокументировать это поведение в публикации.
Для публикаций, которые входят в 1С-Store, мы требуем, чтобы авторы документировали, почему в их обработке или печатной форме отключается безопасный режим, какая цель преследуется.
Добавьте соответствующее описание в документации своего решения, т.е. в публикации на сайте. Если мы, аудиторы, согласны с необходимостью отключения, ваша обработка спокойно пройдет наш аудит.
Привилегированный режим
Аналогичные проблемы возникают с использованием привилегированного режима, вот только тут ситуация намного хуже.
С безопасным режимом все достаточно просто – если мы работаем с файлами или интернетом, его отключать оправданно.
А привилегированный режим включать все-таки опасно, потому что это – обход запретов системы.
Когда авторы устанавливают привилегированный режим при формировании печатной формы, наверное, они не хотят задумываться о том, как будет вести себя их печатная форма или обработка, если ее запустят под разными правами. Просто пишут: «УстановитьПривилегированныйРежим(Истина)». Но забывают, что пользователь может увидеть то, что ему не положено – то, что ему явно запретили администраторы системы.
Например, простейший отчет для показа счетов поставщикам – сколько выставлено, оплачено и так далее. Если в компании есть деление поставщиков по менеджерам, некоторым менеджерам запрещено видеть счета по другим поставщикам. Если в отчете включен привилегированный режим, разработчик может случайно показать менеджеру данные, которые именно этому пользователю показывать не нужно.
Проверять ваше решение перед установкой на боевую систему будут те же самые люди – администраторы систем 1С и аудиторы кода решений. И если вы используете привилегированный режим, шансы вашего решения пройти аудит уменьшаются. Оно будет выглядеть вдвойне подозрительно – зачем в нем выводятся лишние данные?
Для помощи командам разработки и аудиторам решений на языке 1С я сделал специальное правило «Использование привилегированного режима», которое проверяет на установку привилегированного режима. Пока это правило не в релизе BSL LS, хотя пул-реквест я отправил еще в марте 2023 года. У нас в Инфостарте это правило работает примерно с марта 2023 и мы активно его используем для аудита решений Инфостарт для сервиса 1C-Store, ссылка на мой пул-реквест c правилом.
Рекомендации с моей стороны:
-
Включаем привилегированный режим точечно и только на небольшие блоки кода – ни в коем случае не на все методы и не на весь текст метода
-
т.е. в начале метода формирования печатной формы не стоит включать привилегированный режим
-
-
И для 1С-Store в тексте публикации нужно обязательно документировать: почему привилегированный режим включается, для каких целей и на какие данные включается – важно понимать, какие именно данные будут получаться без учета прав.
Напомню, что в стандартах 1С есть стандарт по использованию привилегированного режима.
Часто привилегированный режим устанавливается, чтобы решение стало более производительным – чтобы мы в нем могли быстрее получить какие-то данные без RLS и так далее. Но забывается про права и ограничения доступа.
По стандарту мы сначала должны выполнить проверку прав доступа, а только потом установить привилегированный режим.
Указанный способ позволяет правильно выполнить проверку прав – если права позволяют выполнить операцию, только тогда устанавливается привилегированный режим. При этом и код стал более производительным, и ограничения соблюдаются!
Обработка ошибок и исключений
Поделюсь своими мыслями по моей самой любимой и наболевшей проблеме в разработке 1С – обработке ошибок и исключений. Дело в том, что с исключениями вообще мало кто из разработчиков работает правильно, наблюдаю подобное уже много лет, на ревью кода глаза автоматически выцепляют проблемный код обработки ошибок )
Исключения – это любые ошибки, которые возникают в ходе работы. Обычно мы либо их пропускаем, либо каким-то образом перехватываем в блоке «Попытка… Исключение».
Но если мы их неправильно обрабатываем, то:
-
мы теряем информацию об ошибке;
-
мы вводим в заблуждение администраторов системы, потому что администраторы системы – те, кто поддерживает систему – не знают о проблеме, которая возникает;
-
в результате может получиться так, что придется долго искать и виновника, и способы исправления возникшей ошибки.
Рассмотрим конкретные примеры.
Самая частая ошибка – это пустой блок исключения.
На слайде выше приведены два реальных примера кода.
Кстати, если вдруг авторы увидят на слайдах свой код, поясню, что я максимально постарался убрать особенности, которые могут указать на автора, но что-то где-то может проскочить. Не обижайтесь, не ругайтесь – я это делаю не со зла, не с целью обидеть. У меня цель – показать, научить, что можно сделать по-другому и сделать лучше.
Нижний скриншот – это реальный код из обработки на Инфостарте. Я не знаю, разработчик какого уровня или опыта его написал, но зачем-то он сделал именно так.
Мой коллега Василий назвал этот пример «стилем глухой попытки». Причем Василий просил упомянуть, что «мопед не мой» (с), т.е. он – не автор этого кода, он только дал подобному стилю название.
Теперь по верхнему скриншоту. Если картинка неправильно записалась, никто об этом не узнает. Если дальше наша система рассчитывает что-то с этой картинкой делать – из-за этого кода будет неизвестно, что с ней что-то не так.
Есть псевдо-обработка исключений. Например, мы перехватили исключение и каким-то образом сообщаем пользователю, что у нас возникла ошибка. Но мы при этом теряем данные об ошибке.
Например, на верхнем скриншоте со слайда выше пользователю сообщается о том, что записать номенклатуру не удалось. Но с этим сообщением ни пользователь, ни администратор не смогут понять причины ошибки. Непонятно, какая ошибка возникла. Почему не записался объект? В чем была причина? Может, какая-то подписка сработала?
Или второй вариант – разработчики решений 1С очень любят сообщать об ошибках только тому пользователю, который сейчас работает с системой. И что? Пользователь прочитал, ужаснулся, не понял ошибку, закрыл и всё. И точно так же никакой администратор не узнает о том, что возникли проблемы.
Приведу еще один пример боевого кода – я в нем ничего не удалял, ничего не убирал.
Как видите, присутствует много разных проблем.
-
Здесь нечестный комментарий, который говорит, что дальше там будет исключение. Но реально-то никаких исключений нет – автор по какой-то причине написал комментарий, а потом внизу проброс исключения закомментировал.
-
Опять используется «Сообщить()», но никаких данных для администратора никуда не пишется.
-
Закомментированный код – как обычно.
-
И проблема с «корявой» \ сложной сборкой строки сообщения – в случае, когда можно прекрасно использовать СтрШаблон().
Еще один, уже более продвинутый вариант.
Здесь разработчик уже знает, что использовать ОписаниеОшибки() не нужно, а нужно использовать ИнформацияОбОшибке(), и применяет КраткоеПредставлениеОшибки(ИнформацияОбОшибке()).
Но использует-то все равно неправильно – просто сообщает пользователю. Администраторы системы всё так же не узнают о проблеме.
Следующая ошибка практически такая же.
Первый скриншот – мы сохраняем где-то ОписаниеОшибки(), скорее всего, записываем в реквизит формы. Очень часто такое поведение неверно и подозрительно.
Если мы покажем ошибку в поле формы, пользователь легко закроет эту форму, и информация об ошибке опять потеряется.
Если мы сохраняем ОписаниеОшибки() где-то в табличных частях формы, то и эта информация прекрасно теряется аналогичным образом. И администраторам приходится заново искать проблему.
На втором скриншоте всё то же самое. Казалось бы, вот оно – внутри блока обработки исключения вызывается исключение с описанием ошибки. Автор подобного кода может считать, что код без изъянов, т.к. выполняется передача исключения из исключения, т.н. «проброс исключения» – это хороший шаблон.
Но ошибка в том, что не передается контекст предыдущей ошибки. Мы потеряли контекст – мы не узнаем, почему же у нас тут неправильно записался объект.
У фирмы «1С» есть отличный и полезнейший стандарт «Перехват исключений в коде»,
Есть правило BSL Language Server, которое реагирует на пустой блок Исключение – выдает ошибку, если в Исключении пустой блок кода или комментарий. Правда, если там будет хоть что-то написано в виде Сообщить() или будет любой блок кода – правило не сработает.
Я придумал, как улучшить подобное поведение – я сделал специальное правило, которое выдает замечание, если внутри кода-обработчика исключения и в вызываемых методах нет записи в системный ЖР, у нас в Инфостарт это правило прошло обкатку и работает уже несколько месяцев. В ближайшее время я выложу пул-реквест с этим правилом в репозиторий BSL LS.
Что я советую для правильной обработки исключений:
-
Не нужно торопиться писать код в блоке Исключение. Всегда нужно остановиться и подумать, что вы хотите сделать в этом блоке – как вы хотите обработать ошибку, и как с этой ошибкой далее будут работать пользователи и администраторы системы. Помните об администраторах. Поставьте себя на сторону людей, обслуживающих ваше решение и систему, в которую добавляется ваше решение.
-
Как я уже говорил, не нужно перехватывать ошибку только для того, чтобы показать сообщение пользователю – кстати, об этом явно написано в стандарте. Не нужно выдавать ошибку через Сообщить(), платформа и так покажет необработанную ошибку.
-
Самое важное – всегда нужно фиксировать ошибки в ЖР. Практически любые ошибки, кроме незначащих ошибок конвертации, когда мы приводим значение к какому-то типу, крайне желательно писать в ЖР и фиксировать ошибки через метод ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()).
-
Для выявления ошибок использования записи в ЖР я написал ещё одно правило «Неверное использование метода ЗаписьЖурналаРегистрации» в BSL LS, которое контролирует правильность создания ЗаписьЖурналаРегистрации() для работы в исключениях.
-
Если вы обработали исключение, и нужно передать его данные дальше, тогда выполняем какие-то действия и в конце просто вызываем метод без параметров:
ВызватьИсключение;
Я специально здесь выделил точку с запятой – никаких параметров метода, ничего дополнительного, передавать не нужно. В этом случае абсолютно все данные, которые нужны системе, включая стек ошибок, будут переданы наверх. Если ошибку дальше перехватят, весь контекст ошибки будет получен в том месте, где будет перехвачена ошибка. Вы не потеряете данные ошибки, а значит, вы правильно обработали ошибку. -
В стандарте есть два шаблона обработки ошибок – пункты 3.1 и 3.3.
Вот так выглядит шаблон обработки ошибок по п.3.1 стандарта – если имеется некоторая серверная бизнес-логика, которая вызывается с клиента при интерактивной работе пользователя.
На сервере в блоке Исключение записываем в журнал регистрации ошибку и ее контекст:
-
Событие «Выполнение операции».
-
Уровень – Ошибка или Предупреждение, но скорее всего Ошибка.
-
Передаем подробную информацию об ошибке через ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()).
-
И пробрасываем исключение на клиент через ВызватьИсключение;
И затем на клиенте уже просто вызываем нужный серверный метод и в случае исключения отдаем пользователю краткое сообщение об ошибке. Все достаточно просто.
А вот так выглядит шаблон обработки ошибок по п.3.3 стандарта – если исключение возникает при обработке какой-то клиентской бизнес-логики.
Обратите внимание, если есть клиентская логика, даже без вызова серверной функции, рекомендуется сделать специальный серверный метод с директивой &НаСервереБезКонтекста, в котором вызвать метод ЗаписьЖурналаРегистрации(). Этот приём и важен, и полезен.
Еще важный момент – Никогда не используйте функцию ОписаниеОшибки(). Она устарела, она не возвращает стек и она совершенно не информативна. Я очень хочу сделать специальное правило в BSL LS для поиска кода с этим методом.
Вообще для меня загорается красный сигнал, если я вижу ОписаниеОшибки() – явно что-то не так.
Принципы – решения с изменением данных
Теперь немного остановлюсь на принципах, которые я рекомендую использовать в решениях, где изменяются данные.
-
Самое главное – используйте логирование. Я специально выделил на слайде три раза это слово!
-
Пишите события в журнал регистрации: начало изменения данных, всю детальную информацию о том, какие данные конкретно меняете. Если меняете 10 документов, напишите по каждому документу, что его поменяли. Обязательно!
-
Сообщайте пользователю о действиях, которые выполняете – не обо всех действиях, естественно. Пишите о каких-то сводных данных: время начала, обработали 10 документов, время окончания. А уже в ЖР распишите детально все, что нужно.
Как я уже говорил, не нужно хранить ошибки на форме. Это можно делать, но только как дополнение. Основная информация об ошибках должна отражаться в ЖР или в каких-то сервисах – благо сейчас есть возможность передавать информацию об ошибках во внешние сервисы.
-
-
Правильно используйте обработку ошибок в конструкции Попытка…Исключение.
-
Обязательно используйте транзакции. Я часто вижу, что в коде обработки отменяется проведение документа, над ним выполняются какие-то действия, а потом документ опять проводится. Но это делается без попытки, без транзакции. И может получиться так, что при проведении или подготовке проведения возникнут ошибки, и документ не проведется. В итоге система останется в состоянии, когда документ неожиданно стал непроведенным. Подобные связанные вещи нужно делать, конечно, в транзакции. Почему-то некоторые авторы об этом забывают.
-
Используйте таймауты для внешних сервисов. Про таймауты часто забывают, когда создают HTTP или FTP-соединения – не указывают таймаут, а это важно. У фирмы «1С» и для такого есть замечательный стандарт. В нём даже есть рекомендации, для каких операций какое время таймаута выбирать. Обязательно изучите.
-
И еще один важный момент. Если мы делаем обработки для изменения данных, имеет смысл использовать мой любимый принцип TDD – сначала тесты, потом интерфейс или код. Сначала нужно написать некий серверный код. Идеально, конечно, тесты, но если тестов вдруг по каким-то причинам нет, нужно сначала написать серверный код и потом только под него нарисовать форму. Или наоборот – можно нарисовать форму, но с минимальным кодом. Форма должна вызывать только серверные методы – никакой бизнес-логики на клиенте быть не должно. Делаем серверный API и через него работаем. Я так уже много лет работаю – очень удобно.
Именно эти принципы я рекомендую использовать для разработки решений по изменению данных – кратко: логирование, транзакции, Попытка-Исключение и серверный API.
Любимое – работа с файлами
Перехожу к следующим интересным и любимым ошибкам.
Достаточно частая ошибка – когда с клиента на сервер передается путь к файлу на клиенте. Вызывается серверный метод, куда передается путь к файлу на клиенте, либо в диалоге выбирается файл и путь к этому файлу передается на сервер.
Это неправильно, потому что сервер и клиент – это разные машины. О подобной разнице нужно всегда помнить. Конечно, для файловой базы это не так, но нужно затвердить у себя в голове, что сервер и клиент – это всегда разные машины. Тогда не будет вопросов и проблем!
Конечно, на файловой базе легко разрабатывать, но при использовании файловой базы вы постепенно начинаете забывать, что у вас клиент и сервер – разные машины.
Если ваше решение рассчитано на работу многих пользователей, оно должно работать в клиент-серверной машине – иначе у пользователя возникнет ошибка, потому что на сервере по этому пути, скорее всего, файла не окажется.
У упоминаемой ошибки есть различные варианты.
Например, на сервер передается путь к системному каталогу клиентской машины.
Или иногда на сервер передается клиентский путь к запущенной внешней обработке – свойство ИспользуемоеИмяФайла. И затем на сервере пытаются что-то с этой обработкой сделать, что также неправильно.
Но хуже всего – вариант с абсолютными путями. Такие тоже неожиданно встречаются в решениях Инфостарта.
На слайде пример – зачем-то в универсальной обработке так сделано. Причем автор выложил это не как полуфабрикат, а как готовое решение.
При просмотре подобного кода у меня в голове всегда возникает стикер: «Результаты отличные – все красное».
Резюмирую главное по работе с файлами на клиенте-сервере:
-
Никогда не забывайте, что клиент и сервер – это разные машины. Даже когда работаете с файловой базой – все равно считайте их разными машинами.
-
Никогда не передавайте клиентские пути файлов на сервер или обратно с сервера на клиент.
-
Я хочу сделать правило BSL LS, чтобы контролировать эту ошибку. Но для BSL LS его реализовать тяжело – мы ничего не знаем о том, какого типа у нас переменная. В EDT все-таки проще, там у нас есть контекст, возможно, когда-нибудь я или кто-то другой добавит правило для EDT.
-
Ну и напомню, как можно обойти проблему, если нам нужно каким-то образом передать данные с клиента на сервер. Есть несколько простых, известных вариантов:
-
Через двоичные данные. Загружаем файл в двоичные данные, потом передаем во временное хранилище и читаем на сервере из временного хранилища.
-
Или можно непосредственно передавать файлы на сервер – есть специальные методы НачатьПомещениеФайлаНаСервер, НачатьПомещениеФайловНаСервер, НачатьПолучениеФайловССервера и их асинхронные варианты. Очень много методов, причем часть из них в 8.3.15 уже перестали поддерживаться – нужно использовать именно асинхронные варианты. В фирме «1С» добавили функциональность и зарезали ее через несколько релизов, но приходится ее поддерживать и указывать в синтакс-помощнике, а мы потом мучаемся, путаемся.
Любимое – производительность на клиенте\сервере
Следующая моя любимая проблема связана с производительностью кода – авторы часто забывают о том, что нужно минимизировать обращения к серверу на клиенте.
Очень часто в методе ПриОткрытии вызываются серверные методы, хотя фирма «1С» специально для этого сделала замечательный метод ПриСозданииНаСервере.
Конечно, иногда есть причины что-то вызывать с клиента, но лучше сразу разделить контекст, чтобы форма открывалась быстрее – формы-то бывают разные.
Например, на втором скриншоте показано, как автор из клиентского метода ПриОткрытии получает данные по организации, хотя для этого гораздо удобнее использовать обработчик ПриСозданииНаСервере.
Еще один вариант – в клиентском методе вызываются несколько серверных методов. И авторы забывают, что это приводит к проблемам, потому что мы каждый раз гоняем форму с ее данными с клиента на сервер и обратно.
Проще сделать общий серверный метод, в котором вызываются исходные методы, и спокойно вызывать его на клиенте. Форма будет работать гораздо быстрее.
А еще лучше делать внеконтекстный серверный вызов и передавать на сервер только нужные параметры. Так будет еще быстрее.
Следующее. Часто вместо серверных методов без контекста создаются обычные серверные методы. Наверное потому, что когда мы в конфигураторе создаем команду, он предлагает создать серверный метод.
Кстати, раньше, когда мы создавали команду еще в 1С:8.2, можно было выбирать между вариантами «Создать на клиенте» и «Создать на клиенте и процедуру на сервере». А потом, в какой-то версии 8.3, фирма «1С» добавила возможность выбора варианта «Создать на клиенте и процедуру на сервере без контекста».
Так вот, часто забывают – делают простые серверные методы, в которых данные формы не используются. Это лишняя нагрузка на сервер 1С, но авторы довольно редко задумываются о производительности своего кода.
Главный принцип написания клиент-серверного взаимодействия – любой серверный вызов нужно валидировать.
Об этом есть стандарт 1С и несколько правил – в BSL LS и в АПК, по-моему, тоже.
Не нужно торопиться писать код – любой серверный вызов с клиента валидируем на производительность.
Проверяем серверный метод:
-
нужно ли его вызывать;
-
можно ли его сделать внеконтекстным;
-
точно ли нужен полный контекст формы, или у нас используется один-два-три реквизита формы и можно их передать как параметры.
И еще есть важный момент, о котором, возможно, мало кто знает. В серверном методе параметры, которые не изменяются, желательно передавать через Знач. Т.е. добавляем Знач для всех параметров, которые не должны меняться в серверном методе.
В этом случае, когда мы возвращаем результат с сервера на клиент, сервер нам фактически исключает передачу этих данных, он просто использует ту копию, которая была на клиенте. В результате получаем уменьшение трафика, которым обменивается сервер и клиент.
В стандарте немного об этом есть, правда, совсем чуть-чуть. Я написал правило «Передача параметров между клиентом и сервером» для BSL LS, которое это проверяет – подробнее об этом правиле можно прочитать в тикете с описанием правила. Рекомендую использовать это правило, оно уже входит в набор диагностик BSL LS, если я не ошибаюсь. У нас в Инфостарт используется с марта 2023 г.
Сладкое – хардкод
И почти финальное – на сладкое.
К нам в Инфостарт приходят универсальные решения, которые должны работать у разных пользователей. Но выясняется, что некоторые авторы забывают об этом. И появляется какой-то подозрительный, иногда даже «сумасшедший», хардкод.
Я уже показывал пример с указанием сетевого пути для имени сервера, но оказывается, есть еще код «еще лучше» – когда в решении ищутся конкретные номера документов с конкретными датами. На слайде код из настоящих решений на сайте.
В этих примерах документы ищутся по коду, указываются конкретные ИНН, конкретные организации – возможно, разработчики берут с работы решения, которые они создали, и тут же пытаются их монетизировать или поделиться ими с сообществом. Но авторы забывают о том, что решение должно быть универсально. Возможно, они просто не смотрят на свой код, не читают его, потому что торопятся.
А про опасность хардкода логинов, паролей и IP-адресов, я надеюсь, и так все понимают – в BSL LS для этого тоже правило давно есть, ошибочный код давно ловится.
Сладкое – как быстро добавить свои правила анализа кода
Не раз уже упомянутый BSL LS – это замечательный проект и инструмент, я сам очень его люблю, но у этого проекта есть одна проблема – его мейнтейнеры считают, что BSL LS – это не диагностики. Коллеги сами редко делают новые правила, и очень медленно принимают предложенные сообществом диагностики в проект. А нам, разработчикам, командам, нужны новые правила. Я сам стараюсь добавлять новые правила, но это не так легко, как хотелось бы.
Но есть способ, каким образом можно написать свои собственные правила, чтобы быстро проверять какие-то вещи. Естественно, это обходной путь и костыль, но, тем не менее, его стоит использовать.
Еще в 2021 году в BSL LS появилось правило BadWords – «запрещенные» слова. Для этого правила можно указать параметр – регулярное выражение, а в регулярном выражении вы можете указать любой текст, который хотите проверить. Например, вы можете искать вызовы каких-то методов платформы, которые вам или вашей команде кажутся подозрительными.
Если вы этот параметр указали, у вас SonarQube начнет выдавать замечания на код, в котором есть эти методы или эти слова.
Кстати, это правило работает и в комментариях, и в коде, и в этом небольшая проблема у правила, т.к. искомое ищется практически везде, а не только в коде. Я сделал доработку правила, добавив параметр для поиска только внутри кода, мой пул-реквест уже принят, но пока еще не в релизе BSL LS.
Я использую правило как дополнительный показатель, метрику аудита – то, что должно сработать, чтобы на это обратили внимание, чтобы техлид или разработчик посмотрел, что это за код, отмеченный замечанием правила. Может быть, это внешний код, в котором используются какие-то запрещенные методы – например, УдалитьДанныеИнформационнойБазы. Любому разработчику сразу понятно, если такой метод вызывается – это решение подозрительное. Если это внешняя печатная форма, которую принес подрядчик – это подозрительно.
Таким образом вы можете добавить нужные методы, на которые необходимо реагировать, потому что решение с этим кодом нельзя просто так встраивать в свою информационную базу. На слайде я привел пример таких платформенных методов.
Таким простейшим способом вы сможете самостоятельно, не ожидая новых правил, контролировать качество вашего кода. Небольшой хак в копилку разработчикам.
Итоги
Самое главное:
-
Любите свой код;
-
Ухаживайте за ним;
-
Соблюдайте нефункциональные требования – помните, что код должен выполняться не только правильно, но и быстро, качественно, быть безопасным;
-
Читайте стандарты 1С и статьи на Инфостарте;
-
Пишите статьи на Инфостарте, пожалуйста;
-
Пользуйтесь теми инструментами, которые люди придумали для вас: SonarQube, BSL LS и другие средства статического анализа и DevOps-практик.
И моя любимая фраза: «Лучше день потерять, а потом за пять минут долететь!»
Дай Бог, чтобы ваш код был замечательным, и не вызывал проблем при аудите у нас в Инфостарте.
To be continued …
P.S. Используйте стикеры Телеграм, используемые в статье, и повышайте качество своего кода!
*************
Статья написана по итогам доклада (видео), прочитанного на конференции Infostart Event.