Что такое надёжность в разработке?

Ответ

Надёжность в разработке — это характеристика системы, которая определяет её способность корректно выполнять требуемые функции в заданных условиях в течение определённого времени. В моей практике работы с PHP-проектами это достигается через несколько ключевых практик.

Конкретные технические меры, которые я применяю для обеспечения надёжности:

  1. Защищённый ввод данных:

    // Всегда валидирую и санитизирую пользовательский ввод
    $email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
    $age = filter_var($_POST['age'], FILTER_VALIDATE_INT, [
        'options' => ['min_range' => 1, 'max_range' => 120]
    ]);
    
    if (!$email || !$age) {
        throw new InvalidArgumentException('Invalid input data');
    }
  2. Гранулярная обработка исключений:

    try {
        $pdo = new PDO($dsn, $user, $pass);
        $stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?');
        $stmt->execute([$userId]);
        $user = $stmt->fetch();
    
        if (!$user) {
            throw new NotFoundException('User not found');
        }
    } catch (PDOException $e) {
        // Логирую техническую ошибку
        error_log('Database error: ' . $e->getMessage());
        // Пользователю показываю понятное сообщение
        throw new ServiceUnavailableException('Service temporarily unavailable');
    } catch (NotFoundException $e) {
        // Бизнес-логика: пользователь не найден
        http_response_code(404);
        echo json_encode(['error' => $e->getMessage()]);
    }
  3. Комплексное логирование с Monolog:

    $logger = new Logger('app');
    $logger->pushHandler(new StreamHandler('var/log/app.log', Logger::WARNING));
    $logger->pushHandler(new SlackWebhookHandler($webhookUrl));
    
    // Логирую с контекстом
    $logger->error('Payment processing failed', [
        'user_id' => $userId,
        'amount' => $amount,
        'error_code' => $exception->getCode()
    ]);
  4. Автоматизированное тестирование: Пишу unit-тесты для критической бизнес-логики и интеграционные тесты для API-эндпоинтов с помощью PHPUnit. Например, для сервиса оплаты:

    class PaymentServiceTest extends TestCase {
        public function testProcessPaymentThrowsExceptionWhenInsufficientFunds(): void {
            $this->expectException(InsufficientFundsException::class);
    
            $service = new PaymentService();
            $service->processPayment(1000, 'user_with_500');
        }
    }
  5. Мониторинг и алертинг: Настраиваю мониторинг ключевых метрик: время ответа API, процент ошибок 5xx, использование памяти. При деплое на production всегда проверяю наличие работающего мониторинга.

В последнем проекте эти практики помогли снизить количество инцидентов в production на 70% за полгода.

Ответ 18+ 🔞

Ну ты представляешь, надёжность в коде — это как сантехника в доме. Вроде, пока всё течёт, никто и не думает, а потом раз — и тебе уже не до смеха, потому что соседи снизу прибежали с мокрыми тапками. В моих PHP-проектах я эту самую надёжность выстраиваю так, чтобы не было потом мучительно больно в три часа ночи.

Вот конкретно, что я делаю, чтобы не было пиздеца:

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

    // Никаких "ой, да ладно, пройдёт". Валидирую и чищу всё, что влетает.
    $email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
    $age = filter_var($_POST['age'], FILTER_VALIDATE_INT, [
        'options' => ['min_range' => 1, 'max_range' => 120]
    ]);
    
    if (!$email || !$age) {
        throw new InvalidArgumentException('Invalid input data');
    }

    Просто представь: какой-то Вася вводит свой возраст "999" или почту "я@дурак.ру". Без этой проверки твоё приложение станет тем самым пидарасом шерстяным, который всему поверит.

  2. Исключения — не для галочки. Ошибки будут всегда, ёпта. Вопрос в том, упадёт ли всё к чертям с белым экраном или система аккуратно скажет "ой, сорян" и запишет, что случилось. Вот как я это делаю:

    try {
        $pdo = new PDO($dsn, $user, $pass);
        $stmt = $pdo->prepare('SELECT * FROM users WHERE id = ?');
        $stmt->execute([$userId]);
        $user = $stmt->fetch();
    
        if (!$user) {
            throw new NotFoundException('User not found'); // Бизнес-ошибка, не страшно
        }
    } catch (PDOException $e) {
        // Вот тут уже серьёзно. База легла.
        error_log('Database error: ' . $e->getMessage()); // Пишу в лог всё, для себя
        // А юзеру показываю не панику, а вменяемое сообщение
        throw new ServiceUnavailableException('Service temporarily unavailable');
    } catch (NotFoundException $e) {
        // Пользователь не найден — это не конец света, просто 404
        http_response_code(404);
        echo json_encode(['error' => $e->getMessage()]);
    }

    Разделение ошибок на "ой, всё" и "ну бывает" — это основа основ. Доверия ебать ноль ко всему, что снаружи, но внутри система должна быть как швейцарские часы.

  3. Логирование — твои глаза и уши, когда тебя нет. Без логов ты слепой. Я настраиваю Monolog так, чтобы он орал на меня во все возможные места, если что-то пошло не так.

    $logger = new Logger('app');
    $logger->pushHandler(new StreamHandler('var/log/app.log', Logger::WARNING));
    $logger->pushHandler(new SlackWebhookHandler($webhookUrl)); // Чтобы прилетало в слак
    
    // Логирую не просто "чё-то сломалось", а с полным контекстом
    $logger->error('Payment processing failed', [
        'user_id' => $userId, // Кто?
        'amount' => $amount, // Сколько?
        'error_code' => $exception->getCode() // И что за хуйня?
    ]);

    Когда в три ночи раздаётся звонок, первое, что ты делаешь — открываешь логи. И хорошо, если там написано внятно, а не "undefined error at line 0".

  4. Тесты — это не для отчёта начальству. Писать код без тестов — это как ехать на машине без тормозов с горки. Забавно, пока не врежешься. Я пишу тесты для самой важной логики.

    class PaymentServiceTest extends TestCase {
        public function testProcessPaymentThrowsExceptionWhenInsufficientFunds(): void {
            $this->expectException(InsufficientFundsException::class);
            // Жду, что метод выбросит конкретную ошибку, если денег нет
    
            $service = new PaymentService();
            $service->processPayment(1000, 'user_with_500'); // Пытаемся списать 1000 у того, у кого 500
        }
    }

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

  5. Мониторинг — чтобы не охуеть от сюрпризов. Выкатил фичу и забыл? Жди беды. Я всегда настраиваю слежку за самым важным: как быстро отвечает API, сколько ошибок сервера, не жрёт ли память как не в себя. Без этого ты просто ходишь по охуенно тонкому льду и надеешься, что пройдёшь.

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