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

Есть ли критичные моменты в работе?

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

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

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

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

Критичные моменты в работе Python разработчика

Да, в разработке есть критичные моменты, которые требуют повышенного внимания и осторожности. От их правильной обработки зависит надёжность, безопасность и производительность приложений.

1. Критичные моменты в базах данных

Race conditions и параллельный доступ

Когда несколько пользователей одновременно изменяют один ресурс, могут возникнуть непредсказуемые результаты.

# ПЛОХО - race condition
class BankAccount:
    def withdraw(self, amount: float):
        if self.balance >= amount:  # Проверка
            self.balance -= amount   # Изменение
            return True
        return False

# Два потока одновременно:
# Thread 1: проверка: balance=100, amount=100 ✓
# Thread 2: проверка: balance=100, amount=100 ✓
# Thread 1: вычитаем: balance = 0
# Thread 2: вычитаем: balance = -100 ← ОШИБКА!

# ХОРОШО - используем транзакции с блокировкой
from sqlalchemy.orm import Session
from sqlalchemy import select, and_

class BankAccount(Base):
    __tablename__ = "accounts"
    id = Column(Integer, primary_key=True)
    balance = Column(Float, default=0)

def withdraw_safely(account_id: int, amount: float, db: Session) -> bool:
    """Безопасное снятие со счёта"""
    try:
        # SELECT FOR UPDATE - другие транзакции ждут
        account = db.query(BankAccount).with_for_update().filter_by(
            id=account_id
        ).first()
        
        if account.balance >= amount:
            account.balance -= amount
            db.commit()
            return True
        
        db.rollback()
        return False
    except Exception as e:
        db.rollback()
        raise

Потеря данных при откатах

Если не обработать исключения, данные могут быть потеряны.

# ПЛОХО
def create_order(user_id: int, items: List[Item]):
    order = Order(user_id=user_id)
    db.add(order)
    db.flush()  # Получили ID
    
    for item in items:
        order_item = OrderItem(order_id=order.id, item_id=item.id)
        db.add(order_item)
        db.commit()  # Если ошибка на 5м товаре, 4 сохранены, 1-4 потеряны

# ХОРОШО - одна транзакция
def create_order(user_id: int, items: List[Item], db: Session):
    try:
        order = Order(user_id=user_id)
        db.add(order)
        db.flush()
        
        for item in items:
            order_item = OrderItem(order_id=order.id, item_id=item.id)
            db.add(order_item)
        
        db.commit()  # Либо все товары добавлены, либо ничего
        return order
    except Exception:
        db.rollback()
        raise

2. Критичные моменты безопасности

SQL Injection

Одна из самых опасных уязвимостей.

# КРИТИЧНО ОПАСНО - SQL Injection
user_input = "'; DROP TABLE users; --"
query = f"SELECT * FROM users WHERE email = '{user_input}'"  # БОМБА!
db.execute(query)

# БЕЗОПАСНО - параметризованные запросы
query = "SELECT * FROM users WHERE email = :email"
db.execute(query, {"email": user_input})  # Строка экранируется

# ИЛИ с SQLAlchemy ORM
from sqlalchemy import select
user = db.session.execute(
    select(User).where(User.email == user_input)
).scalar_one_or_none()  # Безопасно

Хранение паролей

Критично неправильное хранение паролей.

# КРИТИЧНО ОПАСНО
class User(Base):
    password = Column(String)  # Пароль в открытом виде!

user.password = user_input  # Сохраняем то, что прислал пользователь

# БЕЗОПАСНО
from werkzeug.security import generate_password_hash, check_password_hash

class User(Base):
    __tablename__ = "users"
    password_hash = Column(String, nullable=False)

def register_user(email: str, password: str, db: Session):
    user = User(
        email=email,
        password_hash=generate_password_hash(password)  # Хешируем
    )
    db.add(user)
    db.commit()

def verify_password(stored_hash: str, provided_password: str) -> bool:
    return check_password_hash(stored_hash, provided_password)

Экспозиция чувствительных данных

Данные могут утечь в логах, ошибках, кэшах.

# ОПАСНО
import logging

logger = logging.getLogger(__name__)

def process_payment(card_data: dict):
    logger.info(f"Processing payment: {card_data}")  # Логируем карточку!
    # Логи содержат чувствительные данные

# БЕЗОПАСНО
def process_payment(card_data: dict):
    # Логируем только необходимое
    logger.info(f"Processing payment for user, card last4: {card_data['number'][-4:]}")
    
    # Или используем маскирование
    def mask_sensitive(data):
        masked = data.copy()
        masked['card_number'] = "*" * 12 + masked['card_number'][-4:]
        return masked
    
    logger.info(f"Payment data: {mask_sensitive(card_data)}")

3. Критичные моменты в асинхронном коде

Deadlock'и и зависания

В асинхронном коде легко создать ситуацию, когда программа зависает.

# ОПАСНО - может зависнуть
import asyncio

async def fetch_data():
    async with aiohttp.ClientSession() as session:
        response = await session.get("https://api.example.com/data")  # Бесконечно
        return response

# БЕЗОПАСНО - с timeout
async def fetch_data():
    timeout = aiohttp.ClientTimeout(total=5)  # 5 секунд максимум
    async with aiohttp.ClientSession(timeout=timeout) as session:
        response = await session.get("https://api.example.com/data")
        return response

# ИЛИ
async def fetch_data_with_timeout():
    try:
        response = await asyncio.wait_for(
            fetch_data(),
            timeout=5.0
        )
        return response
    except asyncio.TimeoutError:
        logger.error("API request timed out")
        raise

Неправильное управление ресурсами

