Transcription
Ты писал код на 1С, а потом сам не мог в нём разобраться. Ты получал замечание о нарушении стандартов, но не понимал, где это нарушение. Ты не хочешь выглядеть вечным джуном. Ты хочешь писать чистый, правильный код. Тогда это видео для тебя.
Сегодня я подобрал 20 правил и стандартов 1С, которые мне кажутся очень важными. С такими нарушениями я сталкивался каждый день в своей практике. Сегодня разберём, например, как правильно называть функции, зачем нужна транзакция, почему нельзя просто обернуть всё в попытку исключения и почему сортировка в таблице значений может убить всю производительность? Все ссылки на стандарты и прочие материалы из ролика, как обычно, будут по ссылке в описании в моём канале. Итак, поехали.
Ещё совет перед началом видео, как сделать так, чтобы синьоры приняли тебя в свой, так сказать, узкий круг клан. Как пользоваться этим видео? Так, чтобы оно нанесло тебе максимальное количество пользы. Во-первых, посмотри его до конца, чтобы понимать, в общем и целом, что в этом видео и в какой последовательности. Далее, пробуй на практике применять эти стандарты, но не хаотично, а заведи себе привычку. Например, один день посвящай первому стандарту, второй день второму и так далее. А через 20 дней снова начни с первого. Через какое-то время ты практически без раздумья уже будешь применять эти стандарты на автопилоте, и все синеры тебя наверняка зауважают.
Начнём со стандарта 647. Он называется имена процедур и функций. А имена процедур и функций - это первое, что видишь ты, когда смотришь на код. Если имя говорит само за себя, то и его и читать, и понимать в разы проще. То, что там написано, хорошее имя функции или процедура должно чётко отражать, что она делает, а не заставлять тебя там гадать или лезть в её внутренности. Если ты не можешь придумать понятное имя, ну, скорее всего, сама процедура написана изначально криво и архитектурно неверно. Если имя приходит сразу в голову само, значит, с архитектурой всё нормально.
Теперь пройдёмся немножко по правилам. А, во-первых, имена должны быть, так сказать, говорящими. Например, не надо писать что-то вроде выполнить проверку с параметрами. Параметр один, параметр два, параметр три. А это вообще ни о чём не говорит тому, кто это увидит в первый раз. А лучше написать примерно так, что реквизит объекта заданного типа назвать функцию или заполнить имена реквизитов по хозяйственной операции. Из такого названия сразу будет понятно, что делает эта функция, какие у неё параметры. Во-вторых, пишем имена слитно как название переменных, то есть по тем же правилам. Каждое слово с большой буквой, даже предлоги местоимения с одной буквы, тоже с большой. Также не нужно указывать в имени тип возвращаемого значения. Например, неправильно назвать будет получить массив ролей с правом добавления. Лучше просто назвать имена ролей с правом добавления. Тип возвращаемого значения должен быть понятен из контекста, а в качестве исключения, если без него вообще непонятно, что функция возвращает. А процедуры называем от неопределённой формы глагола. Загрузить контрагента - это правильно, а загрузка контрагента - это неправильно. Это действие. Значит, нам нужен глагол. По правилам русского языка вспоминаем неопределённую форму глагола. А функции по-другому немножко. А обычно называют потому, что она возвращает. Например, неправильно будет сказать получить полное имя пользователя. Ну, это избыточно, а правильно назвать проще. Полное имя пользователя. А если функция создаёт новый объект, тогда добавляем слово новый. например, новое поле формы или новый элемент справочника. Если функция проверяет какое-то условие, то начинаем имя со слова это. Как бы намекаем, что это возвращаться будет булево. Либо используем причастие. Неправильно будет написать проверить проведённость документа. Правильно будет написать документ проведён или это внешняя задача. Если важно, как именно получается результат, тогда можно использовать глагол в названии функции. например, выбрать данное по правилу или преобразовать данное по правилу. Также, если функция просто выполняет действие и результат для нас вообще не важен, то называем её также как процедуру от глагола. Например, разрешить редактирование реквизитов объекта. Здесь всё просто. Глагол, когда действие, существительное, когда возвращаем значение. И не нужно никаких загадочных сокращений, как это любят начинающие программисты.
Следующий стандарт 453. Описание процедур и функций. Если сказать коротко, то процедуры и функции нужно описывать, давать там комментарии перед началом процедуры или функции. Но не всегда и везде это нужно. Всё зависит от того, насколько они сложные, и зависит от того, кто их будет использовать. Когда точно нужно писать комментарии согласно стандарту. А если процедура или функция экспортная, то есть её будут вызывать из других модулей или даже из других приложений, в таком случае комментарий обязателен, потому что кто-то другой будет читать твой код, и он должен понять, что делает функция без долгих разбирательств в её теле. А для остальных случаев смотрим по ситуации. Если поведение этой функции не очевидно, какая-то нестандартная логика, она очень большая или нужно объяснять, ээ, потому что делается что-то определённым образом и как-то делается хитро, тогда обязательно нужен комментарий. А если всё понятно из названия параметров, то писать дополнительный текст не оббязательно. Ещё важно, не нужно писать бессмысленные комментарии. Например, не надо подписывать, что процедура, обработчик события при открытии. Ну, блин, это и так понятно названия. Когда ты всё-таки пишешь комментарии, когда он нужен, то нужно делать это грамотно. Нужно начинать с глагола, описывает, что делает функция, например, создаёт структуру, бла-бла-бла, или проверяет доступность, бла-бла-бла. Не нужно дублировать название самой функции, просто пустая трата времени, это пустые слова. А если у функции есть параметры, то обязательно надо прописать их типы. Это требование стандарта. Типы пишутся через запятую после пояснения. Если параметр сложный, мо дать ссылку на функцию можно и которая этот этот параметр создаёт. Или как пример, как это должно выглядеть. Для массивов желательно указывать, из чего они состоят. Например, массив строк или массивы ссылок на задачи. Если функция что-то возвращает, нужно писать, что именно и какого типа. Иногда достаточно одного слова, например, строка. Если возврат может быть разным, то пишем построчно каждый вариант и что он значит. Можно добавить пример вызова функции - это хорошая практика, особенно если входные параметры сложные или не очевидные. И если параметров много или они могут быть разных типов, то полезно добавить секцию с вариантами вызова. То есть примеры типа вот такой набор делает это, такой набор делает это. Это хорошая практика. Это экономит другим программистам их время. Также сэкономит это ваше время, когда вы уже через год будете снова возвращаться к этой задаче и пытаться понять, а что же вы тут такое написали. Если функция когда-то устареет, так прямо нужно написать устарела. Используйте вместо неё то-то, тот таком-то, таком-то модуле. Вы это также можете видеть в БСП. Там часто такой комментарий есть, что устарелая, вместо этой функции используйте то-то, то-то. Почему функции и процедуры сразу не удаляются из БСП? Потому что разработчики могут их где-то использовать. во многих местах, и чтобы не заставлять их всех переписывать, то эта функция остаётся. Но там пишется, что она устарела, пожалуйста, больше не используйте, и когда-нибудь, скорее всего, эта функция там исчезнет. Ещё если в процедуре есть директива компиляции, то сначала пишем комментарий, а потом директиву. Так будет читаться логичнее. Вот здесь на примере на экране видно, что сначала написан комментарий, а потом директива идёт. Кстати, если вам лень писать комментарии к функциям оформлять это всё в общих модулях, то по воспользуйтесь искусственным интеллектом. Ссылку оставлю в моём канале.
Следующий стандарт 640- параметры процедур и функций. Параметры - это то, с чем снаружи будут работать другие разработчики. Позаботься о них. И если с этими параметрами будет бардак, то и вся функция становится вообще непонятной и неудобной. А, во-первых, давай параметрам понятные имена. Не нужно писать P1, P2 или recкв. Правильнее будет написать что-то вроде дата начала, документ, объект, тип цены. По смыслу будет понятно тогда и из предметной области, чтобы разработчик, глянув на эти параметры, даже без просмотра тела функции, уже сразу примерно хотя бы понял, зачем нужен этот параметр. Во-вторых, этот параметр будет использоваться дальше в телефункции. И если вы его назовёте, например, P1, P2, с ним будет там неудобно работать. Он слишком короткий или слишком непонятный. А, во-вторых, не нужно заменять параметры переменными модулями или реквизитами форму. Это путь в никуда. Это путь в дебре, баге и в спагет. Всё, что может меняться и использоваться снаружи, должно быть явно передано в параметрах. Далее, расположение параметров. Сначала в первую очередь пишем общее, а потом частное. То есть, например, сначала объект, с которым работает функция, потом имя поля, которое, например, будем изменять, потом флаг, с которым будем работать, включает НДС или нет или тому подобное. Но не наоборот. Сначала более крупные объекты, более важные. Не пихай в функцию кучу параметров. Если их больше семи, то по стандарту их нужно выносить в структуру, вынести в один параметр, который будет структурой, потому что иначе это будет сложно читать и использовать. Если необязательных параметров больше трёх, это тоже плохо. А надо сгруппировать это всё в структуру на или разбить одну большую функцию на несколько простых тогда. То есть у нас получается два варианта. Либо мы вместо кучи параметров используем структуру и туда передаём параметры, либо разбиваем саму функцию на много маленьких функций, у которых будет меньше параметров. Вы это всё тоже можете видеть в БСП или в стандартных конфигурациях. Там как раз такой подход используется. Чем ещё удобно использовать структуру в качестве параметров? Если нам понадобится добавить новые параметры, то при использовании структуры это намного проще. Мы просто докидываем туда, сколько нужно нам новых параметров без изменения структуры самой функции.
469. Правила создания общих модулей. Бизнес-логику рекомендуется размещать в общих модулях, а не в обработчиках форм. Вы, наверное, это часто слышали, что вот есть форма, то есть не нужно там какую-то глобальную логику именно в части бизнес-процесса реализовать, а нужно всё это вынести в общие модули и то есть там всё реализовать. А модуль формы он предназначен именно для работы больше с элементами формы и так далее, с событиями формы. А вся бизнес-логика должна реализовываться где-то там снаружи в общих модулях, ну или хотя бы в обработках. Откуда это взялось? Я не нашёл этого явного в стандарте, но всегда так вот говорят все и советуют именно так. Косвенная есть ссылка в этом именно стандарте. Что тут написано? Серверные общие модули предназначены для размещения серверных процедур и функций, недоступных для использования из клиентского кода. В них реализуется внимательно внутренняя серверная бизнес-логика приложения. Вот как раз эта фраза нам и говорит о том, что вот там мы должны реализовывать всю бизнес-логику. Таким образом, подытожим. А общий модуль - это не помойка, куда ты скидываешь там всё подряд, ээ, что тебе кажется лишним где-то там в форме и так далее. Это логическо оформленное место, где хранятся процедуры и функции, которые решают конкретную задачу. Вот я вам сейчас как раз вкратце рассказал, о чём этот стандарт. То есть общими модулями мы делаем прообраз архитектуры бизнес-логики. Поэтому это очень важно. хотел, казалось бы, ну, создал общий модуль, туда, написал какие-то процедуры, ну, и отлично вызываем их извне, всё будет хорошо. Но если мы какую-то серьёзную доработку делаем, то нам нужно всё-таки изначально продумать, какие у нас будут общие модули, как они будут между собой взаимодействовать и что конкретно там будем хранить. Заранее продумать бизнес-логику и только потом уже будем писать код. Создание и настройку общих модулей лучше всего доверять и согласовывать с вашим архитектором, которым у вас там самое главное по конфигурации. Ещё немного про то, как называть эти модули. То есть название должно быть соответственно по смыслу. Если модуль обрабатывает файлы, называем его работа с файлами. Если логируют ошибки, логирование ошибок и так далее. Не нужно в названии писать функции работы с файлами, так или процедуры работы с файлами. Это и так понятно, что там будут внутри процедуры и функции. Надо сфокусироваться на сути того, что будет внутри модуля. Также вы можете ещё заметить, что некоторые общие модули в БСП помеченные постфиксами. глобальный, полные права, пофт ип, то есть с повторным использованием, переопределяемый. Есть ещё постфиксолокализация. С этим вы уже разберётесь самостоятельно. Подведём итоги, что общий модуль - это не просто кусок какой-то кода, это единица архитектура, как я уже сразу сказал в самом начале, надо думать о нём как отчасти какой большой конструкции, где всё должно быть чисто, понятно и по назначению. Один модуль- одна задача. Там есть чёткие права, понятное имя и никакой каши.
Далее перенос выражений 44. Казалось бы, что такого в переносе выражения? Переноси как хочешь. А, ну я за свою практику насмотрелся столько вариантов переносов дикого совершенно вида, что я теперь, конечно, понимаю, зачем был разработан этот стандарт. Главная идея такая, что если строка кода получается длинее 120 символов, то переносим. Даже если у вас большой монитор, тогда код будет читаемый, понятный на любых разрешениях, любых мониторах. Потому что если у вас там стоит пятидесятидюймовый монитор, не факт, что у других разработчиков будет такой же и не факт, что у вас завтра будет такой же монитор. Ну и опять же вот так вот головой мотать влево-вправо, это не очень удобно. Потом, как говорится, шея будет болеть. Если у тебя есть длинная формула с арифметикой, то нужно её разбить. Знаки плюс-минус ставятся в начале новой строки, а не в конце. Это, кстати, важно, потому что многие привыкают ставить в конце и так вот и работают годами, а нужно по стандарту ставить в начале. Когда склеиваем строки, тоже их нужно переносить. Лучше ставить плюс тоже в каждом начале строки. Ну, если так читать неудобно, то можно наоборот. В стандарте говорится, что главное, чтобы это нормально читалось в этом случае. Э, если передаётся много параметров в процедуру, каждый параметр пишется с новой строки или равняется по отступу. Скобку и точку с запятой ставятся на той же строке, что и последний параметр. Это тоже важно, потому что кто как привык, тот так разнобой можете и писать, особенно начинающие программисты. А значит, дальше, если пишем длинное условия в в секции если, то нужно разбивать их на части. Каждый логический блок с новой строки, а логические операторы и или, соответственно, как и плюсики, тоже в начале строки. Так глазами проще будет воспринимать логику и потому что, соответственно, стандарт есть в БСП. К БСП мы считаем за эталон. Если мы пишем длинную строку в запросе или в тексте, то не нужно всё пихать в одну строку, нужно её разбить. А если строка потом показывается пользователю, то тогда её лучше оставить единой, чтобы не было разрывов. Запомните, что это нужно не компилятору, это нужно не каким там абстрактным стандартам. Это нужно лично вам и вашим коллегам, в конце концов, чтобы ты сам мог читать свой код через месяц быстро и без боли.
440 - это использование дублирующего кода. Это моё любимое. постоянно, постоянно, даже если разработчик уже 5 лет проработал, а у него всё время как, ну, у некоторых разработчиков, да, они так рука и тянется, скопировать и вставить, как говорится. Почему бы и нет? Значит, что нам говорит стандарт, когда ты копируешь кусок изменений, это называется дублированием. У этого есть минусы, а кто бы мог подумать, да? Во-первых, ты копируешь не только логику, но и ошибки. Если в оригинале были баги, то они теперь будут в двух местах или в стольких местах, сколько ты сделаешь копипа. Теперь, если вы захотите там что-то исправить, хорошо, если вы вспомните, что этот кусок кода у вас пользуется ещё много где. А обычно всё бывает во вральном режиме, естественно, вы в одном месте поправили, как в Таске у вас указано, да, как на канбане взяли, исправили, а про остальные куски кода, ну, редко кто вспоминает. В этом большой минус дублирования кода. Если это нужно исправить, легко забыть где-то там из десяти этих копипастов, где вы там ещё это накопировали, и обязательно забудете. Поэтому, если у вас встречается дублирующий код, избавляйтесь от него как можно скорее. Прилетела задача исправить баг. Нашёл исправил, ушёл довольный. Оказалось, что прошлый разработчик, например, ну, может, даже не вы сами, а прошлый разработчик мог сделать 10 копий функций. Ещё бывает так, что он вроде как копипа сделал. Ну, в каждой из этих функций либо он сам по чуть-чуть логику потом поменял по ходу дела, либо другие разработчики потихоньку там своё добавляли согласно тоже задачам на канбан доске, и теперь тебе надо будет найти все эти места, 10 раз понять, что там поменялось, и исправить везде в десяти местах. И вот вместо одного раза ты сделаешь уже 10 раз. Пам, внимание, барабанная дробь. ты таким образом ещё дополнительные ошибки туда внесёшь, потому что здесь ты исправил исправленную логику, здесь ты недопонял тот алгоритм, который был изменён тем разработчикам. И, пожалуйста, вот новые ошибки. И опять же это всё нужно, ну, по-хорошему передать на тестирование. В общем, хорошо, хорошо подумаете. Надеюсь, я вас напугал вот этой информацией. Но по стандарту допускается дублирование кода. Э, в некоторых случаях, например, если ты понимаешь, что в будущем эти два фрагмента будут сильно отличаться, то есть они пойдут по разной логики, тогда, да, разумно, временно их скопировать, а потом уже развивать их независимо.
Стандарт 438. Проверка на пустой результат выполнения запроса. А значит, когда ты выполняешь запрос в 1С, важно быстро и правильно понять, есть в нём данные или нет. То есть есть не есть вот такие задачи, когда нужно сразу разобраться. Есть данные, делаем то-то. Нет данных, делаем то-то. Тут всё просто. Если тебе не нужны сами данные, а только нужно узнать, пустой результат или нет, тогда используй метод результат пустой. Почему это важно? Потому что если ты сразу начинаешь перебирать выборку, даже если просто вызываешь этот слово следующий, то система уже начинает подгружать данные из выборки. А это приводит к лишним расходам серверного времени. А если ты хочешь просто проверить нали наличие строк, то есть пустой запрос или не пустой, тогда это лишняя нагрузка. Правильно. Так, выполнить запрос и сразу проверить пустой. А если не пустой, значит, есть строки. Вот. А вот если нам нужно потом реально обрабатывать результат запроса, то есть ты собираешься его выгружать или перебирать или что-то с ним делать, тогда пустой вызывать вообще не надо. Просто сразу берёшь выборку и пошёл в цикл. Вызов пустой здесь будет лишним. Он только лишнюю нагрузку создаст. Хотя, конечно, маленькая нагрузка, но всё равно лишняя вызов. Этот стандарт довольно интересный, хотя и короткий. Я встречал в некоторых чатах, форумах, что надо всегда проверять на пустоту запрос. Якобы так стандарт диктует. Но на самом деле вот вам я описал стандарт, зачитал его. Можете по ссылочке перейти. Тут нет прямо такого правила.
Стандарт 758. Псевдонимы источников данных в запросах. А что тут главное? Главное то, что псевдоним - это не техническая формальность. Он должен говорить по смыслу, что это за таблица и зачем она в запросе. А, например, если ты выбираешь остатки товаров, то называем источник остатки на складе, а не таблица два или регистр. Имя должно быть читаемым, без пробелом, каждое слово заглавной буквы. Ну не нужно сокращать до одного символа. Это не экономит память, это добавляет путаницу. Ну, например, представьте, что вы сделали псевдоним под названием П с буквой П. И представьте, что вам потом нужно будет поиском это найти. То есть вы на вы нажимаете найти строку, и ваша буква П везде там в тысячи в миллион раз встречается во всех текстах. То есть как вы будете с этим потом разбираться? Память это действительно не экономит. Называйте псевдонимы нормально. Не начинайте с подчёркивания. Такие имена выглядят как системные и мешают восприятию. Нельзя называть источник просто справочник, документ. Это ни о чём не говорит. Если ты делаешь универсальный механизм, где подставляется произвольная таблица, тогда допустимо использовать нейтральные имена типа таблица. Это нормально, когда ты работаешь, например, с динамическими отчётами, которые собираются каким-то образом предварительно и потом из них собирается большой-большой запрос. или какую-то, может, универсальную обработку делаешь, где нужно вот с помощью алгоритма собрать текст запроса и потом этот запрос передать уже на выполнение. Тогда, да, тогда можно будет какие-то общие имена называть, типа таблица или реквизит один, реквизит 2. Вот. Но если в общем, то такие вещи универсальные пишутся редко, и они пишутся прямо серьёзными уже разработчиками, потому что это сложно. То есть, пока ты начинающий, старайся называть всё по стандарту. Вот 758.
Стандарт 437. Оформление текстов запросов. Первое правило: все ключевые слова пишем заглавными буквами. Выбрать из, где сгруппировать по и так далее. Казалось бы, ну зачем я вам об этом рассказываю? Вроде как и так всё понятно. Ну, ребят, я столько насмотрелся в своей жизни чужого кода, поэтому на всякий случай я всё-таки решил ещё раз об этом напомнить. Пишите всё с большой буквы. Я видел это даже пишут и с маленькой буквы. И начинают с большой, а потом пишут маленькими. Вот. Пишите правильно по стандарту, чтобы это не резало глаз, чтобы было всё понятно. То, что вы пишете не по стандарту, это потом за них глаз цепляется и мозг вместо того, чтобы думать продуктивно над алгоритмами, как это всё сделать, он начинает напрягаться и пугаться. А почему тут так вообще написано? Что за ерунда? Второе. Всегда явно задавай псевдонимы для полей. Будьте аккуратны с полями, вроде касса. Наименование. Вот по умолчанию система сделает псевдоним валюта наименования. Но может быть вам удобнее будет работать с псевдонимом типа наименования, поэтому не забывайте писать после слова как псевдоним этого поля. Это очень важно. Запрос должен быть оформлен в виде блока с отступами не в одну строку. На экране вы видите как раз пример, как обычно мы все и оформляем, как как это делает конструктор запросов. Но на всякий случай я напомню об этом, чтобы вы не забыли. даже если это короткий запрос, то он всё равно выглядит более структурированным. Опять же, мозг меньше будет напрягаться, просматривая такие запросы. Если ты работаешь в конструкторе запросов, ещё раз напомню, то комментарии удаляются. То есть, если вы туда помещаете свои комментарии, например, написали запрос, добавили туда комментарий между строчками или между запросами в пакете запросов, да, вы добавили какой-то коммент, потом вызвали конструктор запросов и комментарий удалился автоматически. Ситуация, конечно, странная. При этом в этом стандарте фирма 1С рекомендует использовать комментарии для сложных запросов. И в то же время она же тут же и пишет, что эти комментарии будут удалены конструктором запросов. Тут какая-то странная логика у них. Ну, сделали бы так, чтобы они не удалялись эти комментарии. Я не вижу проблем. То есть, ну, решили не дорабатывать. Вот так вот мы и работаем. Если ты собираешь запрос программно, например, из нескольких кусков кода, динамически подставляешь название полей и таблиц, то рекомендуется комментировать все этапы сборки. Рекомендуется писать так, чтобы запрос можно было потом открыть в конструкторе запросов без ошибок. То есть пишите его так, чтобы правой кнопкой нажали, вызвали конструктор запросов и у вас он открылся в конструкторе. Потому что это помогает при отладке, проверке. У вас сразу видно, что ошибок в запросе нет. По крайней мере, на первом этапе. Если ты делаешь пакет запросов или временно отключаешь какую-то часть этой конструкции, то не нужно использовать трюки с комментированием внутри текста, типа как две косые поместить. То есть не нужно так делать. Лучше писать запрос, как будто он полный, а потом уже рекомендуется программно удалять или заменять куски запроса. Так будет код более предсказуемым. То есть так нам советуют этот стандарт.
Стандарт 729- оптимизация кода для производительности, особенно запросов и алгоритмов. Прежде чем как начать оптимизировать запрос, сначала задай себе простой вопрос: нормально ли вообще сам запрос? Потому что часто проблему создаёт не кривой индекс или СУБД, а слишком какой-то жирный большой запрос, слишком навороченный запрос или слишком много полей из него выбирается. Он, может быть, делает много лишнего. Прежде чем думать, как между собой поля состыковать, какие-то временные таблицы сделать, надо сначала подумать. А может быть, я просто слишком много информации СБУД тащу. И как раз из этого следует правила, что выбирать нужно только нужные данные. Если тебе нужно два поля, не нужно выбирать все поля. Второе, не нужно загонять всё в один запрос любой ценой. Сложный универсальный запрос - это не всегда хорошо. Иногда проще сделать несколько простых запросов под разными условиями или вообще сделать выборку попроще. Потом чуть-чуть доработает результат уже в 1С уже, когда в цикле работаете. Может быть, это будет быстрее, чем если СУБД начнёт думать, составлять какие-то гигантские планы запросов и пытаться выполнить твои вот эти сложные конструкции и пытаться вычислить какие-то сложные поля. Почему это важно? Потому что оптимизатор в СУБД может выбрать плохой план выполнения. SQL будет очень сложно подобрать план выполнения запроса оптимальный. Поэтому рекомендации в этом стандарте такие, что не нужно описать вложенные запросы просто так, ради красоты. Избегаем сложных условий, где, особенно с выбором или с подзапросами. Минимизируем количество таблиц, потому что даже пять-7мь источников уже могут начать тормозить. Если хочешь проверить,
Как СУБД будет выполнять твой запрос, смотри план выполнения VQL Management Studio, например. То есть, ещё раз, о чём нам говорит этот стандарт? Он говорит о том, что прежде чем перейти к более продвинутым методам оптимизации, необходимо убедиться, что сам запрос адекватен решаемой задаче. Минимизируем количество запросов, то есть убираем запросы в цикле. Не нужно пытаться любой ценой перенести все расчёты именно в запрос. То есть, может быть, какие-то задачи можно сделать обычным циклом, перебором каким-нибудь, поместить в массив там. То есть не нужно пытаться любой ценой перенести выполнение задачи именно в SQL. Отправка запроса в MSSQL - это большие накладные расходы.
Что нам ещё говорит этот стандарт? Что не следует добавлять вложенные запросы только для повышения отчитаемости. Я думаю, тут всё понятно. И здесь говорится о том, что если таблиц более 7-ми, то оптимизатор СУБД в этом случае затрудняется создать оптимальный план выполнения. Оптимизатор СУБД тратит много времени на анализ запроса, что тоже очень плохо. Интересная книга. Ну, давайте продолжать.
С номера 657. Обращение к виртуальным таблицам. Самое главное правило: все условия, которые относятся к виртуальной таблице, нужно передавать прямо в параметры таблицы, а не писать их в секции WHERE. Почему это важно? Потому что если напишешь условие в WHERE, 1С может сначала вытащить всю виртуальную таблицу, а потом уже начать отбирать нужные записи, а это тормоза, особенно на больших регистрах. И раз за разом я всё равно периодически встречаю это в коде других разработчиков, даже если это не начинающие программисты. У нас есть виртуальная таблица, например, остатки. У них есть параметры, и там есть как раз место для того, чтобы мы могли указать там отбор. И так СУБД сразу получает чёткую команду, что нам нужно выбрать виртуальные таблицы только по складу.
Но с этими параметрами не всё так просто. Тоже надо быть аккуратными. Только простые условия туда рекомендуется пихать. Не нужно туда пихать подзапросы. Слово "или" обращение через точку, соединение. Если туда втыкаешь, например, "Документ.Ссылка.Проведен" или "Документ в подзапросе", то СУБД, скорее всего, подберёт нерациональный план запроса, и запрос начнёт выполняться медленно. Тут нужно каждый раз подходить уже, так сказать, творчески, исходя из своего опыта, исходя из того, какая СУБД у нас. Скорее всего, вы уже не в параметры эти условия будете пихать, а, скорее всего, всё-таки в секцию WHERE, либо через внутреннее соединение и так далее. В целом 1С рекомендует использовать всегда параметры. Если невозможно простые параметры поставить, тогда делаем временную таблицу, заполняем её нужными значениями и потом уже используем её как фильтр. Это надёжнее, более читаемо и работать будет быстрее.
Ещё один момент. Если у тебя несколько условий с подзапросами, выбираем то, которое лучше всего отфильтровывает данные и оставляет меньше всего записей. И тогда помещаем это в параметры, а остальные накладываем уже на получившийся запрос, то есть в секцию WHERE. Ещё момент. Если у тебя сложное ограничение прав доступа RLS, которые неявно, естественно, добавляют подзапросы, то виртуальная таблица начнёт работать медленно или может начать работать медленно. В таких случаях используем временные таблицы или привилегированный режим, который отключит RLS. Их не нужно бояться, иногда это просто необходимо. Вывод простой: всё, что можно в параметры. Всё сложное наружу. Временные таблицы - твой друг.
477. Самодостаточность регистров. Такой вроде коротенький, но я, наверное, скажу больше, чем в этом стандарте. Во-первых, регистр должен быть независим от регистратора. Все нужные данные должны храниться внутри него. Что это значит? То есть, если у тебя есть, например, какой-то сложный отчёт, который получает данные из этого регистра, а потом ты уже, так сказать, обогащаешь данные уже из регистратора, например, подтягиваешь статус из "Заказа покупателя". То есть мы получили данные из регистра, поняли, что не все данные там есть, и решили обратиться к регистратору и ещё немножко оттуда данных взять. Правильнее будет добавить вот этот недостающий реквизит, перепроектировать этот регистр, добавить туда этот реквизит и заполнять его уже каким-то образом дорассчитывать сразу, чтобы всё хранилось в регистре максимально и чтобы не было обращения через точку каждый раз при формировании отчёта. Ну, здесь нужно сделать какую-то ремарку, что если у нас какие-то отчёты, которые запускаются раз в год и формируются 10 секунд, конечно, нам не нужно никаких перепроектирований делать. Здесь имеется в виду, что если у вас отчёты строятся долго, большие данные извлекаются или регистр у вас большой, ну или для вас критична нагрузка на SQL, то есть он у вас перегружен по полной программе, и там каждая миллисекунда играет роль, тогда, конечно, нужно сразу всё перепроектировать, всё, чтобы было в регистре. А если у вас какая-нибудь бухгалтерская база данных с маленьким набором данных, конечно, ничего менять не нужно. Вот об этом этот стандарт.
Ещё момент тут как раз про распределённую базу данных. Они пишут, что важный момент: обращение к полям регистратора замедляют запросы и могут не работать в распределённой базе, где регистратора может не быть. Иногда, согласно настроек распределённой информационной базы, у тебя может быть запись, которая прилетела из соседнего узла, а ты не можешь обогатить данные, потому что регистратора и нет. Он остался в другом узле. Эта ситуация может быть вполне, поэтому это надо иметь в виду. Если у вас распределённая база данных, хотя лично я считаю информационные распределённые базы большим злом, и нужно другими способами, конечно, пользоваться, стараться максимально избегать РИБов. Ничего хорошего я в своей жизни от них не видел.
Следующий стандарт номер 648. Ответственное чтение данных. Когда ты читаешь данные, чтобы на их основе что-то изменить или передать куда-нибудь вовне, например, из веб-сервиса, как что-нибудь возвращаешь, то есть к тебе прилетел внешний запрос через веб-сервис или HTTP-сервис, и ты должен что-то вернуть. Например, может быть, при проведении документа, при обмене с внешней системой или, допустим, массовая обработка записей. Здесь нужно ответственно действовать. Вот об этом стандарт. Что это значит? Это значит, что нельзя просто прочитать данные, изменить их и сохранить. Так можно легко попасть в ситуацию, когда другой пользователь за это время уже что-то ещё изменил, и твои действия, твои данные становятся некорректными. Опа, внезапно. Да. Чтобы этого не случилось, сначала нужно установить блокировку на те данные, с которыми ты будешь работать. Если ты планируешь их изменить, это должна быть исключительная блокировка. Если ты только читаешь данные, то можно ограничиться разделяемой блокировкой. Настоятельно рекомендуется, что обязательно нужно обрабатывать возможные ошибки. Если в процессе что-то пошло не так, нужно обязательно откатить транзакцию и сохранить информацию об ошибке, например, в журнал регистрации. Такой подход защитит данные от одновременных конфликтов и потерь. Ну, конечно, есть исключение: если ты строишь отчёт, отображаешь список или просто ищешь данные, которые никто не меняет, например, на какие-то настройки пользовательские или справочное значение. Тут вообще можно читать без блокировок. Это же касается мобильных приложений, монопольного режима, когда ты вообще один в базе находишься, или при работе с условно постоянными объектами или какими-нибудь, может, справочниками, которые никогда не изменяются. Но если ты после чтения данных хоть что-то там меняешь или передаёшь во внешнюю систему, ты просто обязан использовать блокировочки и транзакции.
Стандарт номер 783. Правильное использование транзакций для целостности данных. Он немножко перекликается с прошлым моим стандартом, который я только что рассказал. Ещё раз вспомним, что транзакция - это способ гарантировать, что все действия с базой данных выполняются либо полностью, либо не выполняются вообще. Вот в этом суть транзакции. То есть либо всё, либо ничего. Особенно важно использовать их, когда вы меняете связанные между собой данные. Про это многие забывают. Например, при проведении документа, записи в несколько регистров или в каких-то ответственных бизнес-процессах. А, например, вы создали номенклатуру и к ней создали связанную с ней единицу измерения. Если что-то пойдёт не так при записи единицы измерения, то вам надо откатить всю транзакцию, то есть не создавать тоже номенклатуру. Иначе номенклатура у вас создастся без единицы измерения. То есть это будет ошибочный элемент справочника, который непонятно потом к чему приведёт, потому что, как вы знаете, в единицах измерениях много чего хранится, там какие-то коэффициенты, какие-то ещё взаимосвязи.
С транзакциями в 1С есть особенности, которые надо и будет учитывать. Во-первых, вложенные транзакции не поддерживаются. Это особенность движка. Как бы можем открыть транзакцию внутри транзакции, но по факту это не будет работать как вложенная транзакция. Это, по сути, единая транзакция. А, во-вторых, если в процессе произошла ошибка, и даже если мы её обработали, то завершить транзакцию мы уже не сможем. И попытка это сделать приведёт к исключению, то есть мы должны её откатить. Поэтому транзакции нужно всегда начинать и заканчивать строго по парам. То есть, если вы внутри транзакции открыли ещё одну, то надо обязательно закрыть и предыдущую, и вложенную, и внешнюю. А насчёт блоков "попытка-исключение", здесь также говорится: правильный подход - начинать транзакцию сразу перед блоком "попытка" и потом в этом блоке делать всё необходимое: блокировки, чтения, записи и тому подобное. И только в самом конце блока вызывать "зафиксировать транзакцию". А если что-то пошло не так, в блоке "исключение" обязательно надо вызвать "отмену транзакций", записать ошибку в журнал и, если это необходимо, повторно выбросить исключение, ну, наверх, если есть более вышестоящая транзакция. Такой подход поможет избежать самых сложных при отладке ошибок, когда платформа просто говорит: "В транзакции уже были ошибки". Вы, наверное, такое видели, когда вот такая странная ошибка и нигде непонятно, где это искать. По правилам вот этого стандарта нужно сделать так, чтобы такой ошибки не было, чтобы всегда программист мог быстро диагностировать, почему же случилась ошибка и где её искать.
Значит, иногда транзакция может начинаться неявно, например, когда вы вызываете метод "Записать". То есть это системное событие, тут вообще не нужно открывать транзакцию, это избыточно. И этот стандарт как раз про это и говорит, не рекомендует лишние транзакции открывать, потому что что? Потому что 1С сама откроет транзакцию автоматически при записи. Но если вам нужно ответственное чтение, вспоминаем предыдущий стандарт, или вы, допустим, ищете связанные объекты, то тут без явного начала транзакции вы не сможете обойтись. Так вы защищаетесь от изменений, которые случайно, ну или специально может внести другой пользователь в параллельной сессии. А когда у нас многопользовательская работа, обязательно кто-нибудь зайдёт и что-нибудь поменяет.
Значит, про длительные транзакции также поговорим. Важно помнить, что длительные транзакции - это очень плохо. Стандарт об этом прямо говорит, что они захватывают много ресурсов, удерживают блокировки, нагружают сервер, базу, мешают другим пользователям. И стандарт говорит о том, что если транзакция длится дольше 20 секунд, а нужно делить обработку тогда на порции, оптимизировать запросы, избегать внешних вызовов и не тянуть в транзакцию какую-то тяжеленную логику, которую можно выполнить заранее. То есть всё, что можно выполнить до транзакции, выполняйте до неё. И, наконец, если вы отключаете итоги перед записью регистра, тогда не забывайте, что это обязательно должно происходить внутри транзакции, иначе система выдаст ошибку другим пользователям, которые в этот момент попытаются прочитать итоги. Вот, если честно, мне кажется вообще странным отключать итоги для транзакции, которая длится 20 секунд. Это вообще что такое? Ну, если вы знаете подобные примеры, напишите в комментариях, когда вот, может быть, встречали в своей практике такое. Я не встречал.
465. Обработка событий и поведение объектов. Здесь он очень коротко описано, но я, наверное, скажу больше, чем тут написано. В обработчике события "При записи" объекта, это касается и документов, и регистров, и констант, обычно выполняются действия по записи связанных данных в другие объекты или запускаются какие-то связанные процессы. Ну, тут важно помнить, что сам объект на данном этапе уже записан в базу данных, поэтому что? Поэтому запрещено менять содержимое объекта, которое мы записываем вот в этом событии "При записи". Некоторые пытаются там это всё менять. Это потому что бесполезно и приведёт к ошибкам. И ещё одно строгое правило: все действия в этом обработчике должны выполняться только после проверки на флаг "ОбменДанными.Загрузка" - это защита от лишних действий при загрузке данных из внешних систем. Ну или когда вы создаёте какой-то объект автоматически. Вот про этот флаг. "ОбменДанными.Загрузка" - многие программисты, даже опытные, часто забывают, а он очень-очень важный, этот флаг. Таким образом, вы избегаете вычисления лишней логики в этом обработчике. И если вы посмотрите типовые конфигурации, там этот флаг везде-везде используется практически в самом начале обработчиков "При записи", "После записи" и так далее. Это сделано для того, чтобы облегчить автоматическую логику. То есть, когда у нас записывает что-то пользователь интерактивно, этот флаг, естественно, не взведён, и все проверки, которые идут после него, все обработчики, они срабатывают. А если мы, например, хотим быстренько записать какой-то объект автоматически в регламентном задании или из внешней системы, то мы уже знаем, что нам никакие проверки не нужно делать, и мы просто этот флаг включаем, и мы избегаем всю лишнюю логику, которая там прописана. Очень удобная штука. Рекомендую пользоваться и поглубже разобраться, как это работает.
Следующий стандарт 464 - это обработчик событий "Перед записью". Вызывается он перед тем, как объект будет сохранён в базу. Здесь как раз можно выполнить все нужные проверки, правильное заполнение реквизитов, связанные они с внешними данными или нет. Нужно что-то дополнительно, может быть, рассчитать, дозаполнить. Также здесь можно сравнить текущие значения с теми, что были до редактирования. Часто это нужно в алгоритмах, чтобы понять, ага, вот у нас раньше этот реквизит какой был, а какой сейчас? И исходя из этого, ну, может быть, статус поменялся, да, и исходя из этого какую-то логику прописать. И вот здесь в этом обработчике рекомендуется это и делать. Ну, тут есть обязательное правило, которое описано в этом стандарте. Все действия в этом обработчике выполняются тоже после проверки флага "ОбменДанными.Загрузка". Про него важно помнить всегда, когда вы работаете с этими обработчиками. Это тоже важно, чтобы не мешать автоматической загрузке данных из других систем. Вот. А новички постоянно про это забывают.
Следующий интересный стандарт 740 - безопасное хранение паролей. Частенько их хранят в открытом виде. Ну давайте посмотрим, что говорит нам стандарт. Если ваша подсистема работает с внешними ресурсами: почтой, веб-сервисами, FTP и так далее, логины и пароли лучше не хранить в этой базе вообще. Ну что же делать, скажете вы. Да, особенно в файловой базе. Там любой пользователь может скопировать файл, получить доступ. Отлично. Надёжнее запрашивать пароль у пользователя каждый раз и использовать не сохраняя. Замечательная рекомендация. Но что дальше мы будем делать с этим, да? Заставлять пользователя каждый раз вводить пароль. Кто-то из вас пробовал такое делать? Напишите в комментариях. И к чему это привело. Далее, что пишет стандарт. Они понимают, что это, скорее всего, невозможно будет сделать, и они пишут следующее: "Если без этого не обойтись". Например, также требуется работа серверной логикой без участия пользователя. Ну, хранить пароль можно, но только с осознанием рисков и по определённым правилам. Значит, не пишем пароль в обычные реквизиты, используем отдельный объект. А в БСП есть возможность использовать безопасное хранилище паролей. Там пароли хранятся в зашифрованном виде, не попадают в обмен данными и работать с ними можно только в привилегированном режиме. На форме мы не передаём пароль в открытом виде. Вместо этого подгружаем его на сервере и маскируем уникальным идентификатором, чтобы он никому не засветился. И для этого существует в БСП специальные даже уже готовые функции для работы с этим безопасным хранилищем паролей. Типа "ЗаписатьДанныеБезопасноеХранилище", "ПрочитатьДанныеИзБезопасногоХранилища", "УдалитьДанныеИзБезопасногоХранилища". Так что, кто до сих пор хранит свои пароли в базе данных или, не дай бог, в коде, обратите внимание.
Стандарт номер 499. Перехват исключений в коде. Звучит страшно. Про что же этот стандарт? Давайте разберёмся. Что в большинстве случаев исключение в 1С перехватывать не нужно. Если в коде произошла ошибка, платформа сама отобразит её пользователю, запишет журнал регистрации для администратора и, если настроено, отправит в сервис регистрации ошибок. При этом для распространённых ситуаций уже предусмотрены стандартные шаблоны сообщений и рекомендаций. Однако есть особые случаи, когда технический текст ошибки слишком непонятен пользователю. А это касается, например, проблем с внешними сервисами, электронной почтой, веб-интерфейсами, криптозащитой. Когда ошибка происходит в такой зоне, можно и нужно добавить к сообщению пояснение, чтобы человек понял, в чём именно сбой и что можно с этим сделать. Например, если не удаётся вам отправить письмо, то можно вывести сообщение для пользователя вроде как "проверьте настройки почтового сервера". Почтовый сервер может выдать ошибку, например, какая-нибудь там 258 ошибка, да? И вот если пользователь это увидит, он ничего не поймёт. Поэтому мы должны ему выдать понятную, человекочитаемую ошибку. Главное не прятать саму ошибку, а дополнить её понятным комментарием. И при этом важно не оборачивать весь ваш код в огромный блок "попытка-исключение". Я видел, что люди вот в своём кастомном коде вот начинают свою функцию прямо с "попытки-исключения" и вот весь код свой оборачивают. Ну просто чтобы не заморачиваться с поиском ошибок в алгоритме, как говорится. Если сбойнуло, то всё в исключение выпадет, а так делать не нужно. Стандарт это прямо запрещает. Нужно точечно обернуть именно тот вызов, который может упасть. Об этом, кстати, написано в синтакс-помощнике. Все те функции, которые могут упасть с выдачей ошибок, они там прямо так написано: "будет выдано исключение". И вот именно вызов этих функций нам нужно оборачивать в блоке "попытка-исключение". Не нужно вообще весь код оборачивать в этот "попытка-исключение". Это неправильно. Так мы не будем маскировать ошибки, которые вообще не связаны с нашим участком кода. Вообще я видел, конечно, такое, там целые огромные куски кода там были обёрнуты в "попытку-исключение". Это, конечно, сразу говорит о низком профессионализме человека, который это делает. И при этом вот это очень важно: то есть лучше не использовать устаревшие функции типа "ОписаниеОшибки". Напишите в комментариях: "А вы до сих пор используете эту функцию "ОписаниеОшибки"?" Или всё-таки вы более современно используете? Почему не использовать? Потому что стандарт говорит, что она не показывает стек вызовов, она показывает только текущую ошибку. И также не стоит использовать краткое описание ошибки вместо подробного сообщения для пользователя. Категорически нельзя полностью подавлять исключения, особенно без записи в лог. То есть вот здесь вот в стандарте пункт 32 как раз вот здесь "попытка", а "исключения" ничего нету. Вот это неправильно. Такая практика приводит к тому, что системный администратор, ну или какой-то пользователь ответственный не видит, что пошло не так, и не может провести диагностику. Вместо этого, даже если исключение не показывается пользователю, его нужно записать хотя бы в журнал регистрации с пояснением, почему оно произошло. То есть не оставляйте секцию "исключение" пустой, иначе вы не сможете никогда узнать про эту ошибку. И это будет затруднять вам потом поиск этой ошибки. Потому что пользователи придут рано или поздно, скажут: "А вот у нас там ошибка". А вы не будете даже знать, где она возникла эта ошибка, потому что секция "исключение" у вас пустая, ничего никому не показалось и ничего никуда не записалось.
Значит, если используется Библиотека стандартных подсистем, то лучше применить специальную функцию "УточнениеИсключения". Она находится в модуле "ОбщегоНазначения.Сервер". Вы вот её там можете найти, и она позволяет аккуратно дополнить сообщение об ошибке и при этом сохранить её структуру. Это делает текст понятным пользователю, а лог обогащает полезными данными для разработчика. И, кстати, напишите, вот используете ли вы вообще БСП для обработки ошибок или нет?
Далее, стандарт номер 693. Использование объектов типа "Структура". Значит, о чём у нас этот стандарт? В языке 1С объект типа "Структура" вообще удобен для хранения наборов именованных значений. Когда создаёте структуру, не передавайте в конструктор более трёх значений сразу, иначе код становится громоздким и трудночитаемым. Лучше создайте структуру вообще пустой, а потом добавляйте значение по одному через метод "Вставить". Мы видим здесь на экране такая вот громоздкая конструкция. Она, конечно, очень нечитаемая. А вот по правилам сразу видно, всё чётко понятно, какое значение, какой ключ. Кроме того, не стоит в конструкторе структуры создавать другие структуры, особенно если у них тоже есть параметры. Это сильно затрудняет чтение кода. Гораздо понятнее, когда сначала отдельно создаются нужные структуры, а уже потом эти структуры добавляются в другую структуру. А ещё одна ошибка - это передавать в структуру вызовы функции с множеством параметров прямо в момент создания. Это делает строку слишком длинной и трудной для анализа. Вместо этого лучше сохранить результат вызова функции в переменную, а потом добавить её в структуру. Если вы работаете с какими-то временными структурами, которые вы используете в большом количестве участков кода, например, вы в начале функции создали и планируете работать с разными её значениями ключей. Значит, здесь пишется в стандарте, что не нужно добавлять в них свойства в разных местах программы и не нужно проверять наличие этих свойств через метод "Свойства". Лучше сразу создать структуры с полным набором нужных свойств и заполнить их значениями по умолчанию. Так код получается более надёжным и проще он для поддержки будет, для понимания. Потому что если вы вначале создаёте пустую структуру, потом по ходу ветвления алгоритма добавляете в неё свойства, в итоге вы в конце не понимаете, какие свойства добавлены, а какие ещё нет. Это приведёт к большому количеству ошибок и неудобству восприятия самого кода. Поэтому сразу создаёте структуру, все нужные ключи в неё запихали, присвоили им значение "Неопределено" и дальше спокойно работаете с этой структурой. Значит, но если структура создаётся на основе внешнего источника, например, если это результат запроса по HTTP или какие-то данные сбора, которые получены из терминала или из HTTP-сервиса, то тогда формат у нас получается нефиксированный, потому что вы никогда не знаете, какая структура вам прилетит из внешнего источника, и тогда проверка наличия свойств, она допустима. То есть вы можете проверить, а есть ли такой ключ через метод "Свойства". То же самое касается системных параметров формы или каких-то универсальных параметров выбора. Их можно тоже проверять через свойства. Стандарт об этом прямо говорит, и это не запрещает.
Стандарт номер 781. Особенности сортировки в таблице значений. Когда в таблице значений нужно применить сортировку по колонкам, которые содержат ссылки, нужно помнить о важной особенности. При такой сортировке система будет запрашивать представление каждой ссылки для каждой строки таблицы. То есть система будет предварительно обрабатывать каждую строку, получать для неё представление и по нему будет пытаться сортировать. А если строк много, сотни и тысячи, то получается много обращений к базе в цикле. И это, соответственно, сильно затормозит выполнение этой сортировки. Поэтому, если вам нужно отсортировать таблицу по наименованию, лучше сразу добавляйте в неё отдельную колонку, в которую положите представление ссылки. То есть строковое имя, строку, и по нему уже дальше можно спокойно сортировать. Главное, здесь надо убедиться, что само получение представлений на этапе заполнения, оно не создаёт такую же проблему с производительностью. То есть иногда проще и быстрее, конечно, будет отсортировать по ссылке. А если у вас поле сортировки какое-то громоздким способом вычисляется, то, наверное, проще тогда по ссылке стартовать. Вот. Но если вы стартуете по ссылке, делаете через объект "СравнениеЗначений". Кстати, напишите в комментариях, знали ли вы про такой объект, который существует в стандартных функциях 1С, в языке 1С. Есть такой объект, называется "СравнениеЗначений". Кстати, зайдите, посмотрите, кто не знал. Для этого вы создаёте объект, а потом вызываете метод "Сортировать" с нужными полями и этим объектом. Например, "Дата, Ссылка". Это значит, что сначала сортировка по дате, а потом по ссылке. И такой подход даёт хорошую производительность и позволяет избежать ненужных обращений к базе за представлениями.
Ну что, если хотя бы один из этих стандартов вам каким-то образом отозвался и пригодился или, может быть, заставил посмотреть на привычные вещи по-новому, значит, этот выпуск удался. Поставьте лайк, если было полезно. Делитесь с командой с вашей, пишите в комментариях, какие стандарты вы применяете и что бы вы добавили в этот мой список основных стандартов. Всем желаю продуктивного кода и поменьше костылей. До новых встреч. 1С - это сила. 1С наш бой. [музыка] Создаём миры в мониторе света. Создаём миры в мониторе света. 1С - это сила. 1С - это сила. 1С - это сила. 1С наш бой.