Site icon AppTractor

Григорий Петров: Изменяющиеся требования — проклятье индустрии

OLYMPUS DIGITAL CAMERA

← Предыдущая статья

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

Архитектура программного обеспечения

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

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

Как я уже рассказал, борьба со сложностью в большинстве случаев ведется простыми методами — разделением сложного на части и добавлением уровней абстракций. Нашему мозгу привычно при превышении определенного количества объектов делить их на группы, и так же мы поступаем с кодом. Если рассматривать написание программы с нуля, то оно похоже на эволюцию живого организма. В начале весь текст программы находится одном файле, что является аналогией одноклеточного существа. Когда размер файла превышает возможности программиста быстро его понять — функциональность делится на части и разделяется на несколько файлов, по аналогии с простейшим многоклеточным. С увеличением сложности функциональные части программы усложняются и все более обосабливаются, выделяясь в модули и компоненты, по аналогии с выделением групп клеток в отдельные органы. Более высокие уровни абстракции — это разделение программы на узкоспециализированные, изолированные части: библиотеки, утилиты, сервисы, по аналогии со стаей живых организмов, в которой отдельные особи взаимодействуют между собой.

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

Когда я консультирую новый проект, изучение «архитектуры» начинается с того, что мне рассказывают о разделении программы на самые крупные «части». В зависимости от масштаба проекта, это могут быть как кластеры серверов разного назначение, связанные enterprise middleware, так и несколько файлов внутри стандартного фреймворка, по которым раскидана функциональность пока еще молодого веб-портала. Это «вид сверху» — он позволяет выделить стандартные части, нестандартные части и понять, как «органы» взаимодействуют друг с другом, образуя единый организм. Морской огурец, к примеру, может дышать через жопу. Эволюционно так сложилось. И многие программы по внутреннему устройству легко затыкают его за пояс — череда меняющихся требований приводит к появлению забавных и жутковатых внутренних механизмов, оказывающих не самое лучшее влияние на развитие продукта.

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

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

Изменяющиеся требования как фактор эволюции

Итак, архитектуру я определил. Теперь перейдем к интересному — что плохого для архитектуры в изменяющихся требованиях? Хочет заказчик, чтобы ящерка не бегала, а летала — в чем сложность атрофировать ножки и отрастить крылья?

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

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

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

Как бороться с проклятьями

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

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

Exit mobile version