Поиск  
Always will be ready notify the world about expectations as easy as possible: job change page
9 марта 2015 г.

Три ключевых принципа ПО, которые вы должны понимать

Источник:
Просмотров:
2021

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

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

DRY – Don’t repeat yourself (не повторяй себя)

Этот принцип настолько важен, что не требует повторения! Обычно его упоминают акронимом DRY, который впервые появился в небезызвестной книге "The pragmatic programmer", но концепт, сам по себе, был известен довольно давно. Он относится к самым мелким частям вашего ПО.

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

Самый простой подход по уменьшению сложности — разделить систему на управляемые части.

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

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

Принцип DRY требует, чтобы такие части информации встречались в вашем коде один, и только один раз.

Эти части должны иметь одно представление.

Обратите внимание на различие между информацией (данными), и ее представлением. Когда мы организовываем соединение с БД в нашей CMS, у нас будет код, который будет поднимать нужный драйвер, принимать авторизацию, и сохранять указатель на соединение в переменную. Здесь, код является информацией, описывающей, как мы получаем что-то (соединение). Переменная с указателем на соединение является представлением этой информации, которая может быть использована в любом месте системы. Если вдруг у нас изменится авторизация, нам нужно будет изменить только «данные», но не их представление.

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

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

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

Задача архитектуры – управлять сложностью

Перестаем повторяться

Путей ликвидировать повторения довольно много. Hunt и Thomas предлагают генераторы кода и трансформацию данных. Но DRY, в итоге, является философией выдачи логики через представления.
Каждая часть вашего приложения может быть представлением, каждая часть выдает определенную логику – модуль управления авторизацией дает доступ вашим пользователям, класс пользователя содержит информацию про текущего пользователя с набором его свойств. Этот класс, в свою очередь, получает данные через представление БД.

DRY — это философия, разбивающая логику на представления.

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

  • Составьте графическую схему вашей системы, разделите ее на визуальные компоненты. Сложные проекты могут потребовать такие иерархии на каждый компонент.
  • Если вы вливаетесь в смежный уровень реализаций (часть общей системы), можно попробовать использовать диаграммы UML (или похожие)
  • Перед написанием кода отметьте его в вашей графической схеме, определитесь с его ролью среди других компонентов системы.
  • Четко определите представления, которые ваш код будет выдавать другим частям системы, и которые он должен скрывать (private/public).
  • Убедитесь, что ваш код слабо связан с другими представлениями системы – жесткие связки очень плохо сказываются на общей архитектуре.

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

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

В реальных проектах достичь 100% DRY практически нереально. Но, в свою очередь, проектов, которые не-DRY на неприемлемом уровне, и которыми сложно управлять, довольно много. И, возможно это для вас будет сюрпризом – 50% всех наших проектов провальны, если взглянуть на их код.

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

Пример

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

Редко плохой код пишут плохие программисты.

Мы помним – повторяемость ликвидируется хорошим планированием. Срочные изменения в системе влекут срочные, неоптимальные решения в коде. Как только код подвергается плохим решениям, весь принцип DRY для данного решения перестает работать, до будущих изменений.

Если вы посмотрите на историю самых успешных IT компаний, многие из них были созданы людьми с пониманием проблемы — Bill Gates, Mark Zuckerberg, Steve Wozniak, Steve Jobs, Larry Page, Sergey Brin, Larry Ellison – эти люди знали, что им предстоит преодолеть для решения той или иной задачи. Но есть и компании, которые передают системное управление в руки бухгалтеров, а концептуальное – в руки консультантов. Ни те, ни другие не способны управлять такими областями.

DRY достигается совместным планированием.

Именно поэтому многие решения работают только в Powerpoint, Photoshop, и 27” экранах. Это отчасти работало в, более или менее, статичных, сайтах. Но не сегодня, в мире, где куча разных интерактивных приложений, и устройств.

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

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

Если вас просят не совать нос куда не нужно, читайте дальше – принцип YAGNI спасет вас!

KISS – keep it simple stupid (делайте вещи проще)

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

Зачастую самое простое объяснение является самым правильным решением.

Чуть позже, Альберт Эйнштейн, сотрудник швейцарского патентного бюро, предложил отказаться от этой теории, пренебречь всеми расчетами расстояний, и просто считать, что время не является константой – оно относительно. Такое решение проблемы с учетом минимального количества зависимостей объяснимо принципом «бритвы Оккама».

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

Большая часть прогресса в истории человечества была достигнута благодаря неординарным мыслителям.

HTTP

