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

Какая была самая большая ошибка?

1.3 Junior🔥 161 комментариев
#Soft Skills

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

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

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

Самая большая ошибка (и что я из неё учился)

За 10+ лет разработки было много ошибок. Вот одна из самых значительных.

Контекст: проект e-commerce платформы

Это был проект 2013-2014. Django приложение, несколько сотен тысяч пользователей. У нас был опытный лидер, но я был молодой разработчик (года 3 опыта) и делал первые архитектурные решения.

Ошибка: отсутствие кеширования и неправильная БД архитектура

Что произошло:

Проблема 1: Каждый запрос = запрос в БД

# Как я писал (очень наивно):
def get_user_profile(user_id):
    user = User.objects.get(id=user_id)  # запрос 1
    orders = Order.objects.filter(user_id=user_id)  # запрос 2
    reviews = Review.objects.filter(user_id=user_id)  # запрос 3
    stats = UserStats.objects.get(user_id=user_id)  # запрос 4
    
    return {
        'user': user,
        'orders': orders,
        'reviews': reviews,
        'stats': stats
    }

# Эта функция вызывалась на каждом запросе к /profile
# Даже если отправка статические данные
# Даже если пользователь просто отошёл от компьютера и перезагрузил страницу

# Результат: 4 запроса в БД на каждый раз!
# На 100 000 пользователей = 400 000 запросов в секунду в пик (что привело к deadlock)

Проблема 2: N+1 problem

# На странице товаров:
products = Product.objects.all()  # 1 запрос
for product in products:
    seller = product.seller  # ОТДЕЛЬНЫЙ ЗАПРОС для каждого товара!
    print(f"{product.name} от {seller.name}")

# Если товаров 100, то 101 запрос!
# Нужно было select_related('seller')

Проблема 3: Никакого кеша

По-моему наивному решению, кеш — это для «фейки», что БД не справляется. На самом деле БД полностью перегружена была.

Мы читали популярные категории тысячи раз в день из БД, хотя они меняются раз в день.

Когда это взорвалось

День запуска: всё работает быстро. 100 одновременных пользователей.

День 1 (растущая популярность): 1000 пользователей. Начало замедляться.

День 5: 10 000 пользователей. БД падает каждые 2-3 часа. Нужна перезагрузка.

День 10: Жалобы клиентов. Мы срочно включили 2 additional сервера БД и начали читать логи.

Обнаруженные проблемы в логах:

SLOW QUERY LOG:
- SELECT * FROM products WHERE category_id = 5 (без индекса!)
- SELECT * FROM orders WHERE user_id = 123 (100 раз подряд за минуту)
- SELECT * FROM users WHERE id IN (1,2,3,...,10000) (картезианский product)

CONNECTION POOL EXHAUSTED:
- Нет свободных соединений к БД
- Новые запросы очередь 30 секунд

DEADLOCKS:
- Параллельные UPDATE на order_status блокируют друг друга

Как я это исправлял

Шаг 1: Немедленный quick fix (2 дня)

# Кеширование наиболее дорогих операций
from django.core.cache import cache
import hashlib

def get_user_profile(user_id):
    cache_key = f'user_profile:{user_id}'
    cached = cache.get(cache_key)
    if cached:
        return cached
    
    # Если нет в кеше — достаём из БД
    user = User.objects.select_related('profile').get(id=user_id)
    orders = Order.objects.filter(user_id=user_id)
    
    result = {
        'user': user,
        'orders': orders,
    }
    
    # Кешируем на 1 час
    cache.set(cache_key, result, 3600)
    return result

# При обновлении профиля очищаем кеш
def update_user_profile(user_id, data):
    user = User.objects.get(id=user_id)
    user.name = data['name']
    user.save()
    
    # Инвалидировать кеш
    cache.delete(f'user_profile:{user_id}')

Шаг 2: Исправление N+1 (1 неделя)

# БЫЛО (плохо):
products = Product.objects.all()
for product in products:
    seller = product.seller  # N+1 запросов

# СТАЛО (хорошо):
products = Product.objects.select_related('seller').all()
for product in products:
    seller = product.seller  # Уже загружено в памяти

Шаг 3: Индексирование (2 дня)

