Методики поддержки стилей
Старший аспирант «Нетологии» Алена Батицкая перевела статью «On Style Maintenance» Сары Дрезнер.
На днях я разговаривала со своим другом, великолепным инженером. Он рассказал, что никогда не создавал ничего с нуля. Вся его карьера строилась вокруг поддержки чужого (часто довольно плохого) кода.
В идеальном мире все мы писали бы код с нуля, он бы работал безупречно, мы забывали бы про него моментально и никому не нужно было бы снова в него заглядывать.
Мы все знаем, что так не бывает. Код должен быть поддерживаемым.
Кайл Симпсон часто начинает свое выступление с рассказа о то, что мы тратим только 30% своего времени на написание кода. А оставшиеся 70% тратим на поддержку уже написанного. Быть хорошим коллегой и программистом значит не только умело решать проблемы, но быть понятным.
Я часто шучу, что не хочу нанимать ниндзя-кодера. Ниндзи приходят посреди ночи и оставляют за собой кровавое месиво.
Я хочу кодера-убощика. Кого-то кто ходит по коридорам кода, подметает мусор, стирает заброшенные части, полирует оставшиеся, выбрасывает ненужные кусочки. Я предпочитаю эту, более точную и приятную аналогию. Это именно тот специалист, которого вы хотите видеть в своей команде. Тот, кому вы хотите отдать код на code review.
Овации JD Cantrell. Он невероятный кодер-уборщик и рецензент.
Поддержка кода через призму двух парадигм программирования
В программировании одного и того же результата можно достичь несколькими способами. Нет единственно верного ответа. Вот определение Майкла Фейзера двух наиболее популярных парадигм программирования:
Объектно-ориентированное программирование (ООП) делает код понятным за счет инкапсуляции реиспользуемых частей.
Функциональное программирование (ФП) делает код понятным за счет минимального использования реиспользуемых частей.
Давайте внимательно рассмотрим оба варианта не только с точки зрения понятности (читабельности), но поддерживаемости в дальнейшем.
Функциональное программирование
Я видела ценность в использовании функционального программирования при написании фронтенда на JavaScript. Мы часто надеемся, что код будет использоваться вечно точно в том виде, в котором мы его написали. Но многие из нас по прошествии времени готовы признать, что это не всегда так.
Функциональное программирование включает, но не ограничивается следующим:
- Оно декларативное — мы пишем так, чтобы код можно было максимально реиспользовать и не говорить компьютеру раз за разом что мы хотим в точности получить. Это чем-то похоже на абстракцию.
- Оно чистое — мы не изменяем и не модифицируем ничего за пределами области видимости функции. И по этой причине
- Оно неизменяемое — вам не захочется оказаться в ситуации, когда вы передаете одинаковые значения и получаете разные результаты нескольких вычислений.
Я считаю, что ФП чрезвычайно удобно с точки зрения поддержки из-за отсутствия побочных эффектов. Это много значит. Это уберегает наш код от ломкости. Люди иногда думают, что ошибки в коде — это самая большая проблема. Я же утверждаю, что плох не тот код, который выдает ошибки и не работает. Мы можем разобраться с этим при помощи методики изоляции.
Наихудший код тот, что повсеместно ведет себя абсолютно непредсказуемо.
Вы скачете вокруг кода и играете в игру «Поймай крота». Часто сложно найти причину такого поведения. ФП превентивно решает эту проблему, потому что оно изначально создавалось для исключения подобных ситуаций (как правило).
Вот несколько ресурсов, если вы хотите глубже разобраться в ФП:
- Anjana Vakil: Learning Functional Programming with JavaScript — JSUnconf 2016
- Mary Rose Cook: A practical introduction to functional programming
- Kyle Simpson: Functional Lite
Как бы я не была без ума от ФП, пожалуйста, помните, что потребность в поддержке все же остается. Если в нескольких местах используется одинаковая функция и спустя какое-то время вы решите изменить эту функцию, то можете попасть в западню со скрытыми зависимостями. В этой ситуации вам очень поможет хорошо написанная документация.
Объектно-ориентированное программирование
В отличии от ФП, Объектно-Ориентированное программирование является чуть большим, нежели простое следование инструкциям. В нем используются объекты, которые могут содержать или не содержать данные, и методы, которые обычно являются процедурными. В типичные объекты заложена идея самоуправления (например, this в JavaScript). ООП не сосредоточено на чистоте. Напротив, оно стремится использовать инкапсуляцию для предотвращения утечек между областями видимости.
А что еще лучше, объектно-ориентированный подход побуждает мыслить категориями высокого порядка, затем спускаться к каждому отдельному необходимому для реализации участку, выясняя что необходимо в каждом отдельном случае. Подумайте об этом как о линейной системе классификаций животных или о морфологическом дереве (ведь каждый из нас его составлял :)). Вы начнете с вопроса о теплокровности? Есть ли у этого животного мех? Есть ли рыло? И так далее. Я жестоко пренебрегла биологией, но это всего лишь пример.
Плохо в ООП то, что оно полно ненужного критицизма поскольку вы не всегда описываете то, что хотите описать. Для примера: вы думаете о чем-то как о банане, хотя на самом деле оно является персиком, а все из-за того, что вам известно только что это нечто — фрукт. Выше я упоминала, что этот подход может стать кошмаром в плане поддержки. Это, безусловно, не всегда так. Но стоит об этом помнить.
Применимо к CSS
Теперь, понимая оба подхода, мы можем рассмотреть как полученные знания можно применить для поддержки CSS. CSS пишется декларативно и просто. Вы не можете видоизменить один блок стилей при помощи другого.
Наверняка многие думают, что функциональный подход как раз для CSS. Они обосновывают это тем, что следует оставлять CSS чистым на столько, на сколько это возможно. Отсюда и взялась идея CSS-модулей. Концепция CSS-модулей: если вы инкапсулируете стили элементов, то вы меняете их именно там, где это нужно. Вы имеете дело с одним экземпляром. Никаких побочных эффектов. Я с этим не согласна. Таким способом вы избегаете нескольких вещей.
- Вы избегаете коллизий. В этом месте ООП иногда наносит удар исподтишка.
- Вы избегаете правильного нейминга. Нейминг — сложная штука. Вы можете не заморачиваться за нейминг при таком подходе.
- Вы избегаете работать с каскадом. Я уточню этот момент ниже.
Для других детей (не CSS) программирования мы стараемся сохранять вещи простыми и уклониться от глобальной области видимости. И мы в выигрыше. И так должно быть везде, верно?
В этом месте нам нужно найти правильный инструмент для работы. Если мы поставим поддержку выше написания, то нам подойдут иные подходы:
- Большие и маленькие компании делают редизайн каждые 1–2 года. Если у вас большая кодовая база с множеством специалистов в штате и перед вами встает задача изменить высоту строки с 1.1rem на 1.2rem везде, то вы вынуждены заходить в каждый модуль и менять значение? Глобальный или расширяемый объект становится в этом случае крайне полезным.
- Каскад может быть полезен, если вы понимаете и правильно организуете его. Это сложное проектирование программного обеспечения. Вам нужно смотреть на то, что вы создали и принимать решения о структуре со всей ответственностью. Вы решаете, что может находиться на верхнем уровне и должно быть наследовано другими, более маленькими кусочками. Это помогает избегать лапши в CSS и позволяет соблюдать принцип DRY.
- CSS это про дизайн. Хорошим дизайн можно назвать, если он монолитный, целостный. Кодовая база CSS, спроектированная по всем правилам, облегчает дальнейшую коллаборацию. CSS может служить средством самопроверки дизайнера: «Подождите, тут стоит другая кнопка. Почему?» Каскадность в случае UI/UX дает свои плоды.
- Нейминг сильно помогает при написании документации. Это острый момент и я не до конца уверена в абсолютной его необходимости, но стоит иметь это в виду. Любите ли вы БЭМ, SMACSS/OOCSS или Атомарность, правильный нейминг может дать вам много информации о месте использования и назначении определенного класса.
Характерные общие черты
Как вы возможно уже догадались, хотя я и понимаю значимость других парадигм, я отдаю предпочтение функциональному стилю. Зная это, вы можете поинтересоваться, почему я нахожу причины для использования Объектно-Ориентированного подхода в CSS.
Несмотря на название, OOCSS имеет общие черты с ФП. При работе с OOCSS вы в основном создаете в своей системе миксины, которые по факту схожи с функциями с параметрами (часто с параметрами по умолчанию). Они чистые и могут быть многократно использованы.
Применяется и Объектно-Ориентированный подход. Он выражается в интеллектуальной архитектуре, построенной с учетом того, как элементы могут влиять друг на друга и того, что должно быть унаследовано. Учитывая, что CSS по сути своей это набор объектов, написанное выше имеет больше смысла в контексте CSS, нежели в контексте других языков.
В прошлом году я возглавляла команду, которая занималась рефакторингом гигантской системы компонентов. В проекте использовалась OOCSS архитектура. Я увидела преимущества этой системы. Дизайнеры могли попросить меня изменить что-нибудь и мы могли быстро поменять это на всем сайте. Они (дизайнеры) могли передумать, но это все равно не доставляло проблем и хлопот. В последнюю минуту запрос сверху остановил релиз. Я не утверждаю, что все прошло безболезненно, но все прошло на удивление гладко для таких масштабов. Это позволило мне смотреть на написание CSS для других приложений в совершенно новой точки зрения.
На будущее
Обычно люди противопоставляют друг другу:
- CSS
- Стили, заданные с помощью JavaScript
Я бы сказала, что в обоих случаях возможно создать или удобный для поддержки код, или ночной кошмар поддержки.
Вы совершенно точно можете работать с высотой строк, шрифтами и адаптивностью с помощью Aphrodite, React-CXS или CSS-модулях. Я очень люблю некоторые из этих подходов. Они могут гарантировать наследование и пишутся в стиле DRY с оглядкой на поддержку.
Но то, что вы можете это делать не значит, что вы будете. Лично я не встречала достаточно примеров проектов, в которых соблюдается этот подход. (С удовольствием посмотрю на примеры!)
Обычно я встречаю людей, использующих одну-две переменные для брендовых цветов. И все. Несколько переменных не делают архитектуру масштабируемой. Думаю, что мы способны на большее, если будем думать не о том, как попроще написать код, а о том, как проще потом его изменять. Или даже лучше: о том, на сколько просто кто-то другой сможет его изменить.
Пожалуйста, помимо всего заботьтесь о создании хорошей документации! В рамках любой из парадигм программирования вы должны знать о всех возможных зависимостях перед началом работы. Отличная документация так же помогает принимать более взвешенные решения. Вы будете меньше использовать фразу «ну, типа», объясняя другим что это за штука и где она используется.
Примеры, которые я приводила в этой статье, относятся к теме масштабирования и коллаборации. Не все компании/сайты/web-приложения сталкиваются с этими проблемами. В этой статье выражено мое личное мнение, основанное на опыте. Все проекты разные и работают по-разному.
Целью этой статьи было побудить разработчиков чаще думать о будущем. Мы все в одной лодке и нам стоит учиться друг у друга. Если вы принципиально не согласны со мной, то это абсолютно нормально. Надеюсь только, что вы обратите больше внимания на ключевую концепцию поддерживаемости вне зависимости от точки зрения.
Полный текст статьи читайте на Нетология