Протокол HTTP очень часто приводится, как пример идеального, простого решения – изначально созданный для передачи гипертекстовых документов, сегодня он является основой для многих интерактивных приложений. Возможно, нам иногда приходится искать обходные решения проблем, связанных с ограничениями это протокола, и может быть в будущем его сменит другой протокол. Но на сегодня факт остается фактом: основываясь не нескольких методах запросов (GET/POST), кодах статусов и простых текстовых аргументах, HTTP является достаточно гибким и надежным. Именно поэтому веб-разработчики стараются выжимать из него максимум, и именно поэтому этот протокол все еще в строю.

Подход делать вещи проще довольно очевидный, но история разработки ПО полна различных плохих, сырых решений. Их еще часто называют отдельным словом – bloatware, или DOA (dead on arrival) — мертвое при рождении. Относительного такого софта можно применить теорию, похожую на теорию не-DRY кода… Тем не менее, успех интернета можно объяснить простыми, эффективными решениями.

Так как достичь максимально возможного, простого решения? Все сводится к возможности поддержки, и детализации в разработке ПО. Именно поэтому KISS следует применять на этапе определения требований. Стараясь реализовать требования клиента в вашем коде, попытайтесь выделить следующее:

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

Как-то я принимал участие в проекте, где клиент хотел импортировать Excel таблицы в свою программу по управлению персоналом. Хороший пример. Excel является проприетарным приложением, со сложным форматом. Формат документов сложен, т.к. реализует богатый функционал – к примеру, в него можно добавлять графики и другие фишки, которые по сути, не нужны были клиенту. Ему нужны были просто числа из таблицы. Таким образом, внедряя импорт из Excel, пришлось бы потратить много времени на ненужный функционал. В добавок, существует несколько версий Excel, которых с каждым годом все больше и больше. Т.е. всем этим было бы сложно управлять, и были риски дополнительных затрат в будущем.

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

Мораль – старайтесь рассмотреть вещи с простой стороны, если они выглядят сложными. Если клиент вам рассказывает свои требования, реализация которых вам кажется сложной, вы правы в любом случае. Даже учитывая, что некоторые вещи действительно сложны в реализации, мы нередко сталкиваемся с решениями, которые перегружены необоснованно. Это случается, т.к. в процесс разработки вовлечены некоторые люди, не имеющие технического опыта для правильного расчета затраты/выгода. И они просто не видят всей проблемы. Поэтому всегда дважды проверяйте требования клиента, и убедитесь, что это именно то, что ему нужно. Обсудите критические моменты, объясните ему, почему другие решения могут подойти лучше.

You ain’t gonna need it – вам это не понадобится

Когда Google запустил Google+, Mark Zuckerberg, основатель Facebook-а, был одним из первых зарегистрированных в социалке, которая была призвана превзойти его сеть. Он написал всего лишь одну строку в разделе “Обо мне” – «Я строю вещи». Лично я считаю это блестящим предложением, раскрывающим всю суть программирования. Почему вы выбрали путь кодера? Энтузиазм в технических решениях? Красота эффективности? Чтобы вы не ответили, скорее всего это не будет «построить 100500-ый корпоративный сайт со стандартным функционалом». И тем не менее, многие из нас зарабатывают именно этим. Где бы вы не работали, вам периодически приходится сталкиваться со скучными, рутинными задачами.

Я программирую. Я строю вещи.

Принцип «Вам это не понадобится» (YAGNI – you ain’t gonna need it) как раз призван решать такие задачи.

Это значит, что то, что не задумано в системе, не должно появляться в коде. К примеру, достаточно часто доступ к БД осуществляется через абстракцию, которая может иметь реализацию для разных драйверов – MySQL, PostgreSQL, Oracle. Если вы работаете над сайтом, который размещается на LAMP стеке – какова вероятность того, что клиент сменит БД? Не забывайте, что концепт всегда пишется под бюджет – верно?

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

Вы, должно быть, заметили разницу между YAGNI и DRY системами. Последняя призвана уменьшать сложность, разделяя систему на управляемые компоненты, в то время, как первая уменьшает сложность, уменьшая количество этих компонент. Принцип YAGNI похож на KISS – он старается делать вещи как можно проще. Но KISS старается искать простые решения, а YAGNI просто не делает никаких решений!

Теодор Старджон, американский фантаст, выдвинул закон – «90% всего – полная чушь». Довольно радикальное утверждение, и не всегда применимое в реальных проектах. Не забывайте, что «чушь» может отнимать уйму времени. Существует негласное правило: примерно 80% затраченного времени на разработку тратится на реализацию всего лишь 20% функционала системы. Вспомните ваши проекты. Каждый раз, когда я пересматриваю свои, я постоянно убеждаюсь, как точно работает правило 80/20.

80% затраченного времени на разработку тратится на реализацию всего лишь 20% функционала системы.

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

