Base.vn Prep

2026-05-19 · Middle-level

PRINCIPLES

Software Engineering Principles

SOLID + YAGNI + KISS + DRY + Clean Code. Mỗi principle: What → When → Code example → When NOT.

SOLID Principles

5 nguyên tắc cho OOP scale-friendly. Áp dụng đa số khi codebase > 1000 LOC + nhiều dev.

S · Single Responsibility

1 class/function = 1 lý do thay đổi. Ví dụ: UserService chỉ handle user, không lo email.

// Bad: UserService làm 3 việc
class UserService {
  createUser() { /* validate, save, send email */ }
}

// Good: tách
class UserService { create() { /* save */ } }
class EmailService { sendWelcome() {} }
class UserValidator { validate() {} }

O · Open/Closed

Open for extension, closed for modification. Thêm feature bằng strategy/composition thay vì sửa code cũ.

// Bad: thêm payment method = sửa switch
function pay(method) {
  if (method === 'visa') {} else if (method === 'paypal') {}
}

// Good: strategy pattern
interface PaymentMethod { pay(amount: number): Promise<void> }
class VisaPayment implements PaymentMethod {}
class PaypalPayment implements PaymentMethod {}
// Thêm Momo? Tạo class mới, không sửa Pay function.

L · Liskov Substitution

Subclass phải dùng được như parent — không break contract.

// Bad: Square extends Rectangle nhưng break setWidth
class Rectangle { setWidth(w) {} setHeight(h) {} }
class Square extends Rectangle {
  setWidth(w) { this.w = w; this.h = w; } // ❌ surprising
}

// Good: composition over inheritance
interface Shape { area(): number }
class Square implements Shape {}
class Rectangle implements Shape {}

I · Interface Segregation

Interface nhỏ > interface bloat. Client không phụ thuộc method không dùng.

// Bad: 1 interface ép mọi worker implement đủ
interface Worker { code(); test(); deploy(); manage(); }

// Good: tách
interface Coder { code() }
interface Tester { test() }
interface Deployer { deploy() }

D · Dependency Inversion

Depend on abstraction, not concretion. Inject dependency thay vì new bên trong.

// Bad: hard couple
class OrderService {
  constructor() { this.db = new PostgresDB(); } // ❌
}

// Good: inject abstraction
interface Database { save() }
class OrderService {
  constructor(private db: Database) {} // ✓
}
// Test: inject MockDB. Prod: PostgresDB. Đổi sang Mongo? Implement Database.

YAGNI — You Aren't Gonna Need It

Rule: Đừng code feature trước khi cần. Chỉ build cái bạn dùng ngay.

// Bad: chuẩn bị "sau này sẽ cần"
class UserService {
  createUser(role, type, level, badge, achievements) {
    // role, type, level, badge, achievements — không ai dùng
  }
}

// Good: build cái đang cần
class UserService {
  createUser(email, name) {} // 6 tháng sau cần role? Add lúc đó.
}

Tại sao quan trọng: Code không dùng = tech debt + bug surface + maintenance cost. Không refactor cho tương lai giả định.

Khi NOT áp dụng: Security/audit log/database schema — KHÔNG YAGNI. Add column sau khó hơn add trước. PII encryption cũng vậy.

KISS — Keep It Simple, Stupid

Rule: Giải pháp đơn giản nhất hoạt động đủ. Cleverness ≠ code chất lượng.

// Bad: clever, khó đọc
const isEven = n => !(n & 1) & !!~~n;

// Good: rõ ràng
const isEven = (n: number) => n % 2 === 0;

// Bad: over-abstract cho 2 case
abstract class AnimalFactoryBuilder { /* 50 lines */ }

// Good: KISS
function createDog() { return { type: 'dog', sound: 'woof' }; }
function createCat() { return { type: 'cat', sound: 'meow' }; }

Tactical: Function < 30 lines · class < 200 lines · cyclomatic complexity < 10 · max 3 nested if. Nếu phải comment "this is complex but..." → simplify thay vì comment.

DRY — Don't Repeat Yourself

Rule: Mỗi piece of knowledge phải có 1 source of truth duy nhất trong system.

// Bad: validate copy paste
function createUser(data) { if (!data.email.includes('@')) throw; /* ... */ }
function updateUser(data) { if (!data.email.includes('@')) throw; /* ... */ }

// Good: extract
const emailSchema = z.string().email();
function createUser(data) { emailSchema.parse(data.email); }
function updateUser(data) { emailSchema.parse(data.email); }

⚠️ Cảnh báo: AHA > DRY

"Avoid Hasty Abstractions." Đừng DRY 2 chỗ giống nhau tình cờ — wait until 3-4 lần để biết pattern thật. Premature DRY tạo coupling khó hơn duplication. Kent C. Dodds — AHA.

Clean Code Essentials

Naming

Intent-revealing. elapsedTimeInDays > d. Verb cho function (getUser), noun cho class/var.

Function size

"Do one thing." Max ~20-30 lines. Nếu > 30 line, split.

Comments

Code tự giải thích > comment. Chỉ comment why, không what. Stale comment tệ hơn không có.

Magic number

Tách thành constant: const MAX_RETRIES = 3 > if (count < 3).

Boolean param

save(user, true) ❌ → saveAndPublish(user) ✓ hoặc options object.

Error handling

Throw early, catch at boundary. Custom error class. Không swallow exception silent.

📚 Đọc thêm

Clean Code — Robert C. Martin · The Pragmatic Programmer — Hunt & Thomas · A Philosophy of Software Design — Ousterhout

← Frontend Next: Cheatsheet →