Еще в 22-м году я рассказывал, как за довольно небольшой промежуток времени мы смогли ворваться в ТОП-3 финтех-приложений для малого и среднего бизнеса. С тех пор уже состоялось новое исследование MarksWebb 24-го года, в котором мы стали лучшими. В мае текущего года мы стали серебряными призерами — с колоссальным отрывом от всех остальных преследователей.
На связи Кирилл Маканков, iOS-разработчик из ПСБ.
В новой статье я хотел бы рассказать о тех системных изменениях, которые мы целенаправленно продолжаем вносить в процессы разработки, чтобы достичь этих почётных званий. Статья будет полезна:
руководителям всех направлений разработки, так как показывает, где и как находить неэффективные процессы, и, самое главное, — как радикально повышать их эффективность;
архитекторам программного обеспечения, так как именно они ответственны за эффективность процессов разработки;
рядовым разработчикам, так как показывает эффективные процессы, которые позволяют меньшими затратами получать существенно большие результаты и тем самым выдерживать конкуренцию на современном рынке.
Что же было раньше?
Но начать я хотел бы с краткого перечисления того, что уже было сделано и описано ранее.
Начали мы с модуляризации, создания дизайн-системы (далее — ДС), стандартизации практик разработки, исчерпывающего документирования всех процессов разработки, внедрения обязательного юнит-тестирования в объеме не меньшем, чем уже имеется в проекте. Эти меры позволили нам существенно повысить эффективность сотрудников: они стали меньше времени тратить на необязательные активности.
Например, мы проанализировали и подробно описали, что кодинг в работе среднестатистического сотрудника занимает только треть времени. Пятую же часть времени в типичном процессе разработки занимает поддержка, от которой вполне можно избавиться полностью или сократить её до очень незначительных величин. В указанной статье мой коллега подробно описал, что модульные тесты способны радикально сократить время на поддержку ранее написанного кода.
В свою очередь ДС позволяет радикально сократить временные затраты на взаимодействие коллег в команде, так как каждый компонент ДС заранее жестко определен, имеет конкретные состояния, алгоритмы работы, что сильно сужает простор неопределенности при взаимодействии с компонентом.
Перечислять, как все упомянутые ранее инструменты помогли нам сократить работу, без которой вполне можно обойтись, я не стану. Лишь отмечу, что это действительно помогло — мобильная разработка действительно идёт хорошими темпами. Чтобы этого достичь, мы следовали двум фундаментальным принципам:
— избегаем ненужных работ;
— контролируем эволюцию.
Далее я подробнее раскрою оба принципа. Но сначала отмечу, что в прошлом году наша команда закрепила их в Стратегии Развития Центра Компетенций iOS-разработки (далее — «Стратегия ЦК»).
Избегание ненужных работ

Ненужные работы – основная причина, убивающая эффективность и производительность любой, даже самой производительной команды. Все работы должны быть сфокусированы вокруг главной цели существования подразделения. В нашем случае такая цель обозначена в Стратегии ЦК следующим образом:
«Эффективно и гибко предоставлять качественный и устойчивый продукт, своевременно удовлетворяющий ожидания пользователей».
Детально проанализировав нашу цель, мы поняли, что такими абсолютно ненужными для нашей цели работами являются (отсортировано от наиболее бесполезного к наименее):
1. «круги на воде», или комбинаторный взрыв спровоцированных изменений;
2. переписывание успешно работающего функционала без необходимости (в том числе масштабный рефакторинг ради рефакторинга);
3. повторения, дублирования функционала различными направлениями бизнеса без переиспользования уже разработанного;
4. потери из-за отсутствия понимания текущего состояния процессов и технологий;
5. потери на реализации и поддержке функционала;
6. потери на коммуникациях.
Далее подробно объясню, что же это за ненужные работы, от которых вполне можно избавляться.
«Круги на воде» или комбинаторный взрыв спровоцированных изменений

