Представьте: вы создаёте милое маленькое приложение TODO. «Это займёт выходные», — говорите вы себе. Перенесёмся на шесть месяцев вперёд, и вот вы уже отлаживаете состояния гонки в своей пользовательской реализации WebSocket, а схема вашей базы данных напоминает картину Джексона Поллока. Бывали там? Давайте поговорим о стратегическом управлении сложностью.
Почему вашу архитектуру не волнует ваша кошка
Большинство приложений начинаются как невинные проекты с нуля. Подобно чрезмерно усердным садовникам, мы продолжаем добавлять функции, пока наш код не станет напоминать растительность тропических лесов Амазонки. Ключ не в том, чтобы избежать сложности, а в том, чтобы управлять ею. Вот мой проверенный в боях подход:
Принцип луковицы
Выкладывайте слои вашего приложения, как повар лазаньи с обсессивно-компульсивным расстройством:
Начните с базового приложения Flask, которое перерастает свою одежду:
# Версия 1.0 — «Что может пойти не так?»
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "Теперь я по сути веб-сайт!"
Когда функции размножаются как кролики, применяйте правило трёх:
- 1 реализация: держите её простой;
- 2 реализации: создайте вспомогательную функцию;
- 3+ реализации: разработайте модуль.
Управление зависимостями для хронически перегруженных
Современные приложения похожи на невротичных поваров — им нужно 57 ингредиентов, чтобы приготовить тост. Вот как я избегаю спагетти из зависимостей:
# Подход «Я взрослый»
from dependency_injector import containers, providers
class ServiceContainer(containers.DeclarativeContainer):
база данных = providers.Singleton(
DatabaseClient,
host=os.getenv('DB_HOST')
)
кэш = providers.Singleton(
RedisCache,
ttl=3600
)
container = ServiceContainer()
user_service = container.user_service()
Совет: если ваш граф зависимостей похож на карту метро, вы либо создали следующий Kubernetes, либо вам нужна профессиональная помощь.
Искусство стратегического переусложнения
Иногда вам нужно добавить сложность, чтобы уменьшить сложность. Мои любимые силовые приёмы:
- Парадокс прогнозирования Постройте систему мониторинга до того, как она вам понадобится:
# Потому что пользователи лгут об ошибках
import logging
logger = logging.getLogger('app')
def рискованная операция():
try:
# Код, который может укусить
except Exception as e:
logger.error(f"Сбой из-за {e}, который думает, что это забавно")
metrics.counter('ошибки', теги=['тип:рискованный'])
raise
- Флаги функций для победы Разверните функции, как агент ЦРУ:
from unleash import UnleashClient
unleash = UnleashClient()
if unleash.is_enabled("секретная функция"):
включить режим телепатии()
else:
print("Ваше воображение — это предел!")
Когда стоит принять безумие
Сложность становится вашим союзником, когда:
- создаёте системы проверки, которые растут вместе с требованиями;
- работаете с несколькими источниками данных, которые не могут согласовать форматы;
- создаёте точки расширения для неизвестных будущих потребностей:
# Фабричный шаблон «Я видел вещи»
class Фабрика Загрузчиков Данных:
@classmethod
def get_loader(cls, source_type):
return {
'csv': CsvLoader,
'api': ApiLoader,
'экстрасенс': PrecogLoader
}[source_type]()
Помните: хорошая сложность подобна чесноку — она должна улучшать блюдо, а не делать людей неуязвимыми для вампиров на несколько дней.
Танец обслуживания
Каждые полгода проводите комплекс ча-ча-сложности:
- Проверяйте межмодульные зависимости;
- Удаляйте мёртвые ветки кода (RIP, экспериментальная интеграция блокчейна);
- Обновляйте показатели соотношения WTF в минуту. Как говорил мой любимый профессор: «Сложность неизбежна, катастрофический отказ необязателен». А теперь извините меня, мне нужно объяснить продакшену, почему он не должен падать во вторник.