Масштабное проектирование: Решение проблемы некоммутативных преобразований в breakshaft
Содержание
- Постановка проблемы
- Анализ текущей ситуации
- Варианты решений
- Сравнительная таблица
- Рекомендации
1. Постановка проблемы
1.1. Что такое некоммутативность в breakshaft?
Некоммутативное преобразование — ситуация, когда существует несколько путей преобразования типов, дающих разные результаты.
Пример:
1.2. Почему это проблема?
| Аспект |
Проблема |
| Детерминизм |
Один и тот же код может давать разные результаты |
| Отладка |
Сложно понять, какой путь был выбран |
| Предсказуемость |
Поведение зависит от внутреннего порядка обхода графа |
| Тестирование |
Тесты могут проходить/падать недетерминированно |
1.3. Где возникает в коде?
Критическое место — filter_exploded_callgraph_branch():
- Использует эвристики для выбора пути
- Порядок обхода не гарантирован
- При
force_commutative=True выбрасывает ошибку если >1 пути
2. Анализ текущей ситуации
2.1. Текущий алгоритм выбора пути
2.2. Проблемы текущей реализации
| Проблема |
Описание |
Влияние |
| P1. Недетерминированная сортировка |
universal_qualname() не гарантирует порядок |
Разные результаты на разных машинах |
| P2. Эвристики не семантические |
Выбор по метрикам графа, не по логике |
Может выбрать "неправильный" путь |
| P3. Комбинаторный взрыв |
explode_callgraph_branches() генерирует все варианты |
O(n!) сложность |
| P4. Нет кэширования |
Граф пересчитывается каждый раз |
Усугубляет P3 |
| P5. Нет явного приоритета |
Все инжекторы равны |
Невозможно указать "предпочтительный" путь |
2.3. Статистика (из тестов)
3. Варианты решений
Вариант 1: Явные приоритеты инжекторов
Описание
Добавить параметр priority к mark_injector(). При выборе пути предпочитать инжекторы с высшим приоритетом.
Реализация
Сильные стороны
| + |
Описание |
| Контроль |
Разработчик явно указывает предпочтения |
| Детерминизм |
Приоритеты дают однозначный выбор |
| Гибкость |
Можно менять приоритеты без изменения кода |
| Обратная совместимость |
priority=0 по умолчанию не ломает существующий код |
Слабые стороны
| - |
Описание |
| Сложность API |
Новый параметр для изучения |
| Конфликты приоритетов |
Одинаковые приоритеты → снова недетерминизм |
| Не решает комбинаторный взрыв |
Всё ещё генерируются все варианты |
| Субъективность |
Приоритеты могут быть произвольными |
Оценка сложности
- Код: ~50 строк изменений
- Тесты: ~10 новых тестов
- Риск: Низкий
Вариант 2: Именованные пути (Named Paths)
Описание
Разработчик явно именовывает пути преобразования и выбирает их по имени.
Реализация
Сильные стороны
| + |
Описание |
| Полный контроль |
Разработчик всегда выбирает путь |
| Читаемость |
Код явно показывает какой путь используется |
| Документированность |
Имена путей служат документацией |
| Тестируемость |
Можно тестировать разные пути явно |
Слабые стороны
| - |
Описание |
| Бойлерплейт |
Нужно именовать каждый путь |
| Сложность |
Управление именами в больших проектах |
| Конфликты имён |
Нужна проверка уникальности |
| Не решает комбинаторный взрыв |
Генерация всех вариантов остаётся |
Оценка сложности
- Код: ~150 строк изменений
- Тесты: ~15 новых тестов
- Риск: Средний
Вариант 3: Стратегии выбора пути (Path Selection Strategies)
Описание
Встроенные стратегии выбора пути с возможностью расширения.
Реализация
Сильные стороны
| + |
Описание |
| Гибкость |
Разные стратегии для разных случаев |
| Расширяемость |
Пользовательские стратегии |
| Явность |
Стратегия видна в коде вызова |
| Переиспользование |
Стратегии можно переиспользовать |
Слабые стороны
| - |
Описание |
| Сложность API |
5+ стратегий для изучения |
| Не решает комбинаторный взрыв |
Стратегия применяется после генерации |
| Производительность |
Некоторые стратегии дорогие |
Оценка сложности
- Код: ~200 строк изменений
- Тесты: ~20 новых тестов
- Риск: Средний
Вариант 4: Ограничение глубины графа (Depth Limiting)
Описание
Ограничить максимальную глубину/сложность графа преобразований.
Реализация
Сильные стороны
| + |
Описание |
| Защита от взрыва |
Гарантированная верхняя граница сложности |
| Производительность |
Предсказуемое время выполнения |
| Простота |
Один параметр для настройки |
Слабые стороны
| - |
Описание |
| Ограничения |
Может отсечь валидные пути |
| Не детерминизм |
Не решает проблему выбора пути |
| Настройка |
Нужно подбирать значения |
Оценка сложности
- Код: ~80 строк изменений
- Тесты: ~8 новых тестов
- Риск: Низкий
Вариант 5: Кэширование графов (Graph Caching)
Описание
Кэшировать построенные графы преобразований для повторного использования.
Реализация
Сильные стороны
| + |
Описание |
| Производительность |
Повторные вызовы мгновенные |
| Масштабируемость |
Работает с большим числом инжекторов |
| Прозрачность |
Кэш прозрачен для пользователя |
Слабые стороны
| - |
Описание |
| Память |
Кэш потребляет память |
| Инвалидация |
Сложность при изменении инжекторов |
| Не решает выбор пути |
Кэширует выбранный путь, но не детерминирует выбор |
Оценка сложности
- Код: ~250 строк изменений
- Тесты: ~25 новых тестов
- Риск: Высокий (состояние, гонки)
Вариант 6: Статический анализ графа (Static Graph Analysis)
Описание
Анализировать граф на этапе регистрации инжекторов, обнаруживать проблемы заранее.
Реализация
Сильные стороны
| + |
Описание |
| Раннее обнаружение |
Ошибки на этапе регистрации |
| Документированность |
Явное разрешение конфликтов |
| Безопасность |
Невозможно создать неоднозначность |
Слабые стороны
| - |
Описание |
| Сложность |
Анализ графа дорог |
| Жёсткость |
Может быть слишком ограничительно |
| Не решает комбинаторный взрыв |
Анализ добавляет overhead |
Оценка сложности
- Код: ~300 строк изменений
- Тесты: ~30 новых тестов
- Риск: Высокий
Вариант 7: Версионирование путей (Path Versioning)
Описание
Каждый путь имеет версию, можно выбирать конкретную версию.
Реализация
Сильные стороны
| + |
Описание |
| Эволюция |
Плавный переход между версиями |
| Совместимость |
Старый код продолжает работать |
| Контроль |
Явный выбор версии |
Слабые стороны
| - |
Описание |
| Сложность |
Управление версиями |
| Бойлерплейт |
Версии для каждого пути |
| Не решает комбинаторный взрыв |
Все версии генерируются |
Оценка сложности
- Код: ~200 строк изменений
- Тесты: ~20 новых тестов
- Риск: Средний
Вариант 8: Комбинированный подход (Hybrid Solution)
Описание
Комбинация нескольких подходов для максимального эффекта.
Реализация
Сильные стороны
| + |
Описание |
| Максимальная гибкость |
Все инструменты доступны |
| Масштабируемость |
Работает с большими графами |
| Контроль |
Полный контроль над поведением |
Слабые стороны
| - |
Описание |
| Сложность |
Много параметров для настройки |
| Обучение |
Крутая кривая обучения |
| Риск ошибок |
Неправильная конфигурация |
Оценка сложности
- Код: ~500 строк изменений
- Тесты: ~50 новых тестов
- Риск: Высокий
Вариант 9: Декларативное описание графа (Declarative Graph)
Описание
Полностью декларативное описание путей преобразования вместо автоматического вывода.
Реализация
Сильные стороны
| + |
Описание |
| Полный контроль |
Явное описание всех путей |
| Детерминизм |
Никакой неявной логики |
| Документированность |
Граф виден в коде |
| Нет комбинаторного взрыва |
Только явные пути |
Слабые стороны
| - |
Описание |
| Бойлерплейт |
Много кода для описания |
| Потеря автоматизма |
Нет автоматического вывода путей |
| Сложность поддержки |
Изменение графа требует правки описания |
Оценка сложности
- Код: ~400 строк изменений
- Тесты: ~40 новых тестов
- Риск: Высокий (меняет парадигму)
Вариант 10: Машинное обучение для выбора пути (ML-Based Selection)
Описание
Использовать ML для предсказания "лучшего" пути на основе истории использования.
Реализация
Сильные стороны
| + |
Описание |
| Адаптивность |
Учится на использовании |
| Оптимизация |
Выбирает эффективные пути |
| Автоматизм |
Не требует ручной настройки |
Слабые стороны
| - |
Описание |
| Сложность |
ML модель + обучение |
| Непредсказуемость |
ML может выбрать неожиданно |
| Зависимость от данных |
Нужна история для обучения |
| Overhead |
Предсказание модели |
Оценка сложности
- Код: ~600 строк изменений
- Тесты: ~60 новых тестов
- Риск: Очень высокий
4. Сравнительная таблица
| Вариант |
Детерминизм |
Производительность |
Сложность |
Обратная совместимость |
Риск |
| 1. Приоритеты |
⭐⭐⭐⭐ |
⭐⭐⭐ |
⭐ |
⭐⭐⭐⭐⭐ |
Низкий |
| 2. Именованные пути |
⭐⭐⭐⭐⭐ |
⭐⭐⭐ |
⭐⭐ |
⭐⭐⭐⭐ |
Средний |
| 3. Стратегии |
⭐⭐⭐⭐ |
⭐⭐⭐ |
⭐⭐⭐ |
⭐⭐⭐⭐ |
Средний |
| 4. Ограничение глубины |
⭐⭐ |
⭐⭐⭐⭐⭐ |
⭐ |
⭐⭐⭐⭐⭐ |
Низкий |
| 5. Кэширование |
⭐⭐⭐ |
⭐⭐⭐⭐⭐ |
⭐⭐⭐ |
⭐⭐⭐⭐⭐ |
Высокий |
| 6. Статический анализ |
⭐⭐⭐⭐⭐ |
⭐⭐ |
⭐⭐⭐⭐ |
⭐⭐⭐ |
Высокий |
| 7. Версионирование |
⭐⭐⭐⭐ |
⭐⭐⭐ |
⭐⭐⭐ |
⭐⭐⭐⭐ |
Средний |
| 8. Комбинированный |
⭐⭐⭐⭐⭐ |
⭐⭐⭐⭐ |
⭐⭐⭐⭐⭐ |
⭐⭐⭐ |
Высокий |
| 9. Декларативный |
⭐⭐⭐⭐⭐ |
⭐⭐⭐⭐⭐ |
⭐⭐⭐⭐ |
⭐⭐ |
Высокий |
| 10. ML |
⭐⭐ |
⭐⭐ |
⭐⭐⭐⭐⭐ |
⭐⭐⭐⭐⭐ |
Очень высокий |
Легенда:
- ⭐⭐⭐⭐⭐ — Отлично
- ⭐⭐⭐⭐ — Хорошо
- ⭐⭐⭐ — Удовлетворительно
- ⭐⭐ — Плохо
- ⭐ — Очень плохо
5. Рекомендации
5.1. Краткосрочные решения (быстрая победа)
Вариант 1 + Вариант 4: Приоритеты + Ограничение глубины
Преимущества:
- ~130 строк кода
- Низкий риск
- Обратная совместимость
- Решает 80% проблем
5.2. Среднесрочные решения (баланс)
Вариант 3 + Вариант 5: Стратегии + Кэширование
Преимущества:
- Хорошая производительность
- Гибкость для пользователей
- Решает проблему комбинаторного взрыва
5.3. Долгосрочные решения (полное решение)
Вариант 8 (Комбинированный) с элементами Варианта 9
Преимущества:
- Полное решение проблемы
- Масштабируемость
- Гибкость
5.4. Дорожная карта
6. Заключение
6.1. Выводы
- Нет серебряной пули — каждый вариант имеет компромиссы
- Комбинированный подход даёт лучший результат
- Начинать с простого — приоритеты + ограничения
- Итеративное улучшение — добавлять функции постепенно
6.2. Риски
| Риск |
Вероятность |
Влияние |
Митигация |
| Ломает обратную совместимость |
Низкая |
Высокое |
Поэтапное внедрение |
| Усложнение API |
Средняя |
Среднее |
Хорошая документация |
| Производительность |
Низкая |
Высокое |
Бенчмарки на каждом этапе |
| Комбинаторный взрыв |
Средняя |
Высокое |
Ограничения + кэш |
6.3. Следующие шаги
- Выбрать подход для Фазы 1
- Создать PR с приоритетами и ограничениями
- Собрать фидбэк от пользователей
- Итеративно улучшать
Документ создан для breakshaft v0.1.6
Дата: 2026-03-28