Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Об одном неудачном проекте и его уроках
Один из самых ярких неудачных проектов в моей практике связан с разработкой сложного многомодульного финансового приложения в середине 2010-х годов. Задача казалась классической: создать приложение для управления личными финансами с прогнозированием, синхронизацией данных между устройствами и интеграцией с несколькими банковскими API (честно говоря, тогда это было модно и казалось перспективным).
Корень проблем: архитектура и процесс
Основная ошибка была совершена на самом старте проекта — в выборе архитектуры и организации процесса разработки.
- Чрезмерная сложность архитектуры. Мы решили использовать тогда еще новую и не до конца освоенную парадигму VIPER (View-Interactor-Presenter-Entity-Router), при этом не имея глубокого опыта с ней в команде. Мотивация была «правильной»: разделить ответственность, обеспечить тестируемость. Но на практике это вылилось в:
// Пример того, как это усложняло даже простые операции class BudgetInteractor { weak var presenter: BudgetPresenter? var entity: BudgetEntity? func fetchData() { // Запрос к сервису -> преобразование в Entity -> передача Presenter // Все это для одной модели } } // Один модуль (скажем, экран бюджета) мог включать 8-10 файлов.
Каждый модуль превращался в лабиринт классов. **Сложность поддержки** росла экспоненциально.
-
Отсутствие единого состояния (State Management). Мы не использовали централизованный подход к управлению состоянием (типа Redux или даже простого собственного Store). Данные «жили» в разных Interactor и Entity, синхронизация между модулями стала адом. Баги, где данные на одном экране не соответствовали данным на другом, были постоянными.
-
Плохая коммуникация и отсутствие прототипа. Дизайнеры и менеджеры продукта работали с нами удаленно. Мы начали писать код без полноценного, отработанного и согласованного UI/UX прототипа. Это привело к постоянным изменениям требований на этапе, когда модули уже были «закодированы» по изначальному ТЗ. Рефакторинг сложной VIPER-архитектуры под новые требования был крайне трудоемким.
Технические последствия
- Низкая скорость разработки. Добавление новой функциональности занимало недели вместо дней.
- Проблемы с тестированием. Хотя VIPER задумывался для тестируемости, из-за сильной связности некоторых модулей и сложной сети зависимостей unit-тесты писать было тяжело, а многие стали бесполезными после рефакторинга.
- Ужасная синхронизация данных. Реализация offline-режима и синхронизации между устройствами (честный CoreData + CloudKit) стала кошмаром из-за разрозненной архитектуры данных.
Критическая точка и решение
Проект двигался медленно, баги накапливались, деморализация команды росла. После 6 месяцев разработки мы имели лишь половину запланированного функционала, и его качество было низким.
Решение, которое, хоть и было болезненным, спасило проект:
-
Полный архитектурный рефакторинг. Мы остановили разработку новых функций на месяц. Провели анализ, выбрали более простую и знакомую команде архитектуру — MVVM с координаторами (Coordinators) для навигации и централизованным State Container (простой собственный класс-хранилище на основе наблюдаемых свойств).
// Новый подход был значительно чище class BudgetViewModel { var budget: Observable<Budget> func updateBudget() { // Прямое обращение к сервису и обновление центрального State Container appState.budget = newBudget } } -
Строгий процесс и прототип. Мы установили правило: никакой серьезной разработки без утвержденного и статического прототипа в Figma (тогда использовали Sketch). Все изменения требований проходили через этап обновления прототипа перед любыми изменениями в коде.
-
Приоритет на данные. Первым делом после рефакторинга мы построили надежный, единый Data Layer с четким Repository pattern и Unified Model, который использовался всем приложением.
Выученные уроки
- Архитектура должна служить команде, а не принципам. Не выбирайте сложную архитектуру ради ее «красоты» или моды, если она не соответствует опыту команды и реальным требованиям проекта. MVVM, MVC или даже хорошо структурированный UIKit могут быть лучше чрезмерно декомпозированного VIPER для многих проектов.
- Единое состояние — основа сложных приложений. Для приложений с множеством данных и экранов централизованное управление состоянием (будь то Redux, SwiftUI’s State, или собственный Store) — это не опция, это необходимость.
- Прототип — это часть технической спецификации. Без финализированного дизайна и UX нельзя начинать писать сложную логику. Это гарантия бесконечных изменений и рефакторингов.
- Рефакторинг — это инструмент, а не признак неудачи. Иногда нужно остановиться, признать, что текущий путь ведет в тупик, и провести коренной рефакторинг. Это дорого в краткосроке, но спасает проект в долгосроке.
Этот опыт, хотя и был тяжелым, стал одним из самых ценных в моей карьере. Он научил меня оценивать не только «как сделать», но и «стоит ли делать именно так», учитывая человеческие и процессные факторы, а не только чисто технические.