Как сделать ваш код чище

Опубликовано: 2020-06-24

Разработчики тратят большую часть времени на чтение и поддержку существующего кода. Мы пытаемся понять, что он делает и как его изменить или расширить. Так зачем фокусироваться только на производительности?

Удобочитаемость должна быть столь же важна при создании кода на века.

При разработке программного обеспечения с длительным сроком службы очень важно иметь отличную читаемость кода. Будь то поддержка старого кода, добавление новых функций или изменение старого, читабельный код значительно упрощает работу .

Еще одним преимуществом является возможность адаптации новых разработчиков . Успешная адаптация зависит от читабельности существующего кода.

Реальность такова, что когда вы начинаете работать над продуктом, который разрабатывался годами, в какой-то момент вам придется иметь дело с устаревшим кодом . Много разочарований накапливается, когда вы завалены тысячами строк нечитаемого кода, который вы не писали. Поддерживать его и строить на нем только усугубляет ситуацию.

В Mediatoolkit мы разрабатываем наш продукт в долгосрочной перспективе . С одной стороны, это означает, что мы избегаем краткосрочных исправлений и рискованных бизнес-решений. С другой стороны, это требует от инженерной группы дополнительных усилий по обеспечению читабельности кода.

Правда в том, что всего пару лет назад мы не уделяли такого внимания читабельности кода. Как вы уже догадались, именно тогда возникли серьезные проблемы с привлечением новых разработчиков и обновлением функций.

Будем надеяться, что другие не повторят наших ошибок и начнут вовремя убираться . Вот некоторые рекомендации по удобочитаемости кода, которых мы придерживаемся в Mediatoolkit.

Читабельность важна

При работе над существующим продуктом разработчики тратят большую часть времени на чтение и поддержку существующего кода. Если на разработку чего-то ушли годы, само собой разумеется, что потребуется больше минуты, чтобы понять, что оно делает и как это изменить.

Несмотря на количество времени, которое разработчики тратят на чтение существующего кода, больше внимания уделяется написанию нового, более эффективного кода. Время, необходимое для его поддержания, редко принимается во внимание. Итак, на чем вместо этого сосредоточиться?

Начните с основ . Это первые шаги, которые вы можете предпринять, чтобы улучшить свой код и сделать его чище:

  • Используйте осмысленные имена
  • Улучшить функции
  • Предпочитать неизменность
  • Предпочитайте декларативное программирование императивному
  • Будь проще, глупый - ПОЦЕЛУЙ
  • Не повторяйтесь – СУХОЙ
  • Тебе это не понадобится — YAGNI

Используйте осмысленные имена

Вы называете все в коде, поэтому убедитесь, что делаете это хорошо. Значимые имена обеспечивают контекст того, что происходит. Имена должны сообщать о намерениях кода — причина, по которой что-то существует, что оно делает и как оно используется.

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

Код без осмысленных имен

Код со значимыми именами

По возможности имена должны исходить из проблемного домена . Не используйте технические имена, если в домене есть лучшее имя.

Классы и объекты должны иметь существительные или именные словосочетания в качестве имен, например, customer , emailSender или htmlParser .

Но, следуя этому правилу, старайтесь избегать имен, содержащих общие существительные, такие как data , info , manager , processor , controller …, так как они чаще всего не имеют большого значения.

Общий термин не говорит вам, какова его область. Он только говорит вам, что он содержит в самом широком смысле.

Например, объект с именем info гораздо труднее читать, чем userInfo . Кроме того, в коде может быть несколько infos , controllers или managers , поэтому легче следовать логике кода, когда эти имена различаются доменом.

Методы должны называться с использованием глаголов или глагольных фраз, таких как postPayment , deletePage или insert . Лучше избегать использования сокращенных имен , поскольку они часто сбивают с толку и интуитивно понятны только на начальном этапе разработки.

Вместо этого используйте произносимые имена . Их легче понять и запомнить при чтении и понимании кода.

Плохое имя без комментариев не имеет смысла:

Лучше говорящее название:

Выберите один термин для каждой концепции и придерживайтесь его

Это сбивает с толку, когда fetch , retrieve и get являются эквивалентными методами разных классов.

Использование разных имен для одних и тех же или похожих вещей сбивает читателя с толку и вызывает вопросы, например, чем отличаются эти названные вещи, поскольку они называются по-разному.

Улучшить функции

Первое, что вы можете сделать для улучшения функций, — это сделать их маленькими . Когда я говорю маленький, я имею в виду до 50 строк, а лучше до 10-15. Сохраняя функции маленькими, вы уменьшаете контекст , о котором вам приходится думать во время чтения.

Отступы в функциях также увеличивают сохранение контекста . У вас должно быть хорошее объяснение того, почему у вас есть 2 (или более) уровня отступа в функции.

Если возможно, замените вложенные блоки отступов вызовами функций .

Версия `calculateEmployeeSalary` с меньшим отступом сверху:

Чтобы улучшить читабельность, функция должна делать только одну вещь .

Длинные функции часто делают несколько вещей, что делает их длинными. Чтобы делать несколько вещей, сохраняя при этом читабельность кода, вы должны разделить длинные функции на несколько более мелких функций.

Функция, вызывающая другие функции, по-прежнему должна делать только одну вещь, только немного более абстрактную.

Внедрить несколько уровней абстракции

Извлекайте фрагменты кода в функции, если вы можете описать поведение с более высоким уровнем абстракции.

Многие функции более высокого уровня абстракции легче понять по сравнению с несколькими низкоуровневыми. Абстракции должны быть основаны на поведении предметной области .

Произвольные абстракции, введенные с целью повторного использования кода, чаще всего только ухудшают ситуацию. Старайтесь поддерживать вызовы функций на одном уровне абстракции . Иногда для этого требуются однострочные функции только для уровня абстракции, что нормально (производительность не должна быть проблемой, поскольку современные языки оптимизируются для этого путем встраивания).

Иерархическая организация функций по уровням позволяет коду рассказывать поведенческую историю , а не пошаговый рецепт.

Функции должны иметь как можно меньше аргументов . Редко следует использовать более 2.

Многие аргументы усложняют понимание кода , особенно при использовании аргументов в качестве выходных данных (аргументы, которые изменяются после завершения функции).

Выходные аргументы часто бывают неожиданными и неестественными, поэтому избегайте их.

ПРИМЕЧАНИЕ . В предыдущем примере используются три аргумента, чтобы оставаться как можно более похожим на связанный пример, но этого следует избегать.

Функция должна быть либо командой, выполняющей действие (побочный эффект), либо запросом, возвращающим данные вызывающей стороне , но не тем и другим одновременно.

Их «роль» следует подчеркивать в названии. Никогда не должно быть недопонимания, является ли функция командой или запросом по ее имени.

Назовите пример команды, которая изменяет состояние своих аргументов:

Назовите пример запроса, который извлекает значения без изменения состояния аргументов:

Побочные эффекты всегда должны быть явными. Они никогда не должны быть неожиданными. Если в этом нет необходимости, избегайте их. Старайтесь, чтобы функции оставались «чистыми», т.е. без состояния.

Предпочитать неизменность

Что на самом деле означает «неизменяемость» в программировании?

Это свойство, указывающее, что объект/переменная не может быть изменена после того, как он полностью создан . Неизменяемый код вместо мутирующего состояния объекта создает копию с измененным состоянием.

Изменяемый код:

То же поведение в неизменном коде:

Неизменяемость делает код более простым и понятным .

Зная, что объект неизменяем, вам не нужно беспокоиться о том, изменит ли его функция, которой вы передаете объект. Параллельное программирование также становится проще и чище.

Неизменяемые объекты по своей сути потокобезопасны , что означает отсутствие необходимости в блоках синхронизации, поскольку вы всегда работаете с неизменяемым непротиворечивым состоянием.

