Глава 13: Промпты для разработки ПО
Принцип
Промпты для программирования должны учитывать контекст проекта, существующие паттерны и требования к качеству кода.
Архитектура
Выбор паттерна
Пример 1: Strategy
"КОНТЕКСТ:
Система расчёта скидок в e-commerce.
Типы скидок:
- Процент от суммы
- Фиксированная сумма
- Бесплатная доставка
- Комбинированные
ТРЕБОВАНИЯ:
- Легко добавлять новые типы скидок
- Скидки могут комбинироваться
- Тестируемость
ЗАДАЧА:
Реализуй паттерн Strategy.
\`\`\`python
from abc import ABC, abstractmethod
from decimal import Decimal
from dataclasses import dataclass
@dataclass
class Order:
subtotal: Decimal
shipping: Decimal
class DiscountStrategy(ABC):
@abstractmethod
def apply(self, order: Order) -\> Decimal:
'''Возвращает сумму скидки.'''
pass
\# Реализации стратегий
\# Контекст применения
\`\`\`"
Пример 2: Repository
"КОНТЕКСТ:
- ORM: SQLAlchemy async
- БД: PostgreSQL
- Модель: User(id, email, hashed\_password, created\_at, is\_active)
ТРЕБОВАНИЯ:
- Абстракция над ORM
- Типизация
- Dependency injection ready
ЗАДАЧА:
Repository для User.
\`\`\`python
from typing import Protocol, Optional
from sqlalchemy.ext.asyncio import AsyncSession
class UserRepository(Protocol):
async def get\_by\_id(self, user\_id: int) -\> Optional$User$: ...
async def get\_by\_email(self, email: str) -\> Optional$User$: ...
async def create(self, user: UserCreate) -\> User: ...
async def update(self, user\_id: int, data: UserUpdate) -\> Optional$User$: ...
async def delete(self, user\_id: int) -\> bool: ...
class SQLAlchemyUserRepository:
def \_\_init\_\_(self, session: AsyncSession):
self.\_session = session
\# Реализация методов
\`\`\`"
Пример 3: Event-driven
"КОНТЕКСТ:
Система уведомлений.
События: user\_registered, order\_placed, payment\_failed
Handlers: email, sms, push, webhook
ТРЕБОВАНИЯ:
- Loose coupling
- Async handlers
- Retry механизм
ЗАДАЧА:
Event bus с handlers.
\`\`\`python
from typing import Callable, Awaitable
from dataclasses import dataclass
import asyncio
@dataclass
class Event:
name: str
payload: dict
EventHandler = Callable$\[Event$, Awaitable$None$\]
class EventBus:
def \_\_init\_\_(self):
self.\_handlers: dict$str, list\[EventHandler$\] = \{\}
def subscribe(self, event\_name: str, handler: EventHandler) -\> None: ...
async def publish(self, event: Event) -\> None: ...
\# Handlers с retry
\`\`\`"
Dependency Injection
"КОНТЕКСТ:
FastAPI приложение с сервисами:
- UserService (зависит от UserRepository, PasswordHasher)
- OrderService (зависит от OrderRepository, UserService, PaymentGateway)
ЗАДАЧА:
DI через Depends без внешних библиотек.
\`\`\`python
from fastapi import Depends
from functools import lru\_cache
\# Фабрики зависимостей
def get\_db\_session() -\> AsyncSession:
...
def get\_user\_repository(
session: AsyncSession = Depends(get\_db\_session)
) -\> UserRepository:
return SQLAlchemyUserRepository(session)
def get\_user\_service(
repo: UserRepository = Depends(get\_user\_repository),
hasher: PasswordHasher = Depends(get\_password\_hasher)
) -\> UserService:
return UserService(repo, hasher)
\# Использование в endpoint
@router.post('/users')
async def create\_user(
data: UserCreate,
service: UserService = Depends(get\_user\_service)
):
return await service.register(data)
\`\`\`"
Тестирование
Unit тесты
Пример 1: Базовый тест
"ФУНКЦИЯ:
\`\`\`python
def calculate\_discount(price: Decimal, discount\_percent: int) -\> Decimal:
if not 0 \<= discount\_percent \<= 100:
raise ValueError('Discount must be 0-100')
return price \* Decimal(discount\_percent) / 100
ЗАДАЧА: pytest тесты.
ПОКРЫТИЕ:
-
Обычные значения
-
Граничные значения (0%, 100%)
-
Невалидные значения (-1%, 101%)
-
Типы (float вместо Decimal)
ФОРМАТ:
import pytest
from decimal import Decimal
class TestCalculateDiscount:
def test\_normal\_discount(self): ...
def test\_zero\_discount(self): ...
def test\_full\_discount(self): ...
def test\_negative\_discount\_raises(self): ...
def test\_over\_hundred\_raises(self): ...
@pytest.mark.parametrize('price,percent,expected', $(Decimal('100'), 10, Decimal('10')),
(Decimal('50'), 20, Decimal('10')),$)
def test\_parametrized(self, price, percent, expected): ...
\`\`\`"
Пример 2: Mocking
"ФУНКЦИЯ:
\`\`\`python
class UserService:
def \_\_init\_\_(self, repo: UserRepository, email\_client: EmailClient):
self.\_repo = repo
self.\_email = email\_client
async def register(self, data: UserCreate) -\> User:
user = await self.\_repo.create(data)
await self.\_email.send\_welcome(user.email)
return user
ЗАДАЧА: Тесты с моками.
import pytest
from unittest.mock import AsyncMock, MagicMock
@pytest.fixture
def mock\_repo():
repo = AsyncMock(spec=UserRepository)
repo.create.return\_value = User(id=1, email='test@test.com')
return repo
@pytest.fixture
def mock\_email():
return AsyncMock(spec=EmailClient)
@pytest.fixture
def service(mock\_repo, mock\_email):
return UserService(mock\_repo, mock\_email)
class TestUserServiceRegister:
async def test\_creates\_user(self, service, mock\_repo): ...
async def test\_sends\_welcome\_email(self, service, mock\_email): ...
async def test\_returns\_created\_user(self, service): ...
\`\`\`"
Пример 3: Fixtures
"КОНТЕКСТ:
Интеграционные тесты с БД.
ЗАДАЧА:
pytest fixtures с cleanup.
\`\`\`python
import pytest
import asyncio
from sqlalchemy.ext.asyncio import create\_async\_engine, AsyncSession
@pytest.fixture(scope='session')
def event\_loop():
loop = asyncio.get\_event\_loop\_policy().new\_event\_loop()
yield loop
loop.close()
@pytest.fixture(scope='session')
async def engine():
engine = create\_async\_engine('postgresql+asyncpg://test:test@localhost/test')
yield engine
await engine.dispose()
@pytest.fixture
async def session(engine):
async with AsyncSession(engine) as session:
yield session
await session.rollback()
@pytest.fixture
async def user(session):
user = User(email='test@test.com')
session.add(user)
await session.commit()
await session.refresh(user)
yield user
await session.delete(user)
await session.commit()
\`\`\`"
Property-based тесты
"ФУНКЦИЯ:
\`\`\`python
def sort\_by\_key(items: list$dict$, key: str, reverse: bool = False) -\> list$dict$:
return sorted(items, key=lambda x: x.get(key), reverse=reverse)
ЗАДАЧА: Property-based тесты с hypothesis.
СВОЙСТВА:
-
Длина сохраняется
-
Элементы сохраняются (множество)
-
Результат отсортирован
-
Идемпотентность
from hypothesis import given, strategies as st
import hypothesis
\# Стратегия для списка словарей с ключом 'value'
items\_strategy = st.lists(
st.fixed\_dictionaries(\{'value': st.integers()\}),
min\_size=0,
max\_size=100
)
@given(items\_strategy)
def test\_length\_preserved(items):
result = sort\_by\_key(items, 'value')
assert len(result) == len(items)
@given(items\_strategy)
def test\_elements\_preserved(items):
result = sort\_by\_key(items, 'value')
assert sorted(items, key=id) == sorted(result, key=id) \# те же объекты
@given(items\_strategy)
def test\_sorted(items):
result = sort\_by\_key(items, 'value')
values = $x.get('value') for x in result$
assert values == sorted(values)
@given(items\_strategy)
def test\_idempotent(items):
once = sort\_by\_key(items, 'value')
twice = sort\_by\_key(once, 'value')
assert once == twice
\`\`\`"
Отладка
Профилирование
Пример 1: CPU profiling
"ПРОБЛЕМА:
Функция работает медленно на больших данных.
ЗАДАЧА:
1. Профилирование с cProfile
2. Визуализация с snakeviz
3. Идентификация bottleneck
4. Оптимизация
\`\`\`python
import cProfile
import pstats
from pstats import SortKey
def profile\_function(func, \*args, \*\*kwargs):
profiler = cProfile.Profile()
profiler.enable()
result = func(\*args, \*\*kwargs)
profiler.disable()
stats = pstats.Stats(profiler)
stats.sort\_stats(SortKey.CUMULATIVE)
stats.print\_stats(20) \# топ 20 функций
return result
\# Использование
profile\_function(slow\_function, large\_data)
\# Визуализация: snakeviz output.prof
\`\`\`"
Пример 2: Memory profiling
"ПРОБЛЕМА:
Память растёт линейно со временем.
ЗАДАЧА:
Найти утечку памяти.
\`\`\`python
import tracemalloc
import linecache
def memory\_profile():
tracemalloc.start()
\# Код, который нужно проверить
result = process\_data()
snapshot = tracemalloc.take\_snapshot()
top\_stats = snapshot.statistics('lineno')
print('Top 10 memory allocations:')
for stat in top\_stats$:10$:
print(stat)
\# Показать код
for line in stat.traceback.format():
print(line)
tracemalloc.stop()
return result
\# Сравнение снимков
def find\_leak():
tracemalloc.start()
snapshot1 = tracemalloc.take\_snapshot()
\# Операция, которая может течь
for \_ in range(1000):
process\_item()
snapshot2 = tracemalloc.take\_snapshot()
diff = snapshot2.compare\_to(snapshot1, 'lineno')
print('Memory diff:')
for stat in diff$:10$:
print(stat)
\`\`\`"
Пример 3: Async debugging
"ПРОБЛЕМА:
Deadlock при высокой нагрузке.
ЗАДАЧА:
Диагностика asyncio.
\`\`\`python
import asyncio
import sys
\# Включить debug mode
asyncio.get\_event\_loop().set\_debug(True)
\# Логирование медленных callbacks
import logging
logging.getLogger('asyncio').setLevel(logging.DEBUG)
\# Показать все задачи
async def debug\_tasks():
tasks = asyncio.all\_tasks()
for task in tasks:
print(f'Task: \{task.get\_name()\}')
print(f' State: \{task.\_state\}')
print(f' Coro: \{task.get\_coro()\}')
if task.\_exception:
print(f' Exception: \{task.\_exception\}')
\# Timeout для отладки
async def with\_timeout(coro, timeout=5.0):
try:
return await asyncio.wait\_for(coro, timeout)
except asyncio.TimeoutError:
print(f'Timeout in \{coro\}')
await debug\_tasks()
raise
\`\`\`"
Code Review промпты
"КОД:
\`\`\`python
$код для ревью$
ФОКУС:
-
Безопасность (injection, auth, secrets)
-
Производительность (сложность, N+1, память)
-
Корректность (edge cases, типы, ошибки)
-
Читаемость (naming, structure, comments)
ФОРМАТ:
\#\# Критичные (блокеры)
- Строка N: $проблема$ → $решение$
\#\# Важные
- Строка N: $проблема$ → $решение$
\#\# Рекомендации
- $предложение$
\#\# Хорошо
- $что сделано хорошо$
ОГРАНИЧЕНИЯ:
-
Не комментировать форматирование (есть линтер)
-
Максимум 10 замечаний
-
Только реальные проблемы"