Use when implementing language-agnostic patterns like layered architecture, dependency injection, error handling, or code organization principles across any technology stack.
Language-agnostic development patterns and best practices applicable across all technology stacks.
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Presentation Layer ā UI, API handlers, CLI
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Application Layer ā Use cases, services
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Domain Layer ā Business logic, entities
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Infrastructure Layer ā DB, cache, external APIs
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
When to Use: Most applications benefit from clear separation of concerns.
āāāāāāāāāāāāāāāāāāā
ā Frameworks ā (outermost)
ā & Drivers ā
āāāāā“āāāāāāāāāāāāāāāāāā“āāāā
ā Interface Adapters ā
ā (Controllers, Gateways)ā
āāāāā“āāāāāāāāāāāāāāāāāāāāāāāāāā“āāāā
ā Application Business ā
ā Rules (Use Cases) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Enterprise Business Rules ā (innermost)
ā (Entities) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Dependency Rule: Dependencies point inward. Inner layers don't know about outer layers.
src/
āāā components/
ā āāā common/ # Shared UI components
ā āāā layout/ # Layout components
ā āāā features/ # Feature-specific components
āāā hooks/ # Custom hooks
āāā stores/ # State management
āāā services/ # API services
āāā utils/ # Utilities
Each module/function should do ONE thing well.
// BAD: Multiple responsibilities
function processUser(user) {
validateUser(user);
saveToDatabase(user);
sendEmail(user);
logAnalytics(user);
}
// GOOD: Single responsibility
function validateUser(user) { /* validation only */ }
function saveUser(user) { /* persistence only */ }
function notifyUser(user) { /* notification only */ }
Inject dependencies rather than creating them internally.
// BAD: Hard dependency
class UserService {
constructor() {
this.db = new Database(); // Hard-coded
}
}
// GOOD: Injected dependency
class UserService {
constructor(db) {
this.db = db; // Injected
}
}
Prefer many specific interfaces over one general interface.
// BAD: Fat interface
interface Worker {
work();
eat();
sleep();
}
// GOOD: Segregated interfaces
interface Workable { work(); }
interface Eatable { eat(); }
interface Sleepable { sleep(); }
Validate inputs early and fail immediately on invalid data.
function processOrder(order) {
// Validate early
if (!order) throw new Error('Order required');
if (!order.items?.length) throw new Error('Order must have items');
if (!order.customerId) throw new Error('Customer ID required');
// Process only after validation passes
return executeOrder(order);
}
Contain errors at appropriate boundaries.
// API boundary - catch and format errors
async function apiHandler(req, res) {
try {
const result = await processRequest(req);
res.json({ success: true, data: result });
} catch (error) {
res.status(error.statusCode || 500).json({
success: false,
error: error.message
});
}
}
Use Result/Either types instead of exceptions for expected failures.
type Result<T, E> = { ok: true; value: T } | { ok: false; error: E };
function parseConfig(input: string): Result<Config, ParseError> {
try {
return { ok: true, value: JSON.parse(input) };
} catch (e) {
return { ok: false, error: new ParseError(e.message) };
}
}
Data flows in one direction through the application.
Action ā Dispatcher ā Store ā View ā Action
Decouple components through events.
// Publisher
eventBus.emit('user.created', { userId: '123' });
// Subscriber
eventBus.on('user.created', async (event) => {
await sendWelcomeEmail(event.userId);
});
Separate commands (mutations) from queries (reads).
// Query - returns data, no side effects
function getUser(id) { return db.users.find(id); }
// Command - mutates data, returns void/status
function updateUser(id, data) { db.users.update(id, data); }
| Type | Convention | Examples |
|---|---|---|
| Actions | verb + noun | createUser, deleteOrder, validateInput |
| Queries | get/find/is/has + noun | getUser, findOrders, isValid, hasPermission |
| Handlers | handle + event | handleClick, handleSubmit, handleError |
| Callbacks | on + event | onSuccess, onError, onChange |
| Type | Convention | Examples |
|---|---|---|
| Booleans | is/has/can/should | isActive, hasAccess, canEdit, shouldRefresh |
| Collections | plural | users, orders, items |
| Counts | count/num/total | userCount, numItems, totalPrice |
| Type | Convention | Examples |
|---|---|---|
| Components | PascalCase | UserProfile.tsx, OrderList.vue |
| Utilities | camelCase/kebab | formatDate.ts, string-utils.ts |
| Constants | SCREAMING_SNAKE | API_ENDPOINTS.ts, ERROR_CODES.ts |
| Tests | name.test/spec | user.test.ts, order.spec.ts |
Before committing code, verify:
Objects that know too much or do too much. Split into focused components.
Don't optimize before measuring. Write clear code first, optimize proven bottlenecks.
Using strings where enums/types would be safer. Use type systems.
Duplicating code instead of abstracting. But: prefer duplication over wrong abstraction.
Functions with boolean flags that change behavior. Split into explicit functions.
// BAD
function process(data, isAdmin) { /* behaves differently based on flag */ }
// GOOD
function processUserData(data) { /* user logic */ }
function processAdminData(data) { /* admin logic */ }
Universal patterns applicable to all technology stacks