A set of internal engineering rules for writing clean, scalable, and maintainable Laravel code across all projects.
All request validations must live inside dedicated Form Request or Validation classes β never inline inside controllers or services.
Location: app/Http/Requests or app/Validations
Example Controller Usage:
public function store(StoreUserRequest $request)
{
$this->userService->create($request->validated());
}
No ->validate() calls inside controllers or services.
Controllers must:
Controllers = Validation β Service β Standard Response
All controllers related to the same feature must be in a subfolder named after that feature:
app/Http/Controllers/FeatureName/
Services handle:
Controllers must only interact with these services.
Services should:
UserService, PaymentService)Traits are allowed for reusable helper logic across multiple services or models β e.g., logging, formatting, API requests.
Avoid using traits for core business logic β use services instead.
No file should exceed 200 lines of code.
No method should exceed 40 lines.
Use early returns instead of deeply nested if statements.
Follow the Single Responsibility Principle (SRP).
Folder structure must reflect logical boundaries:
app/
βββ Http/
β βββ Controllers/
β β βββ FeatureName/
β βββ Requests/
βββ Services/
βββ Traits/
βββ Models/
βββ Policies/
βββ Helpers/
βββ Enums/
βββ Support/
Naming conventions:
UserController, TransactionControllerUserService, PaymentGatewayServiceUser, TransactionAll controllers should return standardized JSON responses via a Response Helper or Trait.
Example:
return $this->success('User created successfully', $user);
All exceptions must be caught and rethrown as custom exceptions with meaningful messages.
Configure a global exception handler in app/Exceptions/Handler.php.
Use centralized logging for all critical actions.
Avoid using dd() or dump() in production code.
Every Service or Helper should have a corresponding unit or feature test. Use PestPHP for simplicity and consistency. Never deploy untested features to production.
All environment-dependent variables must come from .env β no hardcoded URLs, API keys, or secrets.
Use Laravelβs config/ system for all environment options.
Commit messages must be descriptive:
feat: add user creation via service classfix: handle null response from payment providerEach new Service or Trait must have a PHPDoc block at the top describing its purpose.
Maintain a docs/architecture.md file describing the overall service structure.
Use Dependency Injection wherever possible. Avoid circular dependencies between services. Do not use facades or static calls inside business logic.
All pull requests must pass:
Always use mass assignment protection ($fillable or DTOs).
Never trust client input β validate everything.
Sanitize all external data before persistence.
All API responses must follow a unified structure across the codebase.
{
"status": "success" | "error",
"message": "Human-readable message",
"data": {},
"errors": []
}
{
"status": "success",
"message": "User created successfully",
"data": {
"id": 12,
"name": "John Doe"
}
}
{
"status": "error",
"message": "Validation failed",
"errors": {
"email": ["The email has already been taken."]
}
}
Create a reusable ApiResponseTrait and include it in all controllers:
trait ApiResponseTrait
{
protected function success($message, $data = [], $code = 200)
{
return response()->json([
'status' => 'success',
'message' => $message,
'data' => $data
], $code);
}
protected function error($message, $errors = [], $code = 400)
{
return response()->json([
'status' => 'error',
'message' => $message,
'errors' => $errors
], $code);
}
}
Usage:
return $this->success('User created successfully', $user);