При реализации запросов пользователей и продуктового функционала разработчики порой выбирают самый прямой путь, не анализируя затрат на такую реализацию. Как правило, такой путь оказывается далеко не самым эффективным – он требует изменения большого числа связанного функционала, вызывая тем самым лавинообразный процесс изменений по всему приложению, называемый также «кругами на воде» (ripple effect) или «комбинаторным взрывом». Оба термина обозначают резкий, экспоненциальный (а иногда даже сверхэкспоненциальный) рост затрат при одном входящем изменении системы.
На практике это выглядит так (и каждый разработчик многократно за свою карьеру с этим сталкивался): разработчик вносит изменения в какую-то малую часть системы (одну функцию или класс), меняет её публичный интерфейс, который используется уже несколькими другими клиентскими частями приложения. Вследствие этой зависимости приходится вносить изменения и в эти зависимые части системы. Снова затрагивается уже их публичный интерфейс, который вызывается ещё из большего числа других частей системы… Так «круги на воде» распространяются по всей системе и приводят к совершенно ненужным изменениям уже всей системы, многократно (иногда на порядок и более) увеличивая затраты на изначально маленькое и локальное изменение.
Эти расходы можно радикально и легко сократить изоляцией кода и следованием принципам Low Coupling/High Cohesion (или более понятной и легче применимой на практике вариацией этих принципов, называемой коннасценсцией, представленной в 1996 г. Мейлиром Пейдж-Джонсом в книге с говорящим названием «What Every Programmer Should Know About Object-Oriented Design»). Ещё Эд Йордон и Ларри Константайн в своей книге 1977 г. «Structured Design» доказали, а Кент Бек в 2025 г. уже в своей книге «Чистый дизайн» повторил, что стоимость программного кода примерно равна стоимости сцепленности (Coupling) (ну, или коннасценции, кому проще понимать концепцию в этой трактовке). Пример выше это хорошо показывает. Чем меньше «кругов на воде» – тем меньше работ и ниже стоимость.
Эту же мысль, но другими словами продолжает Р. Мартин в своей книге «Чистая архитектура». Модули, провоцирующие изменения в других модулях, он называет неустойчивыми и обозначает как главную причину всех проблем разработки. В главе 14 он выводит метрики, которые позволяют судить о качестве архитектуры с точки зрения возможности дорабатывать функционал без существенного изменения системы. Например, мы замерили у себя эти метрики в нескольких приложениях и определили, что очень много библиотек находится в так называемой Р. Мартином «зоне боли» — той самой области, которая провоцирует ненужные изменения. Очевидным шагом тут стала переработка таких библиотек для перемещения их в более устойчивые зоны. Рецепты такой переработки Р. Мартин также указывает очень ясно.
Применение этих принципов нам удалось сравнить в двух приложениях: в первом принципы не были применены, и каждое изменение приводило к взрывному увеличению изменений по всему приложению. Разработчики даже хотели отказываться от модульности в пользу монолитной архитектуры, не понимая, что причины проблем кроются вовсе не в модульности.
Во втором приложении, где помимо указанных принципов также было внедрено жёсткое следование принципам Open/Closed (про которые коллеги также уже писали и которое является одним из решений проблемы «кругов на воде») и semver, никакого взрывного роста изменений не происходит — мажорные версии повышаются не чаще раза в полгода. При этом даже повышение мажорных версий не приводит к масштабным изменениям. Наоборот, следование принципам Open/Closed существенно сократило объёмы работ разработчиков.
Следующими крайне масштабными и не очень полезными при этом работами является переписывание успешно работающего функционала без необходимости. Обсудим его подробно в следующем разделе.
Переписывание успешно работающего функционала без необходимости

