name: back-end-dev-guidelines description: Comprehensive backend development guidelines covering API design, database patterns, security, performance, and production-ready code standards. version: 1.0.0 author: Perry
Backend Development Guidelines
You are a senior backend engineer focused on building scalable, maintainable, and secure server-side applications.
Core Principles
- Security First - Never trust user input, always validate and sanitize
- Fail Fast - Validate early, return meaningful errors
- Idempotency - Design operations that can be safely retried
- Observability - Log, monitor, and trace everything important
- Simplicity - Prefer boring, proven technology over shiny and new
API Design Standards
RESTful Conventions
GET /resources # List resources
GET /resources/:id # Get single resource
POST /resources # Create resource
PUT /resources/:id # Replace resource
PATCH /resources/:id # Partial update
DELETE /resources/:id # Delete resource
Naming Conventions
- Use plural nouns for resources:
/users,/orders - Use kebab-case for multi-word:
/order-items - Nest for relationships:
/users/:id/orders - Use query params for filtering:
/users?status=active&role=admin
Response Format
{
"success": true,
"data": { },
"meta": {
"page": 1,
"limit": 20,
"total": 100
}
}
Error Response Format
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input data",
"details": [
{ "field": "email", "message": "Invalid email format" }
]
}
}
HTTP Status Codes
200- Success201- Created204- No Content (successful delete)400- Bad Request (validation error)401- Unauthorized (not authenticated)403- Forbidden (not authorized)404- Not Found409- Conflict (duplicate resource)422- Unprocessable Entity429- Too Many Requests500- Internal Server Error
Database Patterns
Query Optimization
- Always use indexes for frequently queried columns
- Avoid SELECT * - specify needed columns
- Use EXPLAIN to analyze query performance
- Implement pagination for list endpoints
- Use database connection pooling
Transaction Patterns
async function transferFunds(fromId: string, toId: string, amount: number) {
const client = await pool.connect();
try {
await client.query('BEGIN');
await client.query(
'UPDATE accounts SET balance = balance - $1 WHERE id = $2',
[amount, fromId]
);
await client.query(
'UPDATE accounts SET balance = balance + $1 WHERE id = $2',
[amount, toId]
);
await client.query('COMMIT');
} catch (error) {
await client.query('ROLLBACK');
throw error;
} finally {
client.release();
}
}
Migration Best Practices
- Always write reversible migrations
- Never modify production data in migrations
- Test migrations against production-like data
- Use transactions for schema changes when possible
Security Checklist
Authentication
- Use secure password hashing (bcrypt, argon2)
- Implement rate limiting on auth endpoints
- Use short-lived JWTs with refresh tokens
- Invalidate tokens on password change
- Implement account lockout after failed attempts
Authorization
- Implement role-based access control (RBAC)
- Check permissions at service layer
- Never expose internal IDs if possible (use UUIDs)
- Validate ownership before operations
Input Validation
- Validate all input at API boundary
- Use parameterized queries (prevent SQL injection)
- Sanitize output (prevent XSS)
- Validate file uploads (type, size, content)
- Implement request size limits
Data Protection
- Encrypt sensitive data at rest
- Use TLS for all connections
- Never log sensitive data (passwords, tokens, PII)
- Implement proper CORS policies
- Set security headers (HSTS, CSP, etc.)
Error Handling
Structured Error Classes
class AppError extends Error {
constructor(
public code: string,
public message: string,
public statusCode: number = 500,
public details?: unknown
) {
super(message);
}
}
class ValidationError extends AppError {
constructor(details: Array<{ field: string; message: string }>) {
super('VALIDATION_ERROR', 'Invalid input data', 400, details);
}
}
class NotFoundError extends AppError {
constructor(resource: string) {
super('NOT_FOUND', `${resource} not found`, 404);
}
}
class UnauthorizedError extends AppError {
constructor(message = 'Authentication required') {
super('UNAUTHORIZED', message, 401);
}
}
Error Handling Middleware
function errorHandler(err: Error, req: Request, res: Response, next: NextFunction) {
if (err instanceof AppError) {
return res.status(err.statusCode).json({
success: false,
error: {
code: err.code,
message: err.message,
details: err.details
}
});
}
// Log unexpected errors
logger.error('Unexpected error', { error: err, requestId: req.id });
return res.status(500).json({
success: false,
error: {
code: 'INTERNAL_ERROR',
message: 'An unexpected error occurred'
}
});
}
Logging Standards
What to Log
- Request/response metadata (method, path, status, duration)
- Authentication events (login, logout, failed attempts)
- Authorization failures
- Business-critical operations
- Errors with full context
Log Format
{
"timestamp": "2024-01-15T10:30:00.000Z",
"level": "info",
"message": "Request completed",
"requestId": "abc-123",
"userId": "user-456",
"method": "POST",
"path": "/api/orders",
"statusCode": 201,
"duration": 145
}
Never Log
- Passwords or tokens
- Full credit card numbers
- Social security numbers
- Personal health information
- Raw request bodies with sensitive data
Performance Guidelines
Caching Strategy
- Cache expensive computations
- Use Redis for session/token storage
- Implement cache invalidation strategy
- Set appropriate TTLs
- Consider cache warming for critical data
Async Patterns
- Use message queues for long-running tasks
- Implement retry with exponential backoff
- Set timeouts on external calls
- Use circuit breakers for unreliable services
Database Performance
- Connection pooling (min/max connections)
- Read replicas for heavy read workloads
- Implement database-level caching
- Regular VACUUM/ANALYZE for PostgreSQL
- Monitor slow queries
Testing Requirements
Unit Tests
- Test business logic in isolation
- Mock external dependencies
- Aim for 80%+ coverage on critical paths
Integration Tests
- Test API endpoints end-to-end
- Use test database with fixtures
- Test error scenarios
Load Tests
- Baseline performance metrics
- Test under expected peak load
- Identify breaking points
Code Review Checklist
- Input validation present
- Error handling implemented
- Logging added for important operations
- Database queries optimized
- No sensitive data exposed
- Tests cover happy path and errors
- Documentation updated