Тем не менее, даже если этот подход может казаться довольно простым, зачастую не так то и просто отделить нужные от ненужных частей. К примеру, даже если у вас реализована абстракция, это не даст вам никакого выигрыша при дампе БД. Ключевым моментом является то, как мы смотрим на ПО – нас учили писать код, который легко поддерживать, и который будет выгоден в будущем. Т.е. нас учили смотреть наперед, учитывая все возможные изменения. Иногда это критично для больших проектов, но никак не для маленьких. Перестаньте думать о будущем! Если вдруг небольшому сайту потребуются радикальные изменения, будет лучше начать все с нуля. И это не такая уж и проблема, учитывая общие инвестиции.

Планирование проекта

Начиная планирование нового проекта, постарайтесь учесть следующее:

  • Достичь меньшей сложности путем уменьшения уровня абстракций
  • Разделить функционал от возможностей (features)
  • Учесть небольшие не-функциональные требования
  • Определить затратные по времени задачи, чтобы избавиться от них

Давайте разберем это подробнее. По первому пункту я уже приводил пример – не стоит реализовывать абстракцию к драйверу БД. Старайтесь тщательнее оценивать все, что может добавить сложности вашей системе. Учтите, что зачастую многие абстракции реализовываются в сторонних продуктах и библиотеках. К примеру, смотря на каком языке вы пишите, Hibernate (Java), Doctrine (PHP) или Active Record (Ruby) – все идут с уровнем абстракции вокруг БД, и ORM. Каждая из этих библиотек добавляет сложности. И ею придется управлять. Обновления, патчи, исправления в безопасности – все это вам придется делать/применять в будущем.

Каждый день мы внедряем новые возможности, которые, как нам кажется, будут полезны. Следовательно, мы загадываем наперед, и реализовываем слишком много. К примеру, многие клиенты хотят иметь мобильные версии своих сайтов. Мобильность может быть представлена многими значениями, это не обязательно дизайнерское решение. Это сценарий использования! Люди, которые пользуются мобильными сайтами, являются мобильными. Это значит, что им может понадобиться другая информация, или функционал, чем тем, которые пользуются десктопной версией. Представьте сайт кинотеатра – пользователи на пути в кинотеатр, в автобусе, скорее всего захотят увидеть время начала сеанса, а не 50-метровый трейлер.

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

Плохие концепты можно часто определить по отсутствию не-функциональных требований.

Не-функциональные требования не описывают поведения системы, они описывают дополнительные свойства, по которым можно оценить качество продукта. Т.к. описание качества предполагает знание продукта, плохие концепты можно часто определить по отсутствию не-функциональных требований. Возможность поддержки, уровень документации, легкость интеграции – все это примеры не-функциональных требований. Эти требования должны быть оценимыми. Т.е. «Сайт должен грузиться быстро» — слишком обобщенно, а вот «Сайт должен грузиться за 2 секунды в процессе теста производительности» — очень даже конкретно, и ясно. Если вы хотите применить подход YAGNI, постарайтесь учесть некоторые не-функциональные требования, даже если они не учтены в концепте (или учтены, но не очень четкие). Когда вы пишете такое требование, будьте реалистами – небольшой сайт, с 20-50 посещениями в день, не требует трехдневной настройки производительности, т.к. сайт будет грузиться быстро и так, если сервер не перегружен. Даже если компания сможет повысить свою посещаемость, купить более мощный хостинг не должно составить проблем.

Ну и напоследок, всегда помните правило 80/20. Старайтесь определить затратные по времени задачи. Если такая задача обязательна к реализации, придется ее сделать. Вопрос будет только как это сделать. Нужен ли нам еще один фреймворк с небольшим комьюнити? Придется ли вам переходить на только что вышедшую версию библиотеки, если документация по ней все еще не обновлена? Есть ли необходимость использовать новую CMS, если не все расширения к ней работоспособны? Насколько глубокий анализ придется провести, чтобы реализовать вашу задачу? Подход «так приходится делать всегда» — не очень захватывающий, но он поможет вам решить задачу без сюрпризов.

Также важно понимать, что все это вовсе не значит, что можно сесть и писать плохой код, приправленный хаками. Вы просто пишете небольшое приложение, а не плохое! Тем не менее, подход «вам это не понадобится» довольно практичен. Если он помогает сократить несколько строк кода, лично я считаю, что можно внести эту работу в бюджет, и небольшое не-DRY вполне приемлемо. Т.е. можно согласиться не немного возросшие затраты по поддержке – мы живем в реальном мире.

Давайте вернемся в нашей основной идее – мы строим вещи. Бетховен написал “Diabelli Variations” по контракту. Я не думаю, что он шел на компромиссы по бюджету. Он потратил больше времени, но не выпустил плохую музыку – он стремился написать идеальную.

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

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

Написать сообщение
Тип
Почта
Имя
*Сообщение