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

Что делаешь чтобы не допускать ошибок?

1.0 Junior🔥 121 комментариев
#Soft skills и опыт работы#Тестирование

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

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

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

Стратегии предотвращения ошибок в production

Это один из самых важных вопросов. Я использую многоуровневый подход — нет одного серебристого пули, но совокупность практик снижает количество bagов на 95%+.

1. Статический анализ кода

Улавливаю ошибки ДО запуска кода.

// .eslintrc.json
{
  "extends": "eslint:recommended",
  "rules": {
    "no-console": "error",           // Нет console.log в production
    "no-unused-vars": "error",       // Мусор в коде
    "prefer-const": "error",         // let -> const где возможно
    "eqeqeq": "error",               // === вместо ==
    "no-implicit-globals": "error",  // Защита от глобальных переменных
    "no-eval": "error"               // Никогда eval
  }
}

// TypeScript strict mode
// tsconfig.json
{
  "compilerOptions": {
    "strict": true,                  // Всё: strictNullChecks, noImplicitAny, etc.
    "noImplicitAny": true,           // Тип ДОЛЖЕН быть явным
    "noImplicitThis": true,          // this должно быть типизировано
    "alwaysStrict": true,            // use strict
    "noUnusedLocals": true,          // Удаляем мусор
    "noUnusedParameters": true,      // Параметры которые не используются
    "noImplicitReturns": true,       // Все пути возвращают значение
    "noFallthroughCasesInSwitch": true // Забытые breaks в switch
  }
}

2. Тестирование (TDD)

Пишу тесты ДО кода.

// Пример: TDD подход
// 1. RED — тест падает
describe('PaymentService', () => {
  it('should reject payment if amount is negative', async () => {
    const service = new PaymentService();
    
    // Этот тест падает, потому что функция ещё не проверяет
    expect(async () => {
      await service.process(-100);
    }).rejects.toThrow('Amount must be positive');
  });
  
  it('should process payment with valid amount', async () => {
    const service = new PaymentService();
    const result = await service.process(100);
    expect(result.status).toBe('success');
  });
  
  it('should save transaction to database', async () => {
    const mockDB = { save: jest.fn() };
    const service = new PaymentService(mockDB);
    
    await service.process(100);
    
    expect(mockDB.save).toHaveBeenCalledWith(
      expect.objectContaining({
        amount: 100,
        status: 'completed'
      })
    );
  });
});

// 2. GREEN — минимальный код для прохождения тестов
class PaymentService {
  async process(amount) {
    // Валидация (из теста #1)
    if (amount <= 0) {
      throw new Error('Amount must be positive');
    }
    
    // Логика обработки платежа
    const result = await this.gateway.authorize(amount);
    
    // Сохранение в БД (из теста #3)
    if (result.approved) {
      await this.db.save({
        amount,
        status: 'completed',
        timestamp: new Date()
      });
    }
    
    return { status: result.approved ? 'success' : 'failed' };
  }
}

// 3. REFACTOR — улучшение кода, тесты остаются зелёными

3. Типизация (TypeScript)

Ослабляю класс багов на компиляции.

// ❌ Без типов — опасно
const createUser = (email, password) => {
  // Что если email не строка? Что если password null?
  return db.query('INSERT INTO users...');
};

// ✅ С типами — safe
interface CreateUserRequest {
  email: string;
  password: string;
}

interface User {
  id: number;
  email: string;
  password: string; // hashed
  createdAt: Date;
}

const createUser = async (req: CreateUserRequest): Promise<User> => {
  if (!isValidEmail(req.email)) {
    throw new ValidationError('Invalid email');
  }
  
  if (req.password.length < 8) {
    throw new ValidationError('Password too short');
  }
  
  const hashed = await bcrypt.hash(req.password, 12);
  return await db.query('INSERT INTO users...', [req.email, hashed]);
};

// Типизация функций
type AsyncHandler = (req: Request, res: Response) => Promise<void>;
type Middleware = (req: Request, res: Response, next: NextFunction) => void;

// Даже простые утилиты
const sleep = (ms: number): Promise<void> => {
  return new Promise(resolve => setTimeout(resolve, ms));
};

4. Лinting на CI/CD

Улавливаю ошибки в pull request.

# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - run: npm ci
      - run: npm run lint  # ESLint + Prettier
      - run: npm run type-check  # TypeScript compiler
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - run: npm ci
      - run: npm run test:coverage
      - uses: codecov/codecov-action@v2  # Coverage report
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - run: npm audit  # Проверка уязвимостей
      - run: npm run security-scan

5. Code Review

Другие люди находят то, что я упустил.

// Пример плохого код review
const comment = `
Высь, большие изменения, сложновато.
`;

// Пример хорошего code review
const comment = `
Проблемы:

1. **Race condition в findOpponent()**: Если два пользователя найдут друг друга одновременно, можно обновить статус дважды.
Решение: SELECT FOR UPDATE SKIP LOCKED
Пример: PR #1423

2. **Memory leak**: Redis ключи не имеют TTL, будут расти бесконечно
Решение: setEx вместо set

3. **Missing error handling**: Если gateway.authorize() отклонит, payment остаётся в неопределённом состоянии
Решение: Транзакция в БД

Плюсы:
+ Отличная архитектура
+ Хорошие тесты (95% coverage)
+ Документацияясная
`;

