Как минимальный криптоконвертер вырос в полноценный проект: разбор этапов итеративной разработки, ошибок и идей для развития pet-проектов.
021 открытий230 показов
Иногда pet-проект — это не просто «поиграться с кодом». В этой статье расскажу, как с нуля собрал криптоконвертер, а потом на его основе начал развивать платформу для экспериментов с криптоAPI, автоматизацией и ботами.
Итеративная разработка: почему начал с малого
Всё началось с минимального прототипа. Это был криптоконвертер — простой, но функциональный. Постепенно я добавлял новые фичи: что-то по запросам из комьюнити, что-то — просто потому что хотелось попробовать. Параллельно шёл рефакторинг: чем сложнее становился проект, тем больше приходилось возвращаться к архитектуре.
Почему именно конвертер? Это идеальный “hello world” для криптомира:
- нужно подключить внешний API (в моем случае — CoinGecko)
- есть базовая бизнес-логика
- есть простое взаимодействие с пользователем
Это не только полезный юзкейс, но и отличная точка входа для чего-то большего.
Архитектурные решения v1.0
1. Работа с CoinGecko API
Первая реализация была прямолинейной:
def get_rate(crypto: str, currency: str) -> float: response = requests.get( "https://api.coingecko.com/api/v3/simple/price", params={"ids": crypto, "vs_currencies": currency} ) return response.json()[crypto][currency]
Проблема: Каждый запрос — это сетевой вызов. При 1000 пользователях мы превысим лимиты API.
Решение в v1.1: Добавил кеширование через @lru_cache с TTL=5 минут.
2. Упрощённая машина состояний
Вместо полноценного FSM использовали словарь:
user_states = {} # {user_id: {"step": "select_currency", "data": {...}}}
Почему не FSM? Для v1.0 важна была скорость реализации. Aiogram 3.x предоставляет отличный FSM, но его настройка требует дополнительного времени.
3. Валидация ввода
Я решил использовать гибридный подход:
- Кнопки для выбора криптовалюты
- Ручной ввод суммы с regex-валидацией:
@dp.message(F.text.regexp(r'^d+(.d{1,8})?$')) async def handle_amount(message: Message): ...
Боль боли: на какие грабли я наступил
Расскажу о проблемах, с которыми столкнулся. Спойлер: они были.
Ненадёжность публичных API
CoinGecko иногда отвечает 10+ секунд или возвращает 502. Решение:
try: async with async_timeout.timeout(5): response = await session.get(url) if response.status != 200: raise APIError(f"Status {response.status}") except (asyncio.TimeoutError, aiohttp.ClientError) as e: logger.error(f"API error: {e}") await notify_admins("API проблем")
Особенности пользовательского ввода
Даже с кнопками пользователи умудрялись:
- Присылать “BTC” вместо “bitcoin”
- Использовать запятые вместо точек
- Вводить отрицательные суммы
Пришлось добавить нормализацию:
amount = amount.replace(",", ".").strip() if float(amount) <= 0: await message.answer("Число должно быть положительным")
Состояния при перезапусках
При деплое нового кода все сессии сбрасывались. Временное решение — ручное сохранение в JSON, в планах — переход на Redis.
Наступив на все грабли, я продумал дальнейший roadmap. Среди приоритетов:
- Система алертов
- Графики через matplotlib
- Portfolio tracker
Код и рефакторинг
Проект выложен на GitFlic. Самые спорные места кода:
- Отсутствие DI — сервисы передаются через глобальные переменные
- Смесь sync и async вызовов
- Тесты покрывают только core-логику
Приветствуются пулл-реквесты, особенно по:
- Улучшению обработки ошибок
- Оптимизации запросов к API
- Рефакторингу хендлеров
Вы можете проголосовать за дальнейшую ветвь развития в Опроснике на моем ТГ-канале.