Картинка для улыбки :)
Картинка взята с телеграмм-канала Мемы 1С.
Краткое оглавление
- Введение
- Ссылки
- Источники
- Глава 2
- Содержательные имена
- Функции
- Комментарии
- Форматирование
- Многопоточность и длительные операции
- Практический результат
- Заключение
Введение
Доброго времени суток, уважаемый читатель. Если ты зашел в эту статью, значит ты хочешь стать чуточку лучше и ищешь пути своего развития. В этой статье я познакомлю тебя с моим личным путем развития в направлении качества кода. Данная статья не призывает строго соблюдать правила, написанные в ней, и даже не является рекомендацией. Моя цель - познакомить тебя со своим видением этого мира. Если тебе есть что сказать или обсудить - добро пожаловать в комментарии.
Эта публикация является второй частью. Рекомендую ознакомиться так же с первой частью (хотя они не являются взаимосвязанными).
Ссылки
Источники
Продублирую источники из первой статьи, чтобы интернет знал своих героев.
- За основу взята книга Роберта Мартина "Чистый код". Сразу скажу свое видение по поводу этой книги в сфере 1С. Это лучше, чем разбираться в стандартах 1С на ИТС. Почему? Лично мне неудобно ориентироваться в стандартах, непонятная и неочевидная структура. Но добрая половина книги бесполезна для 1С.
- К книге добавлены стандарты 1С. Я потратил много времени чтобы дополнить материал из книги материалами из стандартов. С того времени я не заходил больше в стандарты.
- Пройдена обкатка на практике, после чего изменены и добавлены некоторые пункты из личного опыта (моего и моей команды).
- Так же есть множество статей на Инфостарте, в них можно изучить разные взгляды и подходы. Алексей Беспалов, Артано Майаров, Тимофей Синичкин, Юрий Былинкин, Николай Васильев, Николай Васильев, Владимир Крючков, Владимир Харин, q_i
Глава 2
В этом разделе я приведу те правила из книги, которые мы используем в своей команде. В предыдущей части я рассматривал больше общие правила. Здесь же будет подробно про переменные и методы, модули и т.д.
Далее по статье я буду использовать разные термины: метод, процедура и функция. Примем, что любое из этих слов подразумевает именно метод.
Содержательные имена
Имена должны передавать намерения программиста (осмысленные наименования).
Сравните вызов двух функций. В первом случае совершенно очевидно, что функция вернет таблицу значений, содержащую документы «Заказ клиента» и даты их проведения. А во втором случае совсем не очевидно, что делает функция, и что хранится в переменной «ТЗДоки», можно только сказать, что там некая таблица значений.
ТЗЗаказыКлиентов = ПолучитьТЗЗаказыКлиентовСДатамиПроведения();
ТЗДоки = ТЗДоки();
Избегайте дезинформации (наименование не отображает реальное предназначение).
Вызываем функцию «ПолучитьТЗЗаказыКлиентов» и ожидаем, что она вернет нам таблицу значений с документами «Заказ клиента».
ТЗЗаказыКлиентов = ПолучитьТЗЗаказыКлиентов();
Однако если мы посмотрим на определение функции, но увидим, что название не соответствует выполняемому коду.
Функция ПолучитьТЗЗаказыКлиентов()
Массив = Новый Массив();
Массив.Добавить(Объект.Ссылка);
Возврат Массив;
КонецФункции
Функция возвращает массив, однако из названия следует, что она возвращает таблицу значений. Это и есть дезинформация. Если бы мы в последующем стали использовать методы, относящиеся к таблице значений, например ТЗЗаказыКлиентов.Колонки.Добавить(“СвязанныйКлиент”), то получили бы ошибку только в ходе выполнения программы.
Так же можно привести пример с переменными. Когда в переменной ФИОКлиента оказывается наименование юридического лица. Или в переменной ЦенаТовара оказывается сумма (цена*количество).
Осмысленные различия
Рассмотрим два метода: ПолучитьДанные() и ПолучитьТаблицу(). Разве из наименований этих методов понятно, что они делают? Разве метод ПолучитьДанные не может вернуть таблицу? А таблица не содержит данные? Кажется, что эти методы делают одно и то же. Важно обращать внимание не только на наименование одного метода, но и остальных. Будет ли уместно задавать то или иное наименование метода, когда через 2 метода вниз существует метод с похожим наименованием? В наименованиях методов должны быть четкие различия. Например ПолучитьДанныеВМассив() и ПолучитьДанныеВТабличныйДокумент(). В таком случае различия в методах очевидны: методы возвращают разные типы данных.
Удобопроизносимые имена.
Что скажите о переменной стрВРегДокЗаказКлиент? Или ЭлТЧФорма? Мало того что их произносить неудобно, так ещё и перечитываешь по несколько раз, чтобы понять суть. Совсем другое дело ПредставлениеЗаказаКлиентаВерхнийРегистр (или ПредставлениеЗаказаКлиентаВРег, поскольку ВРег является типовым методом). Не стоит напихивать в наименования сокращения, несвязанные слова и прочее.
Не кодировать имена.
Не стоит кодировать наименования и выдумывать свой собственный язык и правила именования. Плохой пример: кодировать «Число» в «Ч», «Заказ клиента» в «ЗК» и прочее. Используйте только те правила кодирования, которые приняты в вашем направлении, например: «ТаблицаЗначеий» в «ТЗ», «ТабличнаяЧасть» в «ТЧ» и т.д.
Венгерская запись
Типичный пример венгерской записи: «СтрокаНаименование». Венгерская запись – включать в наименования типы данных там, где в этом нет никакого смысла. Всем и так понятно, что наименование – строковый тип данных.
Не использовать префиксы и суффиксы без необходимости.
Иногда встречается стиль написания кода, когда ко всему, что написано в расширениях, добавляются префиксы расширения. И это применяются так же к переменным, что является абсолютно бессмысленным. Суффиксы и префиксы стоит использовать только тогда, когда это необходимо. Плохой пример: «Расш1ТЗЗаказыКлиентов». Изменив наименование на «ТЗЗаказыКлиентов» наименование станет только более понятней.
Конечно, данный пункт не относится к тем ситуациям, когда платформа автоматически подставляет префиксы (например, при расширении методов).
Пример необходимости применения префикса. В расширении расширяется типовой объект. Добавляется реквизит. Наименование реквизита выбрано такое, что, возможно, в будущих обновлениях типовой конфигурации поставщик может применять его для своих реквизитов. Тогда может случится конфликт при обновлении. В этом случае конечно стоит использовать префикс.
Если наименование присваивается переменной, методу или реквизиту самописного объекта метаданных, скорее всего префикс не будет нужен.
Мысленные преобразования
Читая код, имена мысленно преобразуются в другие, подразумевается что-то другое. Такие наименования лучше избегать. В этом пункте я не смог придумать пример и пока что не встречал его применения на практике в нашей сфере. Если у читателя есть свои предложения – добро пожаловать в комментарии.
Имена методов – глаголы.
Метод с наименованием СуммаТоваров – плохой метод. Непонятно, что происходит с суммой в ходе выполнения метода. Такое наименование больше подходит для переменной. Наименования метода следует начинать с глагола, например РассчитатьСуммуТоваров.
Имена из пространства задачи.
Для наименований следует выбирать имена из предметной области или пространства задачи. Если вы работаете с задачей по изменению заказа клиента, то будет странно, если в вашем коде встретится переменная с именем «СуммаПоБухУчету» или «КоличествоВАптеке» и прочее. Тому, кто читает ваш код, будет непонятно назначение этих переменных и содержащиеся в них данные.
Избыточный контекст
Не нужно для каждого служебного метода добавлять префикс приложения и прочие очевидные вещи, например «УТРассчитатьСуммуТоваров». «УТ» - избыточный контекст, который очевиден и без него.
Функции
Компактность.
20 строк. Именно столько нужно для выполнения любой хорошо выделенной части кода на одном уровне абстракции. Если в вашем методе более 20 строк, вероятно стоит задуматься над тем, чтобы разбить метод на несколько методов.
Мне очень сложно соблюдать данный пункт. Я сделал для себя послабление – от 1 до 1,5 экрана. Такой объем гораздо легче соблюдать. Если вы соблюдаете 20 строк – вы большой молодец!
Блоки и отступы.
Не более 4 уровней отступов, не содержат вложенных структур. В идеале внутри 1 строка – вызов функции (в новом отступе). Посмотрите на представленный блок кода. С первого раза невозможно разобраться, что здесь происходит. Необходимо перечитать блок 2 и более раз. А после вставки изменений необходимо убедиться, что изменения были вставлены в нужный уровень вложенности, прокрутив весь метод. И это только самые простые условия.
Процедура ОчиститьПоставщиковНоменклатуры()
Для каждого стрТЗЗаказов из ТЗЗаказыКлиентов Цикл
Если стрТЗЗаказов.ВидЗаказа = Перечисления.ВидыЗаказов.Стандартный Тогда
Для каждого стрТовары из стр.ТЗЗаказов.Товары Цикл
Если стр.Товары.ЕдиницаИзмерения = Перечисления.ЕдиницыИзмерения.Штуки Тогда
ТоварОбъект = стр.Товары.ПолучитьОбъект();
Для каждого стрПоставщики из ТоварОбъект.Поставщики Цикл
стрПоставщики.Поставщик = Неопределено;
КонецЦикла;
ТоварОбъект.Записать();
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Или его же версия.
Процедура ОчиститьПоставщиковНоменклатуры()
Для каждого стрТЗЗаказов из ТЗЗаказыКлиентов Цикл
Если стрТЗЗаказов.ВидЗаказа = Перечисления.ВидыЗаказов.Стандартный Тогда
Для каждого стрТовары из стр.ТЗЗаказов.Товары Цикл
Если стр.Товары.ЕдиницаИзмерения = Перечисления.ЕдиницыИзмерения.Штуки Тогда
ТоварОбъект = стр.Товары.ПолучитьОбъект();
Для каждого стрПоставщики из ТоварОбъект.Поставщики Цикл
стрПоставщики.Поставщик = Неопределено;
КонецЦикла;
ТоварОбъект.Записать();
КонецЕсли;
КонецЦикла;
КонецЕсли;
КонецЦикла;
КонецПроцедуры
А теперь взгляните на новый блок. Функционал двух блоков абсолютно одинаковый. Однако после переработки блок кажется проще и понятнее, читается сверху вниз без прокручивания модуля. Изменения вносятся так же легко.
Процедура ОчиститьПоставщиковНоменклатуры()
Для каждого стрТЗЗаказов из ТЗЗаказыКлиентов Цикл
Если стрТЗЗаказов.ВидЗаказа = Перечисления.ВидыЗаказов.Стандартный Тогда
ИзменитьШтучныеТовары(стрТЗЗаказов );
КонецЕсли;
КонецЦикла;
КонецФПроцедуры
Процедура ИзменитьШтучныеТовары(ЗаказКлиента)
Для каждого стрТовары из стр.ТЗЗаказов.Товары Цикл
Если стр.Товары.ЕдиницаИзмерения = Перечисления.ЕдиницыИзмерения.Штуки Тогда
ОчиститьТЧПоставщикиТовара(стрТовары );
КонецЕсли;
КонецЦикла;
КонецПроцедуры
Процедура ОчиститьТЧПоставщикиТовара(Товар)
ТоварОбъект = Товар.ПолучитьОбъект();
Для каждого стрПоставщики из ТоварОбъект.Поставщики Цикл
стрПоставщики.Поставщик = Неопределено;
КонецЦикла;
ТоварОбъект.Записать();
КонецПроцедуры
Так же обратите внимание на отступы. Отступы разделяют разные уровни вложенности и позволяют наглядно видеть, на каком уровне находится выполняемый код.
Так же отступы между строками. Они должны отделять логические блоки друг от друга. В первом случае метод выглядит скомканным. Во втором все наглядно. Посмотрите на ещё два блока кода, написанных в одном методе.
Процедура РассчитатьСуммуТоваров()
СуммаТоваров = 0;
Для каждого стр из ЗаказКлиента.Товары Цикл
стр.Сумма = стр.сумма * 1.2;
СуммаТоваров = СуммаТоваров + стр.Сумма;
КонецЦикла;
ЗаказКлиентаОбъект = ЗаказКлиента.ПолучитьОбъект();
ЗаказКлиентаОбъект.СуммаДокумента = СуммаТоваров;
ЗаказКлиентаОбъект.Провести(РежимЗаписиДокумента.Проведение);
КонецПроцедуры
Процедура РассчитатьСуммуТоваров()
СуммаТоваров = 0;
Для каждого стр из ЗаказКлиента.Товары Цикл
стр.Сумма = стр.сумма * 1.2;
СуммаТоваров = СуммаТоваров + стр.Сумма;
КонецЦикла;
ЗаказКлиентаОбъект = ЗаказКлиента.ПолучитьОбъект();
ЗаказКлиентаОбъект.СуммаДокумента = СуммаТоваров;
ЗаказКлиентаОбъект.Провести(РежимЗаписиДокумента.Проведение);
КонецПроцедуры
Во втором блоке выделены логические области: переменные, изменение строки заказа, заполнение переменных, изменение заказа. Эти области обрамлены отступами, что отделяет их от основного кода.
Функция выполняет только одну операцию на одном уровне.
Если функцию можно разделить на секции (области, блоки), то функция выполняет более одной операции. Если взять блок кода из предыдущего примера, то очевидно, что эта функция выполняет более одной операции.
Процедура РассчитатьСуммуТоваров()
СуммаТоваров = 0;
Для каждого стр из ЗаказКлиента.Товары Цикл
стр.Сумма = стр.сумма * 1.2;
СуммаТоваров = СуммаТоваров + стр.Сумма;
КонецЦикла;
ЗаказКлиентаОбъект = ЗаказКлиента.ПолучитьОбъект();
ЗаказКлиентаОбъект.СуммаДокумента = СуммаТоваров;
ЗаказКлиентаОбъект.Провести(РежимЗаписиДокумента.Проведение);
КонецПроцедуры
Из операций можно выделить следующие: изменение строки заказа, добавление данных в массив, изменение переменной, изменение документа. Так же здесь 3 уровня: переменные, строки заказа, документ заказа. Таким образом нужно выделить 2 метода.
Процедура РассчитатьСуммуТоваров()
СуммаТоваров = 0;
Для каждого стр из ЗаказКлиента.Товары Цикл
ИзменитьСтрокуЗаказа(стр);
СуммаТоваров = СуммаТоваров + стр.Сумма;
КонецЦикла;
ИзменитьСуммуЗаказа(ЗаказКлиента, СуммаТоваров );
КонецПроцедуры
Процедура ИзменитьСтрокуЗаказа(стрЗаказа)
стрЗаказа.Сумма = стрЗаказа.сумма * 1.2;
КонецПроцедуры
Процедура ИзменитьСуммуЗаказа(ЗаказКлиента, СуммаТоваров)
ЗаказКлиентаОбъект = ЗаказКлиента.ПолучитьОбъект();
ЗаказКлиентаОбъект.СуммаДокумента = СуммаТоваров;
ЗаказКлиентаОбъект.Провести(РежимЗаписиДокумента.Проведение);
КонецПроцедуры
Это наиболее простой пример, встречаются примеры, когда выделяются целые циклы или несколько циклов.
Чтение кода сверху вниз: правило понижения.
В одной функции вызываются функции одного уровня абстракции. В предыдущем примере было четко видно 3 уровня абстракции: переменные, строки заказа, документ заказа. После выделения в отдельные методы, в одной функции остался только один уровень абстракции. Таким образом при чтении метода если мы будет проваливаться в один из методов мы будем углубляться в подробности, переходить на более низкий уровень абстракции, но в пределах одного метода мы будет на одном уровне абстракции.
Содержательные имена.
Требования к именованию методов такие же, как и к переменным: имена должны отражать то, что делает метод, нести логический смысл, не быть перегруженными и т.д.
Аргументы функций.
Максимум 2-4 аргумента. Однако есть хитрость: если у функции несколько аргументов их можно объединить в структуру, и передавать структуру как один аргумент.
Возврат должен явно отображаться. Помните, что все ветви выполнения кода в функции должны содержать возврат. Так же функция должна всегда возвращать значение. Не следует использовать функции без возврата.
Возврат через аргументы не следует использовать. Для возврата значений используйте функции и возвращаемые параметры (ключевое слово Возврат). Возврат через аргументы делает код запутанным.
Передача флага в аргумент.
Зачастую, если в функцию требуется передать флаг – это значит, что функция выполняет более одной операции и её требуется разбить на несколько методов.
Передача множества аргументов
Если в аргументах передается много параметров одного объекта (или структура, содержащая много параметров одного объекта), проще передать весь объект.
Много аргументов можно обернуть в массив или структуру и передавать как один аргумент. В этом случае нужно создать описание метода и описать структуру параметра, какие поля он содержит.
Побочные эффекты.
Метод делает дополнительные операции, не заявленные в имени. Для примера возьмем безвредную на первый взгляд функцию. Функция использует стандартный алгоритм для проверки пары «имя пользователя/пароль». Она возвращает Истина в случае совпадения или Ложь при возникновении проблем. Но у функции также имеется побочный эффект. Имя ПроверитьПароль сообщает, что функция проверяет пароль. Оно ничего не говорит о том, что функция инициализирует сеанс. Таким образом, тот, кто поверит имени функции, рискует потерять текущие сеансовые данные, когда он решит проверить данные пользователя.
Проверка и действие – разные методы. Пример:
Если ИзменитьРеквизит("Имя", "Иван") Тогда
В данном случае непонятно, что делает функция ИзменитьРеквизит и почему она используется в условии. Для данного случая следует создать 2 функции: первая, которая изменяет реквизит, и вторая, которая проверяет, что реквизиту задано соответствующее значение.
Если Не ПроверитьРеквизит("Имя", "Иван") Тогда
ИзменитьРеквизит("Имя", "Иван");
КонецЕсли;
Попытка и исключение
Код внутри попытки и внутри исключения лучше выносить в отдельные методы.
Комментарии
О комментариях
Ничто не помогает так, как уместный комментарий. Ничто не загромождает модуль так, как бессодержательные и безапелляционные комментарии. Ничто не приносит столько вреда, как старый, утративший актуальность комментарий, распространяющий ложь и дезинформацию.
Грамотное применение комментариев должно компенсировать нашу неудачу в выражении своих мыслей в коде. Обратите внимание на слово «неудачу». Комментарий — всегда признак неудачи. Мы вынуждены использовать комментарии, потому что нам не всегда удается выразить свои мысли без них.
Комментарии лгут. Не всегда и не преднамеренно, но это происходит слишком часто. Чем древнее комментарий, чем дальше он расположен от описываемого им кода, тем больше вероятность того, что он просто неверен. Причина проста: программисты не могут нормально сопровождать комментарии.
Неточные комментарии гораздо вреднее, чем полное отсутствие комментариев. Они обманывают и сбивают с толку. Они создают у программиста невыполнимые ожидания. Они устанавливают устаревшие правила, которые не могут (или не должны) соблюдаться в будущем. Истину можно найти только в одном месте: в коде. Только код может правдиво сообщить, что он делает. Это единственный источник действительно достоверной информации.
Одной из распространенных причин для написания комментариев является низкое качество кода. Вы пишете модуль и видите, что код получился запутанным и беспорядочным. Вы знаете, что разобраться в нем невозможно. Поэтому вы говорите себе: «О, да это стоит прокомментировать!» Нет! Лучше исправьте свой код! Ясный и выразительный код с минимумом комментариев гораздо лучше громоздкого, сложного кода с большим количеством комментариев. Не тратьте время на написание комментариев, объясняющих созданную вами путаницу, — лучше потратьте его на исправление.
Сейчас в своей практике я редко использую комментарии. Когда я только начинал применять правила чистого кода, я часто писал комментарии. Применяя все больше и больше правил я практически полностью избавился от комментариев.
TODO комментарий
Можно оставлять TODO комментарии. Они обозначают намерения реализовать данный метод позже. Возможно сейчас его нельзя реализовать в полной мере.
Процедура ИзменитьСуммуТовара(стрЗаказа)
//TODO: сделать изменение суммы товара
//Сейчас модуль расчета суммы не реализован
КонецПроцедуры
По «TODO» очень легко искать места, которые ещё не реализованы. Достаточно воспользоваться поиском.
Непонятные комментарии
Если при прочтении комментария стороннему человеку не понятно, что он значит – это плохой комментарий
Процедура ИзменитьСуммуЗаказа(ЗаказКлиента, СуммаТоваров)
// заказ
ЗаказКлиентаОбъект = ЗаказКлиента.ПолучитьОбъект();
ЗаказКлиентаОбъект.СуммаДокумента = СуммаТоваров;
ЗаказКлиентаОбъект.Провести(РежимЗаписиДокумента.Проведение);
КонецПроцедуры
Избыточные комментарии
Если код понятен без комментария, комментарий не поясняет и не дополняет код – это плохой комментарий.
Процедура ИзменитьСуммуЗаказа(ЗаказКлиента, СуммаТоваров)
// измененный заказ проводится
ЗаказКлиентаОбъект = ЗаказКлиента.ПолучитьОбъект();
ЗаказКлиентаОбъект.СуммаДокумента = СуммаТоваров;
ЗаказКлиентаОбъект.Провести(РежимЗаписиДокумента.Проведение);
КонецПроцедуры
Противоречащие комментарии
Если комментарий противоречит коду – это плохой комментарий.
Процедура ИзменитьСуммуЗаказа(ЗаказКлиента, СуммаТоваров)
// заказ записывается без проведения
ЗаказКлиентаОбъект = ЗаказКлиента.ПолучитьОбъект();
ЗаказКлиентаОбъект.СуммаДокумента = СуммаТоваров;
ЗаказКлиентаОбъект.Провести(РежимЗаписиДокумента.Проведение);
КонецПроцедуры
Чаще всего причина таких комментариев – изменение кода без изменения комментариев.
Обязательные комментарии
Обязательные комментарии – зло (описание методов и аргументов – обязательные комментарии по стандартам). Их стоит использовать только тогда, когда это действительно необходимо. Не стоит к каждому методу приписывать описание.
Комментарии для сложных блоков кода
Использовать комментарий для пояснения сложного блока кода – плохо. Лучше сложный блок обернуть в метод и дать методу понятное наименование.
Закомментированный код
Закомментированный код – плохой комментарий. Другие разработчики не решатся его удалить, а о его важности будет знать только создатель. Такой код может жить в модуле несколько лет.
Связь комментария с кодом
Связь между комментарием и кодом должна быть очевидной. Прочитав код и комментарий, комментарий должен пояснить код, а не запутать.
Комментарии для методов
Хорошо выбранное имя метода лучше, чем имя метода и комментарий.
Форматирование
Детализация
Степень детализации увеличивается сверху вниз. Сверху – высокоуровневые концепции и алгоритмы, снизу – все методы и подробности низшего уровня.
Сверху метод с алгоритмом (высокий уровень), снизу методы изменения строки и проведения документа (низкий уровень).
Процедура РассчитатьСуммуТоваров()
СуммаТоваров = 0;
Для каждого стр из ЗаказКлиента.Товары Цикл
ИзменитьСтрокуЗаказа(стр);
СуммаТоваров = СуммаТоваров + стр.Сумма;
КонецЦикла;
ИзменитьСуммуЗаказа(ЗаказКлиента, СуммаТоваров );
КонецПроцедуры
Процедура ИзменитьСтрокуЗаказа(стрЗаказа)
стрЗаказа.Сумма = стрЗаказа.сумма * 1.2;
КонецПроцедуры
Процедура ИзменитьСуммуЗаказа(ЗаказКлиента, СуммаТоваров)
ЗаказКлиентаОбъект = ЗаказКлиента.ПолучитьОбъект();
ЗаказКлиентаОбъект.СуммаДокумента = СуммаТоваров;
ЗаказКлиентаОбъект.Провести(РежимЗаписиДокумента.Проведение);
КонецПроцедуры
Отступы между строками
Каждая группа строк представляет законченную мысль. Такие группы следует разделять пустыми строками.
Процедура РассчитатьСуммуТоваров()
СуммаТоваров = 0;
Для каждого стр из ЗаказКлиента.Товары Цикл
стр.Сумма = стр.сумма * 1.2;
СуммаТоваров = СуммаТоваров + стр.Сумма;
КонецЦикла;
ЗаказКлиентаОбъект = ЗаказКлиента.ПолучитьОбъект();
ЗаказКлиентаОбъект.СуммаДокумента = СуммаТоваров;
ЗаказКлиентаОбъект.Провести(РежимЗаписиДокумента.Проведение);
КонецПроцедуры
Выделены логические области: переменные, изменение строки заказа, заполнение переменных, изменение заказа. Эти области обрамлены отступами, что отделяет их от основного кода.
Строки кода, между которыми существует тесная связь, должны быть вертикально сжаты, между ними нет пустых строк, комментариев и других разрывов.
Объявление переменных
Переменные объявляются непосредственно перед их использованием. Не следует объявлять переменные в начале модуля. Дойдя до места их использования, читатель забудет об их существовании.
Расположение вызываемых методов между собой
Вызываемый метод должен находится радом с вызывающим, а вызывающий метод должна быть сверху (если это возможно).
Длинна строк
Строки должны быть шириной не более 120 символов.
Разделение и сжатие
Следует использовать горизонтальное сжатие и разделение. Операторы присваивания выделять с двух сторон пробелами, разделять пробелами аргументы методов, после запятой ставить пробел. А вот метод и скобку наоборот следует сжимать, не разделяя.
Отступы
Исходный файл имеет иерархическую структуру. В нем присутствует информация, относящаяся к методам внутри модулей; к блокам внутри методов и рекурсивно – к блокам внутри блоков. Каждый уровень этой иерархии образует область видимости, в которой могут объявляться имена и в которой интерпретируются исполняемые команды. Чтобы создать наглядное представление этой иерархии, мы снабжаем строки исходного кода отступами, размер которых соответствует их позиции в иерархии. Команды уровня методов отступов не имеют. Реализации этих методов сдвигаются на один уровень вправо от объявления метода. Реализации блоков сдвигаются на один уровень вправо от своих внешних блоков и т. д. Программисты широко используют эту схему расстановки отступов в своей работе. Чтобы определить, к какой области видимости принадлежат строки кода, они визуально группируют строки по левому краю. Это позволяет им быстро пропускать области видимости, не относящиеся к текущей ситуации (например, реализации команд Если и Цикл). У левого края ищутся объявления новых методов, новые переменные. Без отступов программа становится практически нечитаемой для людей.
Не стоит удалять отступы и нарушать их иерархию, даже если условие состоит из одного оператора.
Многопоточность и длительные операции
Многопоточность и длительные операции полезны только при большом объеме данных. На их реализацию необходимо дополнительное время. Не следует реализовывать многопоточность и длительные операции без должного изучения.
Практический результат
- Разработка конфигурации для внутренних нужд нашей компании. На разработку был выделен молодой и перспективный разработчик без опыта работы. В то время ещё не было в нашей команде понятия о стандартах и чистом коде. Разработка планировалась на 2 месяца с учетом неопытного сотрудника. Проект был начат в далеком январе 2022 года… И продолжается до сих пор. Код этого проекта – «легаси» (кому интересно поищите в интернете определение). Сам же разработчик закончил работу только через 6 месяцев. Никто, даже он, уже не могли разобраться в написанном. Этот тот проект, о котором можно сказать: «проще все с нуля сделать, чем переделывать». Кто-то может сказать: причина в неопытности. Я же скажу следующее: если бы разработчик руководствовался не своими фантазиями, а конкретными правилами, то вышел бы качественный продукт. В данном случае на этапе разработки сроки превышены в 3 раза, а сопровождаемость снижена до нуля.
- Разработка модуля интеграции с облачной кассой для торговых конфигураций. В этом проекте было 2 модуля: интеграция с УТ (первый этап) и интеграция с ещё двумя конфигурациями (УНФ, Розница 2.3) (второй этап). Первый этап был оценен в 2 месяца с учетом тестов клиента. При разработке старались придерживаться стандартов и правил чистого кода. Разработкой занимался я лично. Проект был закончен в срок. Когда начали выполнять второй этап, за основу взяли код из первого этапа. На проект было заложено 2 месяца (при объеме работ на 20% больше, чем в первом этапе). Разработкой занимались два других менее опытных разработчика по отдельности (каждый на своей конфигурации). На мое удивление, вопросов было очень мало, а разработчики самостоятельно во всем разобрались. Второй этап был закончен за 1 месяц. В данном случае мы получили опережение по срокам в 2 раза.
- Расширение стороннего разработчика для УТ. После обновления конфигурации мы уже 3 месяца получаем ошибки в совершенно разных местах (при том, что большинство ошибок были исправлены сразу после обновления). Часто ошибки одни и те же, но возникают в разных местах (дублирование кода). На такие ошибки, по первоначальной оценке, мы должны были тратить не более 1 часа. Однако после некоторого количества таких ошибок стало понятно, что время исправления занимает около 3-4 часов. Проблема кроется в запутанности кода.
Заключение
В этой главе я показал правила из главы 2 книги. Они представляют собой конкретные действия, которые я и моя команда соблюдаем. Так же я привел практические примеры, которые для меня показывают эффективность описанных правил. Сейчас мы в команде стараемся руководствоваться этими (и не только) правилами при работе.
Приглашаю обсудить все в комментариях, много спорных вопросов. Надеюсь, вы выскажете свое драгоценное мнение, чтобы улучшить этот мир!