6. Интеграционное и E2E тестирование

Тестирую целые сценарии.

// Интеграционный тест
describe('Payment Flow', () => {
  let app, db, redis;
  
  beforeAll(async () => {
    // Реальные сервисы (или mock)
    db = await connectToTestDB();
    redis = await connectToTestRedis();
    app = createApp(db, redis);
  });
  
  it('should process complete payment flow', async () => {
    // 1. Создаём пользователя
    const { body: user } = await request(app)
      .post('/users')
      .send({ email: 'test@example.com', password: 'Password123' });
    
    // 2. Логинимся
    const { body: auth } = await request(app)
      .post('/auth/login')
      .send({ email: 'test@example.com', password: 'Password123' });
    
    // 3. Создаём платёж
    const { body: payment } = await request(app)
      .post('/payments')
      .set('Authorization', `Bearer ${auth.token}`)
      .send({ amount: 100 });
    
    // 4. Проверяем в БД
    const savedPayment = await db.query(
      'SELECT * FROM payments WHERE id = $1',
      [payment.id]
    );
    expect(savedPayment.status).toBe('completed');
    
    // 5. Проверяем в Redis (кеш)
    const cached = await redis.get(`payment:${payment.id}`);
    expect(cached).toBeDefined();
  });
});

7. Мониторинг в Production

Улавливаю проблемы ЧТО они начинаются.

// Логирование с контекстом
const logger = createLogger();

app.use((req, res, next) => {
  const startTime = Date.now();
  
  res.on('finish', () => {
    const duration = Date.now() - startTime;
    
    logger.info('HTTP request', {
      method: req.method,
      path: req.path,
      status: res.statusCode,
      duration,
      userId: req.user?.id,
      
      // Предупреждаем если медленный запрос
      level: duration > 1000 ? 'warn' : 'info'
    });
    
    // Отправляем в Sentry/DataDog
    if (res.statusCode >= 500) {
      Sentry.captureException(new Error(`HTTP ${res.statusCode}`));
    }
  });
  
  next();
});

// Алерты на критичные ошибки
const alertingMiddleware = (error, req, res, next) => {
  logger.error('Unhandled error', {
    error: error.message,
    stack: error.stack,
    path: req.path
  });
  
  // КРИТИЧНЫЙ ERROR — отправляем алерт в Slack
  if (error.message.includes('DATABASE_CONNECTION_FAILED')) {
    notifier.sendToSlack(`CRITICAL: DB connection failed!
${error.message}\n<@oncall>`);
  }
  
  res.status(500).json({ error: 'Internal error' });
};

8. Валидация входных данных

Никогда не доверяю клиентам.

// Класс для валидации
class ValidationError extends Error {
  constructor(message, field) {
    super(message);
    this.field = field;
  }
}

const validateUserInput = (req) => {
  const { email, password, age } = req.body;
  
  // Email
  if (!email || typeof email !== 'string') {
    throw new ValidationError('Email required', 'email');
  }
  if (!isValidEmail(email)) {
    throw new ValidationError('Invalid email format', 'email');
  }
  
  // Password
  if (!password || typeof password !== 'string') {
    throw new ValidationError('Password required', 'password');
  }
  if (password.length < 8) {
    throw new ValidationError('Password too short (min 8)', 'password');
  }
  
  // Age
  if (age !== undefined) {
    if (typeof age !== 'number' || age < 18) {
      throw new ValidationError('Must be 18+', 'age');
    }
  }
  
  return { email, password, age };
};

// Используем с middleware
app.post('/users', (req, res, next) => {
  try {
    const data = validateUserInput(req);
    res.json(createUser(data));
  } catch (error) {
    if (error instanceof ValidationError) {
      res.status(400).json({
        field: error.field,
        message: error.message
      });
    } else {
      next(error);
    }
  }
});

9. Дефенсивное программирование

Пишу код как будто он сломается.

// Пример: обработка external API
const getExchangeRate = async (from, to) => {
  try {
    const response = await fetch(
      `https://api.exchange.com/rate/${from}/${to}`,
      { timeout: 5000 }
    );
    
    if (!response.ok) {
      logger.warn(`Exchange API returned ${response.status}`);
      // Fallback на кеширован значение
      return await getCachedRate(from, to);
    }
    
    const data = await response.json();
    
    // Валидация ответа
    if (!data.rate || typeof data.rate !== 'number') {
      logger.error('Invalid exchange API response', { data });
      return await getCachedRate(from, to);
    }
    
    // Сохраняем в кеш
    await cache.set(`rate:${from}:${to}`, data.rate, 3600);
    
    return data.rate;
  } catch (error) {
    logger.error('Exchange API error', { error });
    // Fallback
    return await getCachedRate(from, to);
  }
};

Заключение

Мой подход: многоуровневая защита

  1. Статический анализ (ESLint, TypeScript)
  2. Модульные тесты (TDD, jest)
  3. Интеграционные тесты
  4. Code review
  5. CI/CD pipeline
  6. Мониторинг в production
  7. Валидация входных данных
  8. Дефенсивное программирование

Ни один из этих методов не является панацеей, но вместе они создают fortress из качества.

Что делаешь чтобы не допускать ошибок? | PrepBro