Core Principle
Detect and report failures as soon as they occur, preventing invalid states from propagating through the system.
1. Input Validation
class UserRegistration {
public function register(array $data): void {
// Validate all inputs immediately
$this->validateEmail($data['email']);
$this->validatePassword($data['password']);
$this->validateAge($data['age']);
// Only proceed if all validations pass
$this->createUser($data);
}
private function validateEmail(string $email): void {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new ValidationException('Invalid email format');
}
if ($this->emailExists($email)) {
throw new DuplicateEmailException('Email already registered');
}
}
}
Purpose:
- Prevents invalid data from entering the system
- Saves resources by failing before complex operations
- Provides clear error messages to users
- Maintains data integrity
2. Configuration Loading
class AppConfig {
private array $config;
public function __construct(string $configPath) {
if (!file_exists($configPath)) {
throw new ConfigurationException("Config file not found: $configPath");
}
$config = parse_ini_file($configPath, true);
if ($config === false) {
throw new ConfigurationException("Invalid config file format");
}
$this->validateRequiredSettings($config);
$this->config = $config;
}
private function validateRequiredSettings(array $config): void {
$required = ['database', 'api_key', 'environment'];
foreach ($required as $key) {
if (!isset($config[$key])) {
throw new ConfigurationException("Missing required config: $key");
}
}
}
}
Purpose:
- Ensures application starts with valid configuration
- Prevents runtime errors due to missing settings
- Makes configuration problems immediately visible
- Simplifies debugging configuration issues
3. Resource Initialization
class DatabaseConnection {
private PDO $connection;
public function __construct(array $config) {
try {
$this->validateDatabaseConfig($config);
$this->connection = new PDO(
$this->buildDsn($config),
$config['username'],
$config['password'],
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
} catch (PDOException $e) {
throw new DatabaseConnectionException(
"Failed to connect to database: " . $e->getMessage()
);
}
}
private function validateDatabaseConfig(array $config): void {
$required = ['host', 'port', 'database', 'username', 'password'];
foreach ($required as $param) {
if (!isset($config[$param])) {
throw new DatabaseConfigException("Missing $param in database config");
}
}
}
}
Purpose:
- Ensures resources are properly initialized
- Prevents application from running with invalid resources
- Makes resource problems visible during startup
- Avoids cascading failures due to invalid resources
4. External Service Calls
class PaymentGateway {
public function processPayment(Order $order): PaymentResult {
// Validate API credentials
if (!$this->validateApiCredentials()) {
throw new ApiConfigurationException('Invalid API credentials');
}
// Validate order before external call
if (!$order->isValid()) {
throw new InvalidOrderException('Invalid order state');
}
try {
$response = $this->apiClient->charge($order);
if (!$response->isSuccessful()) {
throw new PaymentFailedException($response->getError());
}
return new PaymentResult($response);
} catch (ApiException $e) {
throw new PaymentProcessingException(
"Payment processing failed: " . $e->getMessage()
);
}
}
}
Purpose:
- Prevents unnecessary API calls with invalid data
- Saves time and resources
- Provides immediate feedback on API issues
- Maintains system reliability during external service interactions
5. Data Processing Pipelines
class DataProcessor {
public function processBatch(array $records): array {
$this->validateBatchSize($records);
$results = [];
foreach ($records as $index => $record) {
try {
$this->validateRecord($record);
$results[] = $this->processRecord($record);
} catch (ValidationException $e) {
throw new BatchProcessingException(
"Failed at record $index: " . $e->getMessage()
);
}
}
return $results;
}
private function validateBatchSize(array $records): void {
if (empty($records)) {
throw new EmptyBatchException('Empty batch provided');
}
if (count($records) > 1000) {
throw new BatchSizeException('Batch size exceeds maximum limit');
}
}
}
Purpose:
- Ensures data consistency throughout processing
- Prevents partial processing of invalid data
- Makes data problems visible early
- Simplifies error tracking in complex pipelines
- Maintains data integrity across transformations
Benefits of Fail Fast
- Early error detection
- Cleaner debugging
- Prevents cascading failures
- Maintains data integrity
- Improves system reliability
Best Practices
- Use strong type declarations
- Implement thorough input validation
- Throw specific exceptions
- Validate early in the process
- Use assertions in development
- Implement proper error handling
- Log failures appropriately
When to Use Fail Fast
- Input validation
- Configuration loading
- Resource initialization
- External service calls
- Data processing pipelines
Top comments (0)