Совершенствуйте свои навыки разработки программного обеспечения

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

Цель всех этих принципов — сделать программное обеспечение простым в обслуживании за счет снижения умственной сложности.

№1: Не повторяйся (СУХОЙ)

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

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

Риск: если вы делаете это слишком часто, код обычно становится более сложным.

Инструментальная поддержка: существуют программы для поиска повторяющегося кода. Для Python есть pylint --disable=all --enable=similarities src

№2: Тебе это не понадобится (ЯГНИ)

YAGNI — это осознание того, что слишком большая абстракция на самом деле вредит удобству сопровождения. Разработчики Java, я смотрю на вас!

Преимущество: снижение сложности. Удаление абстракций делает более понятным, как работает код.

Риск: если вы применяете YAGNI слишком часто и, таким образом, делаете слишком мало абстракций, вам будет трудно расширять свое программное обеспечение. А младшие разработчики могут испортить код.

Инструментальная поддержка: нет (может быть, когда-то можно было подсчитывать ссылки на классы в кодовой базе?)

№3: Будь проще и глупее (KISS)

KISS может применяться во многих ситуациях. Хотя некоторые решения являются умными и решают поставленную проблему, более глупое решение может быть лучше, потому что вероятность возникновения проблем меньше. Это может быть менее DRY время от времени.

№4: Принцип наименьшего удивления

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

Преимущество: снижение сложности. Вы следите за тем, чтобы ментальная модель системы соответствовала естественным представлениям людей.

Риск. Возможно, вам придется нарушить DRY, чтобы это сделать.

Инструментальная поддержка: нет. Однако есть некоторые признаки того, что это было нарушено: (1) Вы снова и снова объясняете одни и те же особенности своей системы новым коллегам (2) Вам нужно снова и снова просматривать одну и ту же тему (3) Вы чувствуете необходимость для документирования темы, которая по своей сути не сложна.

№5: Разделение интересов (SoC)

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

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

Выражаясь философией Unix: Делайте одно и делайте это хорошо.

Преимущество: снижение сложности: (1) обычно становится более понятно, где необходимо внести коррективы. (2) У вас должно быть меньше нежелательных побочных эффектов, о которых вам нужно подумать. (3) Люди могут работать параллельно без большого количества конфликтов слияния.

Риск: если вы переусердствуете с SoC, вы, скорее всего, нарушите правила KISS или YAGNI.

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

№6: Ошибаться рано, ошибаться громко

Как разработчики, мы должны иметь дело со всеми видами ошибок. И особенно новичкам, непонятно как с ними бороться.

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

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

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

Риск: отсутствует.

Инструментальная поддержка: нет

№7: Защитное программирование

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

Три основных правила защитного программирования

  • Все данные важны, пока не доказано обратное.
  • Все данные испорчены, пока не доказано обратное.
  • Весь код небезопасен, пока не доказано обратное.

Альтернатива защитному программированию — «дерьмо внутри, дерьмо».

Преимущество: более высокая надежность

Риск: больше обслуживания из-за более сложной/длинной кодовой базы.

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

№8: ТВЕРДЫЙ

Принципы SOLID дают рекомендации относительно связи и сплоченности. Первоначально они были сформулированы для объектно-ориентированного программирования (ООП), но вы можете применять их и к другим уровням абстракции, чем к классам, например. услуги или функции. Позже я буду называть эти «компоненты».

Два компонента могут быть соединены несколькими способами. Например, одной службе может потребоваться знание того, как внутри работает другая служба, чтобы выполнять свою работу. Чем сильнее компонент А зависит от компонента В, тем сильнее А связан с В. Обратите внимание, что это симметричная связь. Для связи нас не волнует направление.

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

Слабая связанность и высокая согласованность между компонентами — вот к чему мы стремимся.

Принцип единой ответственности

«У класса никогда не должно быть более одной причины для изменения».

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

Единая ответственность является одним из инструментов для достижения разделения ответственности.

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

Принцип «открыто-закрыто»

«Программные объекты… должны быть открыты для расширения, но закрыты для модификации».

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

Принцип подстановки Лисков

«Функции, использующие указатели или ссылки на базовые классы, должны иметь возможность использовать объекты производных классов, не зная об этом».

Преимущество. Это основное предположение, которое вы делаете в ООП. Просто следуй этому.

Риск: нет?

Принцип разделения интерфейсов

«Многие клиентские интерфейсы лучше, чем один интерфейс общего назначения».

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

Риск: нарушение KISS/YAGNI.

Принцип инверсии зависимости

«Зависите от абстракций, [не] конкреций».

В некоторых случаях вы хотите работать с более общим классом входных данных, чем с одним конкретным типом, с которым вы имеете дело в данный момент. Примеры, которые приходят мне на ум, это WSGI (соединение веб-серверов с веб-клиентами), JDBC (соединения с базами данных) и практически любая система плагинов (например, Flake8). Вы хотите определить интерфейс и зависеть от него. Затем этот интерфейс должен быть реализован компонентами.

Предположим, у вас есть приложение, которому требуется доступ к реляционной базе данных. Теперь вы можете реализовать все запросы для всех типов реляционных баз данных. Или вы определяете, что функция получает коннектор базы данных, который реализует интерфейс JDBC. Теперь коннектор должен быть расширен и не все функции, которые требуют подключения к БД. Это инверсия.

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

Риск: если вы переусердствуете, вы можете нарушить KISS. Эмпирическим правилом будет то, что по крайней мере два класса должны реализовать интерфейс, прежде чем вы создадите интерфейс.

№9: Принцип локальности

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

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

Риск: нарушение слабой связи или разделения ответственности.

Инструментальная поддержка: пока нет, но я думаю о создании такой поддержки для Python. По сути, я бы проанализировал коммиты git.

Подводя итог: дзен Python

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

Красивое лучше, чем безобразное.
Явное лучше, чем скрытое.
Простое лучше, чем сложное.
Сложное лучше, чем сложное.

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

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

Ошибки никогда не должны оставаться незамеченными.
Если они явно не отключены.

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

Сейчас лучше, чем никогда.
Хотя никогда лучше, чем прямо сейчас.

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

Пространства имен — это отличная идея — давайте сделаем больше таких!

Я люблю писать о разработке программного обеспечения и технологиях 🤩 Не пропустите обновления: Получить мою бесплатную рассылку по электронной почте 📧 или подпишитесь на Medium ✍️, если вы еще этого не сделали — и то, и другое мотивирует меня. писать больше 🤗