Часто разработка начинается с кода. Есть идея - мы открываем IDE, создаём проект и начинаем писать. Не задумываясь о том, что в этот момент мы ещё можем не понимать, какую проблему решаем и что вообще должна делать система.
В этой статье разберём путь от пользовательской потребности до реализации: UCD → FR/NFR → KISS → компонентная разработка → код.
С чего начинается задача?
Представим, заказчик приходит с простой формулировкой:
Нам нужен сайт, на котором студенты смогут смотреть расписание
На первый взгляд задача кажется понятной. Но что именно означает «смотреть расписание»?
Студенту нужно увидеть расписание на весь семестр или только ближайшую неделю? Нужно ли каждый раз выбирать группу? Что делать, если расписание изменилось? Должен ли сайт нормально работать с телефона? Нужно ли показывать преподавателя, аудиторию, тип занятия?
Одна короткая формулировка скрывает множество решений, которые нам нужно принять. Кажется, что достаточно обсудить их с заказчиком и получить ответы. Но здесь есть проблема: заказчик не обязательно является пользователем системы.
Заказчик может знать бизнес-процессы, цели проекта и ограничения, но при этом совершенно иначе представлять себе сценарии потенциальных пользователей. Например, для заказчика «расписание» - это таблица с информацией о занятиях на семестр, а для пользователя - способ быстро узнать информацию о конкретном занятии или ближайшей паре.
Поэтому одного разговора с заказчиком недостаточно. Нужно разобраться, кто будет пользоваться системой, какие задачи он решает и в каком контексте.
Именно здесь появляется UCD (User-Centered Design) - подход, при котором пользователь и его потребности становятся отправной точкой проектирования системы.
Сначала понимаем пользователя
В UCD проектирование начинается не с технологий, а с изучения пользовательского контекста: кто будет пользоваться системой, какую задачу он пытается решить и в каких условиях это происходит.
Например, мы наблюдаем за тем, как студенты действительно пользуются расписанием. Один открывает его утром с телефона по дороге в университет, другой - с компьютера вечером, чтобы посмотреть расписание на следующий день, третий планирует расписание на неделю вперед. При этом часто студент не просматривает всю неделю целиком: обычно ему нужна информация о ближайшей паре - время, аудитория, преподаватель и тип занятия.
Из этого можно сделать вывод, что для пользователя главная задача - не «посмотреть расписание», как сформулировал заказчик, а быстро найти нужную информацию о занятии в конкретной ситуации.
Теперь у нас не абстрактная формулировка задачи, а конкретная потребность пользователя. Но сама по себе потребность еще не говорит разработчику, что именно должна уметь система. Чтобы перейти от понимания к проектированию решения, ее нужно превратить в набор требований.
Теперь превращаем потребности в требования
Функциональные требования (FR, Functional Requirements) описывают то, что система должна делать.
Из потребности «быстро узнать информацию о ближайшей паре» получаются:
пользователь может выбрать свою учебную группу;
система показывает расписание выбранной группы;
пользователь может переключать день;
система показывает время, аудиторию, преподавателя и тип занятия;
Но даже если все эти функции реализованы, это ещё не означает, что система будет удобной. Пользователь может получить нужную информацию, но потратить на это минуту, открыть сайт с телефона и обнаружить, что таблица не помещается на экране. Поэтому нам нужны требования не только к функциям, но и к свойствам системы.
Нефункциональные требования (NFR, Non‑Functional Requirements) описывают то, какими характеристиками должна обладать система.
В нашем случае эти характеристики напрямую связаны с контекстом использования расписания: студент может открывать его с телефона, находясь в дороге, быстро искать нужную информацию или пользоваться сайтом с другого устройства. Поэтому из пользовательского сценария появляются следующие нефункциональные требования:
интерфейс должен корректно работать на мобильных устройствах;
основные действия должны быть доступны с клавиатуры;
расписание должно загружаться за приемлемое время;
система должна корректно работать в основных современных браузерах.
Теперь у нас есть требования, которые описывают и функциональность системы, и условия, которым она должна соответствовать. Но требований недостаточно, чтобы определить, как именно всё это реализовать. Для одной и той же задачи можно придумать несколько решений - от простой страницы до сложной архитектуры с множеством технологий.
И здесь возникает следующий вопрос: какое из этих решений действительно нам необходимо?
Не усложняй то, что можно сделать проще
К этому моменту мы уже понимаем, что должна делать система и каким требованиям она должна соответствовать. Но всё ещё остаётся вопрос реализации. Например, расписание можно сделать обычной статической страницей с JavaScript, полноценным серверным приложением или построить клиентское приложение на React с отдельным API.
На этом этапе легко пойти по пути «чем больше технологий, тем лучше». Если для задачи уже достаточно HTML, CSS и JavaScript, можно добавить React. Если данные можно получить напрямую, можно добавить отдельный API. Если проект небольшой, всё равно можно начать разбивать его на множество отдельных слоёв и абстракций.
Каждое такое решение само по себе может быть вполне разумным. Проблема начинается тогда, когда мы добавляем их не потому, что они нужны для решения задачи, а просто потому, что можем.
Поэтому при выборе решения стоит задать простой вопрос: можно ли решить эту задачу проще?
Именно эту идею выражает принцип KISS (Keep It Simple, Stupid) - не усложняй решение, если в этом нет необходимости.
В нашем случае это означает, что сначала стоит рассмотреть самое простое решение, которое удовлетворяет сформулированным требованиям. Например, если расписание заранее подготовлено и не требует динамического редактирования, нам может хватить обычной страницы с JavaScript и данными в JSON.
Если же расписание должно храниться в базе данных и изменяться через административную панель, появляется необходимость в серверной части. Тогда можно добавить PHP и базу данных.
При этом нам не нужно заранее добавлять технологии только на случай будущих проблем. Если появится новая потребность - архитектуру можно будет усложнить тогда, когда это действительно понадобится.
Однако простота решения не означает, что весь проект должен находиться в одном файле. По мере развития системы в ней появляются повторяющиеся элементы и части интерфейса, которые имеют собственную ответственность. Их можно выделить в отдельные компоненты, не усложняя при этом архитектуру.
Разделяем интерфейс на компоненты
KISS помогает не добавлять в проект лишнюю сложность, но даже простое приложение со временем может разрастись. У нас появляются страницы, формы, элементы управления и повторяющиеся части интерфейса. Если держать всё в одном файле, изменения в одной части приложения начинают затрагивать другие.
Грубо говоря, в нашем проекте есть несколько самостоятельных частей интерфейса. Например, выбор учебной группы, переключение дня, список занятий и карточка отдельного занятия. Каждую такую часть мы можем выделить в отдельный компонент.
Например, выделим:
GroupSelector - выбор учебной группы
DaySwitcher - переключение дня
Schedule - расписание
LessonCard - информация об одном занятии
Эти части отвечают за разные задачи. Поэтому изменения в одной части интерфейса не обязательно требуют изменений во всём остальном проекте. Если мы решим изменить способ выбора группы, нам не придётся переписывать расписание. Если изменится внешний вид карточки занятия, это не должно затрагивать переключатель дней. Вместо одного большого интерфейса мы получаем набор частей, у каждой из них есть своя ответственность, а вместе они формируют готовый интерфейс.
Такой подход и называют компонентной разработкой: интерфейс разбивается на самостоятельные компоненты, каждый из которых отвечает за определённую часть пользовательского интерфейса и его поведения.
Представим, что через месяц заказчик попросил изменить отображение занятия: добавить номер корпуса и сделать время начала более заметным. Если информация о занятии собрана в LessonCard, мы изменяем только этот компонент. Остальная часть интерфейса остаётся без изменений.
Это особенно важно по мере роста проекта. Чем больше интерфейс, тем сложнее становится держать его целиком в голове. Разделение на компоненты позволяет работать с отдельными частями системы, не погружаясь каждый раз во весь проект.
А теперь можно кодить
Мы прошли довольно длинный путь, прежде чем написать первую строку кода. Но теперь у нас есть всё необходимое: мы понимаем пользователя, знаем его потребность, сформулировали требования, выбрали минимально необходимое решение и определили основные компоненты интерфейса.
Теперь действительно можно открывать IDE и начинать реализацию.
И здесь есть главное отличие от того, с чего мы начали: теперь каждая строка кода появляется не потому, что мы просто хотим что-то написать, а потому, что за ней стоит конкретная потребность, требование или решение.
Код - это не начало разработки. Это результат принятых решений.