Добрый день, друзья! Меня зовут Алексей Степаненко. В 1С я уже практически 19 лет, застал еще платформу 7.5. За это время я был по разные стороны баррикад – и со стороны внедренцев, и со стороны конечного клиента.
Поскольку так получилось, что я для себя все-таки выбрал позицию in-house, я вижу, как эволюционируют системы с момента внедрения и в течение всего жизненного цикла. И сейчас я хочу с вами поделиться определенными концепциями – как надо изначально разрабатывать конфигурацию, чтобы облегчить жизнь в поддержке.
Я назвал доклад шуточно – «Есть ли жизнь после внедрения», потому что зачастую проблемы, которые возникают у нас в поддержке, связаны с неправильной разработкой, которая была на момент внедрения (только тогда мы этого не знали).
Проблемы на этапе поддержки решения
Смотрите, у нас есть:
- проект, у которого есть конечный срок, ограниченный бюджетом этого проекта;
- и есть поддержка, у которой у нас нет срока окончания – она у нас постоянная.
С чем можно сравнить работу на проекте? Допустим, когда мы строим с нуля город – у нас есть какое-то большое поле. Пожалуйста – делай, что хочешь, переделывай, сноси дома, строй заново. Никто не мешает.
А поддержка – это уже строительство в городской застройке. Внести какие-то серьезные исправления архитектуры на этапе поддержки довольно сложно. Мы понимаем, что уже задействованы многие подсистемы, между объектами много связей, в системе уже работают пользователи.
Почему-то в нашем сообществе есть такое мнение, что на поддержке должны работать начинающие специалисты. Я не совсем согласен с такой политикой. Мне кажется, что поддержкой все-таки должны заниматься квалифицированные разработчики. Потому что зачастую нужно быстро понять, где произошла ошибка, и что к ней привело. А это все-таки накладывает определенные требования к квалификации разработчиков.
Концепция минимизации разрушений типовой конфигурации
Многие из вас, наверное, сталкивались с ситуацией, когда после обновления все падает. Надо стремиться, чтобы такого не было, но, к сожалению, очень часто случается наоборот.
Этот слайд я взял из доклада Виталия Онянова, где он говорит, что следует всегда выбирать те способы решения задач, которые обеспечат более простое обновление конфигурации в будущем, даже если они несколько сложнее в реализации.
Прошу вас обратить внимание на фразу «несколько сложнее в реализации». Я бы подискутировал, потому что если бы они были «несколько сложнее в реализации», именно этими концепциями все бы и пользовались. Но, к сожалению, ими не пользуются, потому что они, все-таки, несколько сложны.
Вывод из предыдущего слайда – минимизация разрушений типовой конфигурации – это только добавление нового. Теперь посмотрим, как же мы будем добавлять что-то новое.
Формы
Допустим, мы меняем формы – добавляем на форму реквизиты.
- Самый простой способ добавить реквизит формы – перетащить его из списка реквизитов в дерево. Быстро. Три секунды.
- Если же добавлять реквизит кодом, то это можно сделать по аналогии с тем, как показано на слайде – создаем свою собственную процедуру и добавляем в обработчик «ПриСозданииНаСервере» вызов метода по изменению формы. Время разработки увеличивается, но в поддержке получается очень легко – когда мы производим обновление, мы видим, что изменения в модуле связаны только с добавлением новой процедуры, плюс добавляется строчка в типовой процедуре «ПриСозданииНаСервере».
Какие минусы?
- Неудобство разработки, конечно же.
- Не нужно забывать, что данный код будет отрабатывать при каждом открытии этой формы.
Об этом нужно всегда помнить, поэтому если у вас форма очень сильно доработана, и этот документ часто вызывается, или если у вас высоконагруженная система, то может получиться так, что данный подход вам не подходит – нужно будет придумать что-то новое.
Например, вы можете копипастом создать из стандартной формы свою собственную, но тогда вы должны понимать, что при обновлении на типовой релиз, вам нужно будет дополнительно проводить исследования, что же там изменилось.
Помимо этого есть еще один подход, как можно упростить изменение формы, уменьшив недостатки программной модификации – можно разработать фреймворк, где:
- структурно описать в табличном документе все изменяемые элементы формы – например, в первой колонке будет имя элемента, а во второй – его тип;
- разработать процедуру общего модуля для программной модификации форм, куда в качестве параметров мы будем передавать это структурированное описание изменений;
- а если у нас в нашем рабочем процессе еще будет такая процедура, как сборка (когда мы из исходных кодов собираем наш cf-ник или cfu-файл), то мы можем скриптом найти в модулях форм строчки, которые вызывают эту общую процедуру – ее вызов убрать, а эти структурные изменения, которые у нас есть в виде табличного документа, перенести в XML-описание формы, и после этого собрать cf-ник.
С одной стороны у нас получается удобство разработки и обновления. А с другой стороны мы убираем минус отрисовки формы каждый раз при открытии.
Движения документов
Следующее. Как мы дорабатываем движения документов.
- Самый простой способ – это вклиниться в процедуру «ОбработкаПроведения» документа. Но прямое изменение процедуры проведения я не рекомендую – наверняка все знают, что вместо этого нужно использовать подписки на события.
- Есть правило, что на одно событие должна быть одна подписка. Даже если у нас какой-то код выполняет одни и те же действия в разных подписках, то все-таки разделяйте их. Так удобнее в сопровождении. Да, это ведет к дублированию кода, но все-таки надо балансировать и соблюдать правило: одно событие – одна подписка.
- Источником у этой подписки мы устанавливаем только необходимые документы. На это есть две причины:
- Если мы установим ДокументОбъект, как ветка дерева, то эта подписка будет отрабатывать для каждого документа, который есть в конфигурации. Зачем нам терять лишние миллисекунды в наших системах?
- И вторая причина – если мы будем искать те объекты, в которых фигурирует наш документ с помощью «Поиска ссылок на объект», то эта подписка, к сожалению, найдена не будет.
- И реализацию одной подписки мы выносим в один модуль. Мы не объединяем все наши подписки в одном модуле. Это удобнее с точки зрения сопровождения.
Вот как это выглядит:
- добавляем общий модуль;
- добавляем подписку;
- указываем в качестве источника только необходимые документы;
- устанавливаем событие;
- обработчиком мы выбираем наш добавленный модуль.
Получается вот такая структура.
У нас есть наша общая процедура «ОбработкаПроведения» – здесь мы как раз выбираем тип источника, причем обработка каждого объекта идет в каждой своей отдельной процедуре.
Обращаю ваше внимание на эту секцию «Иначе»– рекомендую, чтобы она у вас все-таки была.
- Во-первых, это соблюдение стандартов разработки. Стандарт говорит о том, что если у вас есть секция «Если…ИначеЕсли», то секция «Иначе» обязательно должна быть.
- И здесь мы подстраховываемся. Мы, допустим, можем здесь вывести в журнал регистрации сообщение о том, что у нас сработала подписка на тех объектах, которые мы не предполагали. Допустим, разработчик случайно поставил, что эта подписка фигурирует еще для какого-то документа, или забыл написать обработку этого события. В общем, мы подстраховываемся.
Минусы – напоминаю, что при подписках отработка типового кода все равно будет. Соответственно, если подписка тяжелая, то эти секунды могут быть все-таки существенными – придется решать вопрос, нужна эта подписка или не нужна.
Плюсы – аналогичный подход позволяет нам реализовать специфичную бизнес-логику для любых менеджеров и объектов. Очень удобно.
Запросы
Дальше я хочу обсудить с вами то, как мы исправляем запросы. У нас есть два пути – мы либо комментариями выделяем то, что добавили в типовой запрос, либо типовой запрос полностью комментируем и создаем свой. И тот, и другой способ неудобны.
- Первый способ неудобен тем, что если вы случайно этот запрос в конструкторе откроете и нажмете кнопку «ОК», все комментарии затрутся.
- А во втором способе мы не можем быстро получить отличия – что было добавлено.
Но можно попробовать реализовать изменения через объект «Схема запроса». У нас есть объектная модель нашего запроса, и мы можем внести туда исправления и изменения приблизительно таким кодом, как показано на слайде.
В чем плюс?
Если нам просто нужно добавить в выборку еще одно поле, если вендор изменит исходный запрос и добавит в выборку других полей, наше поле все равно туда добавится, и этот запрос отработает.
Какие минусы:
- Работать со схемой запроса не так удобно, как просто вносить изменения в запрос.
- Кроме этого, при программной модификации запроса происходит увеличение времени исполнения. Все-таки объект «Схема запроса» медленный. Замеров я, к сожалению, не делал, но по ощущениям мне показалось, что время выполнения запроса увеличивалось раза в полтора.
Как уменьшить? У меня есть идея, что нужно разработать какой-то генератор описания отличий. Мы можем получить схему запроса в виде дерева объектов исходного запроса, сформировать также дерево объектов измененного запроса и сравнить, в чем там будут различия.
Да, алгоритм все-таки будет сложный – я начал его делать в свободное от работы время, но через неделю остановился – нужно было уже другими задачами заниматься. Поэтому это пока на уровне идеи.
Оформление условий
Перейдем к общим вопросам, которые не касаются только обновления типовых конфигураций, а относятся к тому, как в принципе разрабатывать.
Допустим, у нас есть блок условий. Это типовая ситуация – очень часто бывает. Причем, бывает так, что этот блок эволюционировал в течение 3-4 лет. Вначале было добавлено первое условие, потом второе, потом первое условие было изменено. Эта генерация была сделана не сразу.
И строк в этом блоке более 50-100. Анализировать этот блок, особенно, когда здесь возникла какая-то ошибка, чтобы разработчик, который сидит на поддержке, быстро понял, в чем там проблема, довольно сложно. Уходит очень много времени.
Что можно сделать, как его можно улучшить.
Если пойти по советам, как рекомендуют писать запросы – мы все знаем, что если у нас условие «ИЛИ» в секции «ГДЕ» или в секции «ПО», это приводит к тому, что запрос выполняется очень долго, и рекомендуют разделять на два запроса и делать через «ОБЪЕДИНИТЬ ВСЕ».
Здесь приблизительно та же самая схема. Предыдущий блок условий, которые разделены «ИЛИ», мы можем переписать следующим образом:
Если Условие1 И Условие2
ИначеЕсли Условие3 И Условие4
То есть сам блок кода, который есть между секциями, мы переносим в отдельную процедуру.
Конечно же, в этом случае она занимает на экране уже меньше места, и можно довольно быстро понять логику, что у нас происходит.
Можно пойти по другому принципу, когда мы наше условие переносим в отдельную функцию, которую так и называем «ВыполняютсяУсловияДляДействия1». Тогда тоже удобно смотреть – в нашем блоке условий еще меньше строчек.
Выбирайте, что вам удобнее. И то, и другое в сопровождении имеет место быть.
Комментирование кода
По комментариям хочу отдельно поговорить.
- Если код требует какого-то отдельного описания (что делает этот код), мы его переносим в отдельную процедуру, которую называем так, чтобы сразу было понятно, что она выполняет.
- Мы оставляем только концептуальные комментарии – описание бизнес-логики и т.д.
- Я очень часто видел, что в комментарии выносят какие-то блоки кода (комментируют старый неактуальный код). Я рекомендую этого все-таки не делать. Если код не нужен, его нужно удалить. Поэтому код мы не комментируем – с единственным исключением: мы комментируем типовой код. Допустим, если нам все-таки необходимо закомментировать типовой код – это можно сделать. Это даже нужно сделать, потому что тогда при сравнении/объединении мы поймем, что не какой-то свой код удалили, а типовой.
- Запросы все-таки комментируем, поскольку в запросах мы не можем, как в коде, выделить в отдельную процедуру, назвать ее соответствующим образом. Поэтому желательно как-то описать схему запроса и дать какие-то пояснения относительно того, каким образом идет сборка этого запроса.
У меня был случай из практики, когда лично я писал отчет для Бухгалтерии. И при реализации запроса для отчета я решил, что мне нужна виртуальная таблица ОборотыДтКт. Я создал эту версию отчета, ее приняли, она работает. Проходит два месяца – приходит задача на доработку этого отчета. Я открываю запрос и не помню, почему у меня используется именно таблица ОборотыДтКт, если проще использовать Обороты. Я переписал отчет на использование таблицы Обороты, но через два часа понял, почему я использовал ОборотыДтКт, и переделал обратно на ОборотыДтКт. Сдал работу, ее приняли, все хорошо. Прошло еще три месяца, и я опять, открыв отчет, не помню, для чего я использовал ОборотыДтКт. И я опять ее поменял на Обороты. И мне опять пришлось менять ее обратно. Только после этого я написал в запросе комментарий: «Есть определенный нюанс, из-за которого нужно использовать именно виртуальную таблицу ОборотыДтКт – не исправляйте». В четвертый раз я уже не стал ничего менять – я увидел свой комментарий, и мне это помогло.
Реализация отчетов
Я хочу еще отдельно с вами обсудить отчеты. На слайде показан отчет из реальной конфигурации. В этом запросе полторы тысячи строк. Это только запрос из отчета.Причем, это уже третья или четвертая реинкарнация этого отчета.
Проблема в том, что этот отчет каждый раз разрабатывал новый сотрудник. И хотя с точки зрения пользователя изменения, которые вносились в этот отчет, были не глобальные – одну колонку добавить, другую убрать, поменять их местами, немного изменить логику просчета группировок или еще что-нибудь. Но в итоге это приводило к тому, что если сравнить запросы этого отчета, то они приблизительно будут занимать столько же строк, но при этом будут отличаться друг от друга более чем на 60%.
То есть разработчик, получая задание на доработку отчета, фактически начинал делать его заново – он не мог разобраться в логике, не понимал, как отчет формировался раньше.
Видите, сколько в этом запросе пакетов и объединений.
Поэтому я все-таки рекомендую делать так, как сейчас делает 1С в типовых конфигурациях:
- Использовать в схеме компоновки фиктивный запрос, достаточный только для того, чтобы мы могли сделать группировки и вывести поля.
- Использовать программное формирование текста запроса.
- Именно при программном формировании мы можем добавить комментарии там, где они нужны: описать схему, указать, почему используется именно она, сделать пояснения по ходу добавления временных таблиц и т.д.
Это выглядит приблизительно так, как на слайде
У нас есть текст запроса. Причем, мы можем наш запрос разбить по отдельным функциям, которые он возвращает, а потом их собрать и запихнуть в компоновщик.
Почему так? Если мы все-таки оставим изначальный запрос в схеме компоновки, а потом будем его в тексте изменять, то у разработчика может уйти очень много времени, чтобы понять, что текст запроса все-таки меняется. А когда он увидит фиктивный запрос – тут будет сразу понятно, что нужно лезть в код отчета и смотреть, что там.
Тернарный оператор
Еще я хочу рассказать про тернарный оператор. Я все-таки рекомендую его не использовать. Объясню, почему.
Обратите внимание, первой строчкой идет наш тернарный оператор, он тут простой.
Но при эволюции нашей системы он может вырасти до такого вида, как на второй строчке. Слету довольно сложно понять, что здесь происходит. Особенно, когда тут может быть значение перечисления или ссылка на предопределенный элемент справочника. Разобрать этот кусок и быстро понять, что в нем, довольно сложно.
А еще бывает ситуация, как в третьем примере – через какое-то время мы понимаем, что ее нужно отрефакторить, и мы делаем так, как показано ниже. Но обратите внимание, здесь закралась ошибка. Видите, в условии используется другая переменная, а мы эту строку тоже добавили сюда.
И найти, что у вас произошла эта ошибка, довольно сложно. И последствия могут быть плачевными.
Здесь, конечно, довольно упрощенная схема, но смысл ее такой.
Периодически в сообществе всплывает такой вопрос – как правильно писать условия на неравенства. Либо использовать <>, либо писать НЕ. Мое мнение – выберите, как вам удобно в вашей команде. Самое главное – не путайте это правило. Потому что это в поддержке тоже вызывает боль.
Источники информации
Что можно почитать на тему приемов доработки конфигураций:
- Естественно, я рекомендую изучить систему стандартов 1С – https://its.1c.ru/db/v8std. Это нужно завести за правило. Там очень много полезной информации.
- Телеграм-канал по системе стандартов 1С v8std давно не обновлялся и, наверное, уже умер. Поэтому я еще рекомендую канал CodeClean (чистый код). Те советы, которые там есть, не привязаны к языкам программирования. Они привязаны к каким-то общим концепциям, их также можно использовать в 1С.
- Естественно, «Правила и приемы доработки типовых конфигураций 1С» от Виталия Онянова.
- Классная статья «Ректальное программирование: основы для практикующих 1С-программистов». Получите массу удовольствия.
- Две статьи по программной доработке форм – //infostart.ru/public/560508/ и //infostart.ru/public/1116920/.
- Статья про объектную модель «Схема запроса» – //infostart.ru/public/307045/.
- Отдельно я об этом не говорил, но есть статья «Вы не умеете работать с транзакциями». Рекомендую прочитать. Действительно, открываются глаза.
- И «Оформление и рефакторинг сложных логических выражений». Тоже очень интересно. Почитайте хотя бы для общего развития.
Вопросы
- Как ты относишься к применению расширений именно в сопровождении?
- Расширения я рекомендую использовать именно для хотфиксов. Я только для этого их использую. Когда их становится много, когда мы доработки делаем через расширения, их потом очень сложно модерировать. И в сопровождении лучше их использовать для исправления каких-то проблем на лету. Потом все, что мы поместили в расширение, мы переносим в основную базу. Пока только для этого.
****************
Данная статья написана по итогам доклада (видео), прочитанного на конференции INFOSTART EVENT 2019.