Master software architecture patterns, clean code principles, and production-ready development practices
// Bad
const d = new Date();
const x = getUserData();
// Good
const currentDate = new Date();
const activeUser = getUserData();
Each function/class should do one thing well.
// Bad - doing too much
function processUser(user) {
validateUser(user);
saveToDatabase(user);
sendWelcomeEmail(user);
logActivity(user);
}
// Good - separated concerns
function createUser(user) {
const validated = validateUser(user);
const saved = await userRepository.save(validated);
await emailService.sendWelcome(saved);
await activityLogger.log('user_created', saved.id);
return saved;
}
// Bad - repetition
const userFullName = user.firstName + ' ' + user.lastName;
const adminFullName = admin.firstName + ' ' + admin.lastName;
// Good - reusable function
function getFullName(person) {
return `${person.firstName} ${person.lastName} `;
}
Functions should be 20 lines or less, do one thing, and have clear names.
// Use try-catch for async operations
async function fetchUserData(id) {
try {
const user = await api.getUser(id);
return user;
} catch (error) {
logger.error('Failed to fetch user', { id, error });
throw new UserNotFoundError(id);
}
}
Separate data, presentation, and logic.
// Model - Data layer
class UserModel {
async findById(id) { /* database query */ }
async create(data) { /* insert */ }
}
// Controller - Business logic
class UserController {
async getUser(req, res) {
const user = await UserModel.findById(req.params.id);
res.json(user);
}
}
// View - Presentation (React component)
function UserProfile({ user }) {
return <div> {user.name} </div> ;
}
Abstract data access logic.
class UserRepository {
constructor(database) {
this.db = database;
}
async findAll() {
return this.db.query('SELECT * FROM users');
}
async findById(id) {
return this.db.query('SELECT * FROM users WHERE id = ?', [id]);
}
async create(user) {
return this.db.query('INSERT INTO users SET ?', user);
}
}
Pass dependencies instead of creating them internally.
// Bad - tight coupling
class UserService {
constructor() {
this.repo = new UserRepository();
}
}
// Good - dependency injection
class UserService {
constructor(userRepository) {
this.repo = userRepository;
}
}
// Usage
const repo = new UserRepository(database);
const service = new UserService(repo);
Manage multiple packages in a single repository for better code sharing and consistency.
# Project structure
my-monorepo/
├── apps/
│ ├── web/ # Next.js frontend
│ ├── api/ # Express backend
│ └── admin/ # Admin dashboard
├── packages/
│ ├── ui/ # Shared UI components
│ ├── config/ # Shared configs
│ ├── types/ # Shared TypeScript types
│ └── utils/ # Shared utilities
├── turbo.json
└── package.json
# turbo.json
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "dist/**"]
},
"dev": {
"cache": false
}
}
}
const redis = require('redis');
const client = redis.createClient();
async function getCachedUser(id) {
// Try cache first
const cached = await client.get(`user:${id} `);
if (cached) return JSON.parse(cached);
// Fetch from database
const user = await db.users.findById(id);
// Cache for 1 hour
await client.setEx(`user:${id} `, 3600, JSON.stringify(user));
return user;
}
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.isOperational = true;
}
}
class NotFoundError extends AppError {
constructor(resource) {
super(`${resource} not found`, 404);
}
}
class ValidationError extends AppError {
constructor(message) {
super(message, 400);
}
}
app.use((err, req, res, next) => {
// Log error
logger.error(err);
// Send to error tracking (Sentry)
if (process.env.NODE_ENV =>= 'production') {
Sentry.captureException(err);
}
// Send response
res.status(err.statusCode || 500).json({
error: {
message: err.message,
...(process.env.NODE_ENV =>= 'development' && { stack: err.stack })
}
});
});
const winston = require('winston');
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
// Usage
logger.info('User logged in', { userId: user.id });
logger.error('Database connection failed', { error });
logger.warn('Rate limit exceeded', { ip: req.ip });
You've learned production-ready architecture and best practices:
Next: Build a complete SaaS application in Module 7!