Асинхронные ресурсы (соединения, сессии) нужно закрывать правильно.

# ОПАСНО - утечка соединений
async def process_requests(urls: List[str]):
    session = aiohttp.ClientSession()
    tasks = [
        session.get(url) for url in urls
    ]
    results = await asyncio.gather(*tasks)
    # Забыли закрыть session! Утечка соединения
    return results

# БЕЗОПАСНО
async def process_requests(urls: List[str]):
    async with aiohttp.ClientSession() as session:
        tasks = [
            session.get(url) for url in urls
        ]
        results = await asyncio.gather(*tasks)
        # Сессия закроется автоматически
    return results

4. Критичные моменты в производительности

N+1 запросы

Максимально неэффективно, может убить производительность приложения.

# КРИТИЧНО ОПАСНО - N+1 запросы
users = db.query(User).all()  # 1 запрос

for user in users:  # Если пользователей 10 000
    posts = db.query(Post).filter_by(user_id=user.id).all()  # 10 000 запросов!
    # Итого: 10 001 запрос вместо 1!

# БЕЗОПАСНО - eager loading
from sqlalchemy.orm import joinedload

users = db.query(User).options(
    joinedload(User.posts)
).all()  # 1 запрос с JOIN

for user in users:
    posts = user.posts  # Данные уже загружены

Утечки памяти

От которых может упасть сервер в production.

# ОПАСНО - кэш растёт бесконечно
from functools import lru_cache

cache = {}  # Глобальный словарь

def process_file(filename: str):
    if filename not in cache:
        cache[filename] = read_large_file(filename)  # 100MB
    return cache[filename]

# После обработки 1000 файлов: 100GB памяти!

# БЕЗОПАСНО - ограниченный кэш
from functools import lru_cache

@lru_cache(maxsize=100)  # Максимум 100 записей
def process_file(filename: str):
    return read_large_file(filename)

# Или вручную с временем жизни
import time
from collections import OrderedDict

class TTLCache:
    def __init__(self, ttl_seconds: int = 3600):
        self.cache = OrderedDict()
        self.ttl = ttl_seconds
        self.timestamps = {}
    
    def get(self, key: str):
        if key in self.cache:
            if time.time() - self.timestamps[key] < self.ttl:
                return self.cache[key]
            else:
                del self.cache[key]
                del self.timestamps[key]
        return None
    
    def set(self, key: str, value):
        self.cache[key] = value
        self.timestamps[key] = time.time()

5. Критичные моменты в API

Неправильная обработка ошибок

Может привести к утечке внутренней информации или плохому UX.

# ОПАСНО
@app.post("/login")
async def login(credentials: LoginRequest):
    user = db.query(User).filter_by(email=credentials.email).first()
    if not user:
        raise HTTPException(status_code=404, detail="User not found")  # Утечка!
    if not verify_password(user.password, credentials.password):
        raise HTTPException(status_code=401, detail="Wrong password")  # Утечка!

# БЕЗОПАСНО - одинаковые ошибки
@app.post("/login")
async def login(credentials: LoginRequest):
    user = db.query(User).filter_by(email=credentials.email).first()
    if not user or not verify_password(user.password, credentials.password):
        raise HTTPException(
            status_code=401,
            detail="Invalid email or password"  # Одна ошибка для обоих случаев
        )
    return {"token": create_token(user.id)}

Отсутствие rate limiting

Открытые двери для DDoS и brute force.

# ОПАСНО - нет защиты
@app.post("/login")
async def login(credentials: LoginRequest):
    # Кто угодно может атаковать перебором
    ...

# БЕЗОПАСНО - rate limiting
from slowapi import Limiter
from slowapi.util import get_remote_address

limiter = Limiter(key_func=get_remote_address)

@app.post("/login")
@limiter.limit("5/minute")  # 5 попыток в минуту
async def login(request: Request, credentials: LoginRequest):
    ...

6. Критичные моменты в развёртывании

Не проверены миграции в production

# ОПАСНО - миграция может сломать production
def migrate_user_emails():
    """На production это может быть критично!"""
    users = db.query(User).all()
    for user in users:
        # Что если это займёт 2 часа на 1M пользователей?
        # БД будет блокирована!
        user.email = user.email.lower()
    db.commit()

# БЕЗОПАСНО
def migrate_user_emails():
    """Батчами, с логированием, с возможностью отката"""
    batch_size = 1000
    offset = 0
    
    while True:
        users = db.query(User).offset(offset).limit(batch_size).all()
        if not users:
            break
        
        for user in users:
            user.email = user.email.lower()
        
        db.commit()
        logger.info(f"Migrated {offset + len(users)} users")
        offset += batch_size
        time.sleep(0.1)  # Снижаем нагрузку на БД

Чеклист критичных моментов

  • ✓ Все HTTP запросы имеют timeout
  • ✓ Все SQL запросы параметризованы
  • ✓ Пароли хешируются (bcrypt/argon2)
  • ✓ Чувствительные данные не логируются
  • ✓ Race conditions обработаны (SELECT FOR UPDATE)
  • ✓ Все ресурсы (файлы, соединения) закрываются
  • ✓ Кэши имеют размер ограничения или TTL
  • ✓ Нет N+1 запросов (eager loading)
  • ✓ Есть rate limiting на публичных endpoints
  • ✓ Миграции тестируются перед production
  • ✓ Логируются ошибки, но не чувствительные данные
  • ✓ Есть мониторинг и алерты в production

Вывод: Критичные моменты требуют тщательного внимания к деталям. Одна ошибка может привести к потере данных, утечке информации или краху сервиса. Всегда думайте о edge cases и тестируйте сценарии отказа.

Есть ли критичные моменты в работе? | PrepBro