← Назад к вопросам

Как относишься к Legacy Code?

1.0 Junior🔥 151 комментариев
#Soft Skills и карьера

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Отношение к Legacy Code: от вызова к возможности

Как iOS-разработчик с более чем 10-летним опытом, я отношусь к Legacy Code (унаследованному коду) не как к "техническому долгу", который нужно избегать, а как к ценному активу и профессиональному вызову. Это неизбежная часть жизненного цикла любого долгоживущего приложения, особенно в экосистеме Apple, где языки, фреймворки и архитектурные подходы претерпели радикальные изменения (от Objective-C и MVC на UIKit до Swift и SwiftUI с чистой архитектурой).

Почему Legacy Code — это вызов и возможность

  1. Исторический контекст и бизнес-логика: Зачастую Legacy Code — это единственный источник истины о сложной бизнес-логике, которая развивалась годами. Его изучение позволяет понять, почему система работает именно так, а не искать "быстрое" и потенциально разрушительное решение.

  2. Архитектурный челлендж: Работа с таким кодом требует высочайшей дисциплины и применения лучших практик рефакторинга. Это отличный полигон для оттачивания навыков в написании тестов (Unit, Integration), внедрении зависимостей (Dependency Injection) и постепенном внедрении современных архитектур (MVVM, Clean Architecture) в устаревшую кодовую базу.

  3. Стратегическое мышление: Невозможно всё переписать с нуля (знаменитый "Second System Effect" часто ведёт к провалу). Ключ — в стратегической, инкрементальной модернизации. Например, можно начать с изоляции самого проблемного модуля, покрытия его тестами и последующего рефакторинга.

Мой практический подход к работе с Legacy Code

Моя стратегия строится на принципе "Не сломай то, что работает" и включает несколько обязательных этапов:

  1. Анализ и составление карты: Прежде чем что-то трогать, я трачу время на изучение кодовой базы. Я выявляю ключевые модули, точки входа, зависимости (часто скрытые, в виде синглтонов или глобальных объектов). Инструменты в помощь: Find Call Hierarchy в Xcode, поиск по всему проекту, статические анализаторы.

  2. Обеспечение безопасности тестами: Первое, что я делаю с фрагментом Legacy Code, который нужно изменить, — пытаюсь написать для него тесты. Если код слишком связан, я начинаю с интеграционных тестов на уровне UI (с помощью XCTest), а затем, по мере рефакторинга, спускаюсь к модульным.

    // Пример: Предположим, у нас есть старый непроверяемый ViewController.
    // Шаг 1 - Пишем интеграционный тест, чтобы зафиксировать текущее поведение.
    class LegacyProfileViewControllerTests: XCTestCase {
        func testViewControllerLoadsUserDataOnAppear() {
            // Given
            let storyboard = UIStoryboard(name: "Main", bundle: nil)
            let sut = storyboard.instantiateViewController(
                identifier: "LegacyProfileVC"
            ) as! LegacyProfileViewController
            // Неидеально, но это начало. Мы фиксируем поведение.
            
            // When
            sut.loadViewIfNeeded()
            
            // Then
            XCTAssertNotNil(sut.nameLabel.text)
        }
    }
    
  3. Малые, изолированные изменения (Baby Steps): Я вношу микроскопические, атомарные изменения, постоянно запуская имеющиеся тесты. Типичные первые шаги:

    *   **Выделение методов** для уменьшения дублирования.
    *   **Замена "магических чисел" и строк** на константы или enum.
    *   **Инкапсуляция синглтонов** за протоколами для внедрения зависимостей.

```swift
// Было: Жёсткая зависимость от синглтона.
class OldService {
    func fetchData() {
        let data = NetworkManager.shared.get(from: "https://api.example.com/data")
        // ... обработка
    }
}

// Стало: Зависимость инкапсулирована за протоколом и внедряется извне.
protocol Networking {
    func get(from url: String) -> Data
}

class RefactoredService {
    private let network: Networking
    
    init(network: Networking) { // Dependency Injection
        self.network = network
    }
    
    func fetchData() {
        let data = network.get(from: APIEndpoint.data.urlString)
        // ... обработка
    }
}

// Теперь код можно тестировать с моковым Networking.
```

4. Стратегический рефакторинг "сэндвичем": При необходимости замены целого слоя (например, перехода с UIViewController на SwiftUI.View), я применяю подход "Обтекания" (Strangler Fig Pattern). Новый функционал пишется на новой технологии, а старый постепенно маршрутизируется в новый интерфейс, пока полностью не будет вытеснен.

Заключение

Для меня Legacy Code — это не ругательство, а реальность коммерческой разработки. Уважение к коду, написанному до тебя, понимание бизнес-контекста, системный подход к модернизации и железная дисциплина тестирования — вот что отличает зрелого инженера. Умение эффективно работать с Legacy Code — критически важный навык, который позволяет не просто поддерживать, а постоянно и безопасно улучшать продукт, добавляя ценность для пользователей и бизнеса, не останавливаясь в развитии.