-- Добавили индексы на часто используемые WHERE колонки
CREATE INDEX idx_products_category ON products(category_id);
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_reviews_product_id ON reviews(product_id);

-- Проверили планы запросов
EXPLAIN ANALYZE SELECT * FROM products WHERE category_id = 5;

Шаг 4: Архитектурные изменения (2 недели)

# Разделили чтение и запись
# CQRS паттерн (Command Query Responsibility Segregation)

# Для чтения — реплика с кешем
def get_popular_products():
    # Читаем из read-only реплики
    return cache.get_or_set(
        'popular_products',
        lambda: ProductReadReplica.objects.filter(
            popularity_score__gt=100
        ).order_by('-popularity_score')[:10],
        3600
    )

# Для записи — основная БД
def create_order(user_id, items):
    # Пишем в основную БД
    order = Order.objects.create(user_id=user_id, total=0)
    for item in items:
        OrderItem.objects.create(order=order, product=item['id'])
    
    # Асинхронно обновляем счётчик в read-модели
    update_popular_products.delay()

Шаг 5: Мониторинг (постоянно)

# Добавили логирование медленных запросов
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'OPTIONS': {
            'options': '-c log_min_duration_statement=500'  # >500ms
        }
    }
}

# Добавили метрики
from django.db import connection
from django.conf import settings

def log_db_stats():
    with connection.cursor() as cursor:
        cursor.execute('SELECT count(*) FROM pg_stat_statements')
        slow_queries = cursor.fetchone()[0]
        metrics.gauge('db.slow_queries', slow_queries)

Результаты

До исправления:

  • Response time: 2-5 секунд
  • DB CPU: 95%
  • Downtime: 2-3 часа в неделю

После исправления:

  • Response time: 100-200ms
  • DB CPU: 30%
  • Downtime: 0 (до конца проекта)

Что я учился

1. Performance matters from day 1

Не думай: "Оптимизируем потом, когда будет много пользователей"

Оптимизировать потом ТЕМ СЛОЖНЕЕ, чем сделать правильно сразу:

# День 1: легко добавить кеш и индексы
cache.set(...)
CREATE INDEX ...

# День 1000 (когда миллионы записей): 
# очень трудно добавить кеш (слишком много инвалидации)
# очень трудно добавить индекс (блокирует БД на часы)

2. Тестирование под нагрузкой

После этого я всегда:

  • Load testing перед релизом
  • Stress testing до breaking point
  • Capacity planning на 3-6 месяцев вперёд
# Используем locust или ab для нагрузочного тестирования
from locust import HttpUser, task

class UserBehavior(HttpUser):
    @task
    def view_profile(self):
        self.client.get("/profile/123")
    
    @task
    def view_products(self):
        self.client.get("/products/category/5")

3. Мониторинг и логи с начала

# Не ждём проблемы — предупреждаем её
logger.warning(f"Slow query: {query_time}ms for {query}")
if query_time > 1000:
    alert("Critical slow query detected")

4. Очевидные архитектурные паттерны

  • Кеш для часто читаемых данных
  • select_related / prefetch_related
  • Индексы на WHERE, JOIN, ORDER BY
  • Читаемые реплики отдельно от записи

5. Важность Code Review

Если бы был опытный code reviewer, он бы сразу заметил:

  • No caching
  • No indexes
  • Potential N+1
# Code review feedback, который мне нужен был:
"""
🚩 This will be slow with large datasets.
   - Missing cache
   - Missing select_related('seller')
   - No index on category_id

Please add:
- @cache_page(3600) decorator
- select_related in queryset
- Database migration with CREATE INDEX

Let's discuss database design before pushing 100k users
"""

Главный вывод

Эта ошибка стоила:

  • 2 недели emergency fixing
  • Несколько критических downtime
  • Потерю уверенности клиентов

Но это был лучший урок в моей карьере. Теперь я:

  • Думаю о масштабируемости с первого дня
  • Пишу load tests раньше production code
  • Всегда кеширую правильно
  • Делаю code review для себя на потенциальные bottlenecks

На собеседовании это показывает:

  1. Я делал ошибки — это нормально
  2. Я анализирую их и учусь
  3. Я знаю, как их избежать в будущем
  4. Я вижу связь между архитектурными решениями и production проблемам
Какая была самая большая ошибка? | PrepBro