К таким работам относится и рефакторинг ради рефакторинга.
Несколько цитат великих по этой теме:
«Самонадеянность, управляющая перепроектированием, приведёт к тому же беспорядку, что и прежде», — Р. Мартин.
«Больше беспокоит то, что программист вполне может выполнить ту же задачу двумя или тремя способами: иногда неосознанно, но довольно часто просто ради изменения или же создания элегантной вариации», — А. Р. Браун и У. Э. Сэмпсон.
Такие работы могут не нести никакой продуктовой ценности. Порой они не несут даже технической ценности, ведь при внедрении новой технологии стоит учесть не только факт решения каких-то болей команды, но и влияние на проект — со всеми плюсами и минусами.
Также стоит отметить, что приложения постоянно развиваются, количество функционала в них растёт из года в год. В масштабах больших проектов увеличение продуктового функционала измеряется огромным количеством человекочасов в год. Это значит, что цена переписывания уже созданного функционала растёт экспоненциально. Однажды стоимость переписывания просто превысит все доступные ресурсы – это станет невозможно. Зачем дожидаться этого момента? Почему бы не устранить проблему заранее?
А устраняется она достаточно просто: нужно всего лишь адаптировать имеющуюся кодовую базу под новые условия использования. Инструментов для этого предостаточно: инкапсуляция, адаптеры, покрытие тестами всех различных уровней и назначения, постепенное обновление, совмещенное с продуктовой работой и так далее.
Отличный пример — наша дизайн-система. Она была написана на UIKit несколько лет назад, и на её создание было потрачено множество человекочасов трудовых ресурсов. Однако появилась необходимость использовать её в SwiftUI – создание копии ДС потребовало бы ещё огромное количество времени на её дублирование. Вместо этого мы просто создали для уже имеющейся ДС на UIKit адаптер под SwiftUI (обязательно и в обратную сторону!) – мы потратили на это суммарно, со всеми анализами, проработками, документацией, покрытием тестами, на порядок меньше времени! (Об этом, надеюсь, скоро также одним из наших коллег будет опубликована подробная статья).
Здесь работает правило: «Если код работает — не трогайте! Инкапсулируйте, закройте его интерфейсом — и проблема для пользователей этого кода уйдёт полностью».
Стоит, однако, оговориться, что и из этого правила есть исключения. Как уже было отмечено выше, если глубокий анализ показал, что переход на новые технологии даст существенный продуктовый эффект, то стоит эти технологии внедрять. Но и здесь нужно быть очень аккуратным и внимательным и понимать, как преимущества новой технологии проявятся в этом конкретном проекте (и проявятся ли вообще).
Более подробно про рефакторинг, его уместность и возможность сокращать область его применения я пишу в своем блоге. Ссылки на блок в Сетке и Максе смотрите в моем профиле.
Повторения, дублирования функционала различными направлениями бизнеса

Такая ситуация довольно часто встречается в компаниях, организованных по принципу продуктовых колодцев.
Как в этой ситуации мы оказываемся? Очень просто! Две параллельно работающие над разными продуктами команды сталкиваются с необходимостью добавить один и тот же функционал и решают её независимо друг от друга — разными или похожими способами. В итоге в компании, а иногда и даже в одном приложении, появляются две реализации одного и того же функционала. А всё из-за отсутствия постоянных детальных коммуникаций между командами.
Проблема может возникать даже тогда, когда разработчики знают о существовании функционала, но функционал слишком конкретен, заточен на детали реализации конкретной команды. В этом случае для его переиспользования он должен быть абстрагирован от деталей реализации конкретной команды. Но сделать это могут далеко не все разработчики, особенно с чужим функционалом. Абстрагирование – не самая лёгкая часть работы разработчика. В таком случае разработчики снова выбирают самый прямой вариант: просто продублировать функционал для себя.
Для борьбы с бесполезной растратой ресурсов по этому направлению мы провели следующие мероприятия:
— объединили разработчиков из разных направлений бизнеса в Центры по компетенциям, внедрили обязательные ежеквартальные митапы, на которых делимся знаниями и выполненными работами, обучаем сотрудников абстрагированию и переиспользованию функционала;
— на уровне ЦК запретили переписывание и дублирование функционала.
Тут тоже стоит оговориться, что довольно часто дублирование / копирование оправдано (и может быть существенно дешевле, чем борьба с ним). В частности, популярный ныне микросервисный подход как раз и доводит эту мысль до абсолюта – в микросервисах считается благом сокращать связи за счёт дублирования. Мы же стараемся не впадать ни в одну, ни в другую крайности и принимать решение сознательно — сравнивая затраты на оба варианта.
Потери на поддержке функционала мой коллега подробно рассмотрел в статье.
Потери на коммуникациях мы косвенно уже затронули; подробно разбирать их не буду, так как эти потери существенно меньше рассмотренных выше.
Перейдём к потерям из-за отсутствия понимания текущего состояния процессов и технологий.
Потери из-за отсутствия понимания текущего состояния процессов и технологий

