Хаос-инжиниринг для TypeScript: как закалить приложение

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Когда ваш TypeScript-сервис встречает хаос

Представьте: ваше TypeScript-приложение работает идеально — тесты проходят, деплой проходит, пользователи довольны. Но вот наступает 3 часа ночи, и Redis-кластер решает "прилечь отдохнуть". Или CDN начинает отдавать 503 ошибки. Или, что ещё веселее, сетевой пакет теряется где-то между континентами.

В этот момент ваш аккуратный код на TypeScript, написанный с душой, может повести себя как испуганный кот. Или как супергерой. Всё зависит от того, тренировали ли вы его встречать хаос лицом к лицу.

Chaos engineering — это не про то, чтобы сломать продакшн. Это про то, чтобы научиться ломаться правильно. И TypeScript с его строгой типизацией и мощными абстракциями — отличный полигон для такой тренировки.

Что такое chaos engineering на самом деле?

Chaos engineering — это дисциплина экспериментирования над распределённой системой, чтобы выявить её слабые места до того, как это сделают пользователи. В контексте TypeScript-приложений это означает:

  • Систематическое внедрение сбоев — отключение сервисов, задержки, ошибки сети
  • Наблюдение за поведением — как ваш код реагирует на неожиданности
  • Автоматизацию экспериментов — чтобы повторять их регулярно
  • Улучшение архитектуры — на основе полученных данных

Главное правило: вы не должны узнать о проблеме от разгневанного пользователя. Лучше пусть о ней узнает ваш автоматизированный "разрушитель" в контролируемой среде.

Почему TypeScript идеален для chaos engineering?

TypeScript — это не просто надстройка над JavaScript. Это возможность писать код, который предсказуем даже в хаосе. Вот почему:

  • Строгая типизация — вы точно знаете, какие данные приходят и уходят. Но что если вместо числа придёт undefined? Chaos-тесты это проверят.
  • Асинхронность — Promise, async/await, Observable — всё это может "сломаться" интересными способами при сетевых проблемах.
  • Экосистема — Jest, Mocha, Playwright — готовые инструменты для интеграции chaos-сценариев.

Практический пример: внедряем хаос в Express-приложение

Давайте представим, что у нас есть простое Express-приложение на TypeScript, которое обращается к внешнему API для получения данных о погоде. Мы хотим проверить, как оно поведёт себя, если этот API станет недоступен.

Шаг 1: Напишем обычный сервис

// weather.service.ts
interface WeatherResponse {
    temperature: number;
    humidity: number;
    condition: string;
}

class WeatherService {
    async getWeather(city: string): Promise<WeatherResponse> {
        const response = await fetch(`https://api.weather.com/v1/${city}`);
        
        if (!response.ok) {
            throw new Error(`Weather API error: ${response.status}`);
        }
        
        return response.json() as Promise<WeatherResponse>;
    }
}

export const weatherService = new WeatherService();

Шаг 2: Добавим контроллер с защитой

// weather.controller.ts
import { Request, Response } from 'express';
import { weatherService } from './weather.service';

class WeatherController {
    async getWeatherHandler(req: Request, res: Response) {
        const { city } = req.params;
        
        try {
            const weather = await weatherService.getWeather(city);
            res.json(weather);
        } catch (error) {
            // Логируем ошибку
            console.error(`Failed to fetch weather for ${city}:`, error);
            
            // Возвращаем fallback-ответ
            res.status(503).json({
                error: 'Service temporarily unavailable',
                fallback: {
                    temperature: 20,
                    condition: 'unknown'
                }
            });
        }
    }
}

Выглядит неплохо? Но мы не знаем, что произойдёт, если API начнёт отвечать очень медленно. Или если запросы будут падать с разными ошибками. Тут и приходит chaos engineering.

Шаг 3: Создаём chaos-прокси

Мы напишем middleware, который будет имитировать различные сбои. Это ключевой элемент — вы не должны менять свой код. Хаос внедряется на уровне инфраструктуры или middleware.

// chaos-proxy.middleware.ts
import { Request, Response, NextFunction } from 'express';

interface ChaosConfig {
    failureRate: number;       // 0.0 - 1.0
    latencyMs: number;         // задержка в мс
    statusCode?: number;       // какой код ошибки возвращать
}

class ChaosProxyMiddleware {
    private config: ChaosConfig;

    constructor(config: ChaosConfig) {
        this.config = config;
    }

    async handle(req: Request, res: Response, next: NextFunction) {
        // Имитация задержки
        if (this.config.latencyMs > 0) {
            await new Promise(resolve => setTimeout(resolve, this.config.latencyMs));
        }

        // Имитация сбоя
        if (Math.random() < this.config.failureRate) {
            const statusCode = this.config.statusCode || 500;
            
            // Логируем хаос-событие
            console.log(`[CHAOS] Injecting failure: ${statusCode} for ${req.path}`);
            
            res.status(statusCode).json({
                error: 'Simulated chaos failure',
                chaosConfig: this.config
            });
            return;
        }

        next();
    }

    // Метод для обновления конфигурации на лету
    updateConfig(newConfig: Partial<ChaosConfig>) {
        this.config = { ...this.config, ...newConfig };
    }
}

export const chaosProxy = new ChaosProxyMiddleware({
    failureRate: 0.3,  // 30% запросов будут падать
    latencyMs: 500,    // задержка 500мс
    statusCode: 503    // Service Unavailable
});

Шаг 4: Подключаем хаос к приложению

// app.ts
import express from 'express';
import { chaosProxy } from './chaos-proxy.middleware';
import { weatherController } from './weather.controller';

const app = express();

// Подключаем chaos-proxy ТОЛЬКО для тестовой среды
if (process.env.NODE_ENV === 'chaos-test') {
    app.use('/api/*', chaosProxy.handle.bind(chaosProxy));
}

app.get('/api/weather/:city', weatherController.getWeatherHandler);

app.listen(3000, () => {
    console.log('Server running on port 3000');
});

Как проводить chaos-эксперименты с TypeScript?

Теперь, когда у нас есть инструмент для внедрения хаоса, давайте разберёмся, как проводить осмысленные эксперименты. Вот пошаговая методология:

1. Определите гипотезу

Не ломайте всё подряд. Сформулируйте конкретную гипотезу. Например: "Если внешний API для погоды возвращает 503 ошибку, наше приложение корректно отдаёт fallback-ответ и не падает с критической ошибкой".

2. Создайте тестовый сценарий

// chaos-experiment.test.ts
import { chaosProxy } from './chaos-proxy.middleware';
import { weatherService } from './weather.service';

describe('Chaos Experiment: Weather API failure', () => {
    beforeAll(() => {
        // Включаем 100% сбоев для этого эксперимента
        chaosProxy.updateConfig({
            failureRate: 1.0,
            statusCode: 503                

Блоги

Рекомендаций по книгам