Конечно, иногда логика также может быть неестественной, сложной или неэффективной и все же выражаться как неизменяемая. В этом случае вы все равно можете использовать изменчивость, но постарайтесь максимально уменьшить ее объем .

Предпочитайте декларативное программирование императивному

Важной частью написания кода, который ваши наследники будут легко читать, является выбор декларативного программирования над императивным . Что такое императив и что такое декларативное программирование?

Императивное программирование говорит , как что-то делать . Его внимание сосредоточено на пошаговой механике, такой как условные операторы, циклы, мутации.

С другой стороны, декларативное программирование говорит, что делать, а не как это делать . Его внимание сосредоточено на том, что должно быть сделано, то есть на целых действиях, а не на пошаговом объяснении. Декларативное программирование построено на основе пошагового стиля императивного программирования.

Попробуйте абстрагировать императивный код в декларативный .

В декларативном стиле бизнес-логика/намерения становятся более очевидными . Увидев назначение кода с первого взгляда, его обслуживание и исправление становится намного легче понять. Это позволяет нам точно определить проблемную часть, а затем углубиться в детали , в отличие от чтения всего кода сначала только для того, чтобы расшифровать его назначение.

Поведение более очевидно в декларативном стиле, как показано ниже:

Будь проще, глупый - ПОЦЕЛУЙ

Разработчики часто придумывают « умные » решения для той или иной проблемы.

Эти решения, хотя и кажутся в определенные моменты Божьим посланием, не должны быть предпочтительными, несмотря на то, что они могут повысить наше эго. Большую часть времени их трудно понять и следовать им, и тем самым еще труднее их поддерживать или обновлять.

Простота должна быть ключевой целью в дизайне . Избегайте ненужной сложности.

Не повторяйтесь – СУХОЙ

Дублирование — это плохо, особенно дублирование знаний. Вы должны стремиться к единому представлению знаний в вашей системе. Если знание меняется, вы должны изменить его в каждом месте, где оно дублируется.

Если вы пропустите дубликат, есть большая вероятность , что он превратится в ошибку .

Обслуживание легко превращается в кошмар с большим количеством дубликатов.

Это обычная практика копировать-вставлять при создании прототипа при работе над сложной проблемой. В этом нет ничего плохого, так как это помогает нам определить правильные абстракции знаний, что приводит к лучшему выражению намерения.

Но, как нас учили в детстве, после игры пора убираться. Эти дубликаты послужили своей цели. Теперь пришло время их удалить.

Тебе это не понадобится — YAGNI

Не внедряйте то, что вам «может» понадобиться в будущем, внедряйте только то, что вам нужно . То, что нам «может» понадобиться, очень часто не является тем, что нам впоследствии действительно понадобится.

Преждевременная реализация вещей, которые нам «могут» понадобиться, закрывает многие пути, по которым может пойти наш дизайн. Это вынуждает нас создавать дизайн, не оптимизированный для реальных потребностей нашего продукта , что, в свою очередь, приводит к плохому (более) и сложному (более) общему дизайну, который трудно поддерживать.

Следующие шаги

Следующими шагами, которые вы можете предпринять для улучшения, являются принципы SOLID .

Это пять принципов проектирования, призванные сделать дизайн программного обеспечения еще более понятным, гибким и удобным в сопровождении :

  1. Принцип единой ответственности – SRP
  2. Принцип «открыто-закрыто» — OCP
  3. Принцип замещения Лискова - LSP
  4. Принцип разделения интерфейсов — Интернет-провайдер
  5. Принцип инверсии зависимостей – DIP

Следуя этим (среди некоторых других) принципам, нам удалось сохранить большую часть кода и дизайна предметной области читабельными, легко поддерживаемыми и обновляемыми .

Причина, по которой мы это сделали, заключается в том, чтобы подготовиться к приему новых членов нашей команды, в первую очередь, сделав процесс адаптации более эффективным и максимально комфортным.

Говоря о новых членах, ознакомьтесь с нашей открытой вакансией старшего разработчика Java или любыми другими вакансиями на нашей странице вакансий.