Такая ситуация возникает, когда архитекторы (или старшие разработчики) видят боль своих коллег по коду, чувствуют её сами, но вместо того чтобы найти конкретные причины этой боли и устранить её точечно, пытаются слепо копировать популярные решения и технологии: переписать всё с одного языка на другой, а с него на третий (и заглядываться на четвёртый), просто поменять архитектуру на более «трендовую и модную» и так далее.
Проблема здесь в том, что нет понимания текущего запроса, не проводится подробный анализ: что будет решено, а какие особенности будут привнесены взамен. Одна и та же технология в чём-то хороша, а в чём-то – ужасна. Именно поэтому их такое огромное разнообразие.
В итоге непонимание текущей ситуации приводит к бездумным изменениям, которые, в свою очередь, провоцируют все указанные выше проблемы.
Как же управлять работами так, чтобы у разработчиков не возникало соблазна заниматься бесполезными работами?
Перейдём к контролю эволюции.
Контроль эволюции

В своей прошлой статье я рассказал, что сейчас набирает популярность тренд, называемый эволюционной архитектурой.
Его смысл в том, что невозможно раз и навсегда подобрать архитектуру приложения, так как с течением времени появляются новые требования, новые технологии и т. д. Поэтому вместо подбора подходящей универсальной архитектуры предлагается делать архитектуру эволюционной, то есть легко и дёшево изменяемой под изменяющиеся запросы времени.
В той статье я попытался сравнить рекомендуемые подходы по реализации эволюционной архитектуры с принципами эволюции Дарвина и обнаружил сильное сходство. Я разделил механизмы реализации эволюционной архитектуры по трём необходимым и достаточным условиям возникновения эволюции Дарвина и понял, что, добавляя или удаляя эти условия, легко можно контролировать эволюцию нашего программного обеспечения.
Этот же инструмент предлагается использовать и для контроля полезных / вредных изменений. Для полезных изменений мы должны создавать условия, которые способствуют эволюции, а для вредных – наоборот, условия, замедляющие эволюцию.
Например, упомянутое выше правило Open/Closed радикально препятствует случайным непродуманным изменениям, при этом способствует инкапсуляции изменений, делая их точечными, малыми, дешёвыми, а потому – эволюционными, а не революционными.
Именно эволюционность нашей мобильной архитектуры, как мне кажется, и позволила нам так быстро взлететь в топ и продолжать там удерживаться. Критерии оценки и весовые коэффициенты Markswebb дополняются, расширяются, меняются каждые два года. Иногда существенно. Без возможности адаптации в реальном времени оставаться в топе рейтинга становится сильно сложнее. От момента публикации критериев на очередное состязание до финальной оценки приложения проходит всего несколько месяцев – в таких условиях обязательной становится жёсткая фокусировка на требуемую функциональность и возможность реализовывать её всего за несколько спринтов, не растрачивая ресурсы попусту.
Выводы
Закончить статью хотелось бы следующим выводом.
По моему личному опыту (за более чем 15 лет работы) 80 % задач, в которых тонут разработчики, – абсолютно бесполезны и не нужны ни для клиентов и бизнеса, ни для проекта с технической точки зрения. Из-за этого эффективность доставки ценности многократно (иногда на порядки) снижается, а бизнес значительно переплачивает за такую работу. При этом стоимость во времени для бизнеса только растёт, причём экспоненциально.
В связи с этим призываю руководителей и / или архитекторов тщательно анализировать свои проекты на наличие бесполезных и даже вредных активностей, подобных тем (но не ограничиваясь ими), что я привёл в этой статье, – и нещадно от таких активностей избавляться, даже если разработчики всячески убеждают, что без этого всё сломается и рассыпется. Наш пример показывает, что не только не рассыпется, но ещё и лучше станет на порядок и выведет проект на максимум отношения доходов к затратам. Избавление стоит производить, контролируя эволюцию вашего проекта.
Разработчиков же призываю не забывать про ценность и результат. Процесс разработки, несомненно, приносит радость и удовлетворение. Также несомненным плюсом для вас будет ориентация на результат и владение практиками, не допускающими ненужных работ.
ПС: в профиле у меня указаны ссылки на мои каналы, где многие из перечисленных тем будут обсуждаться детальнее, и я буду отвечать на интересующие вас вопросы.
MonkAlex
Когда менеджер приходит с задачкой, не вписывающейся в главную цель, разработчик отказывается делать работу?
Отдельно отмечу, что про эволюционную архитектуру звучит очень странно, надо видимо читать отдельно. Представляется себе сразу дом из костылей, эволюция!