feat: biblioteca inteligente libs/ + 5 novas skills (20 skills total)
NOVAS SKILLS: - next-best-practices v0.1.0 (CLEAN) — Next.js App Router, RSC, caching, data - nextjs-patterns v1.0.0 (CLEAN) — Next.js 15: Server Actions, route handlers - vite v1.0.0 (CLEAN) — env vars, aliases, proxy, CJS compat - uncle-bob v1.0.0 (CLEAN) — Clean Code, SOLID, Clean Architecture - clean-code-review v1.0.0 (CLEAN) — naming, guard clauses, anti-patterns, refactoring - vue v1.0.0 (CLEAN) — Vue framework - vue-composition-api-best-practices v1.0.0 (CLEAN) — composables, Pinia, reactivity BIBLIOTECA INTELIGENTE libs/ (10 dominios, 11 arquivos): - typescript/ — TS safe + generics gotchas - react/ — Next.js App Router + Vite config - vue/ — Composition API + Pinia - linux/ — System diagnostic cheatsheet - database/ — PostgreSQL + MySQL patterns - browser/ — Chromium CLI + E2E testing - security/ — SAST audit (OWASP Top 10) - best-practices/ — Clean Code + SOLID + Clean Architecture - deploy/ — Docker multi-stack + OpenClaw ops - + INDEX.md como guia de navegacao .learnings/ — LRN-20260519-003 criado (biblioteca compartilhada)
This commit is contained in:
@@ -0,0 +1,961 @@
|
||||
# Anti-Patterns Gallery
|
||||
|
||||
Common coding mistakes with explanations and fixes. Each pattern includes bad/good examples to make code review and refactoring actionable.
|
||||
|
||||
---
|
||||
|
||||
## Naming Anti-Patterns
|
||||
|
||||
### 1. Cryptic Abbreviations
|
||||
|
||||
**Problem**: Abbreviations save keystrokes but cost readability. Future readers (including yourself) won't remember what `usrMgr` means.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad
|
||||
const usrMgr = new UsrMgr();
|
||||
const cfg = loadCfg();
|
||||
const btn = document.getElementById('sbmt');
|
||||
const val = calc(x, y, z);
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good
|
||||
const userManager = new UserManager();
|
||||
const config = loadConfig();
|
||||
const submitButton = document.getElementById('submit');
|
||||
const totalPrice = calculateTotalPrice(quantity, unitPrice, taxRate);
|
||||
```
|
||||
|
||||
**Rule**: Spell it out. IDE autocomplete makes long names free.
|
||||
|
||||
---
|
||||
|
||||
### 2. Generic Names
|
||||
|
||||
**Problem**: Names like `data`, `info`, `item`, `thing`, `manager`, `handler` tell you nothing about what the code does.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad
|
||||
function processData(data: any) {
|
||||
const info = getData();
|
||||
const result = handle(info);
|
||||
return result;
|
||||
}
|
||||
|
||||
class Manager {
|
||||
items: any[] = [];
|
||||
process() { /* ... */ }
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good
|
||||
function validateUserRegistration(registration: UserRegistration) {
|
||||
const existingUser = findUserByEmail(registration.email);
|
||||
const validationResult = checkEmailAvailability(existingUser);
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
class ShoppingCart {
|
||||
lineItems: CartLineItem[] = [];
|
||||
calculateTotal() { /* ... */ }
|
||||
}
|
||||
```
|
||||
|
||||
**Rule**: Names should reveal intent. Ask "what does this actually do?"
|
||||
|
||||
---
|
||||
|
||||
### 3. Misleading Names
|
||||
|
||||
**Problem**: Names that lie are worse than bad names. They actively deceive readers.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad - name lies about what it does
|
||||
function getUserList() {
|
||||
// Actually returns a single user, not a list
|
||||
return this.currentUser;
|
||||
}
|
||||
|
||||
const isValid = checkDate(date); // Returns the date, not a boolean
|
||||
|
||||
class AccountList extends Map { } // It's a Map, not a List
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good - names match behavior
|
||||
function getCurrentUser() {
|
||||
return this.currentUser;
|
||||
}
|
||||
|
||||
const normalizedDate = normalizeDate(date);
|
||||
|
||||
class AccountRegistry extends Map { }
|
||||
```
|
||||
|
||||
**Rule**: If the name doesn't match the behavior, change the name (or the behavior).
|
||||
|
||||
---
|
||||
|
||||
### 4. Hungarian Notation
|
||||
|
||||
**Problem**: Encoding types in names was useful in weakly-typed languages. TypeScript makes it redundant and noisy.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad
|
||||
const strName: string = 'Alice';
|
||||
const nCount: number = 42;
|
||||
const arrUsers: User[] = [];
|
||||
const bIsActive: boolean = true;
|
||||
interface IUser { } // "I" prefix for interfaces
|
||||
type TUserRole = 'admin' | 'user'; // "T" prefix for types
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good
|
||||
const name: string = 'Alice';
|
||||
const count: number = 42;
|
||||
const users: User[] = [];
|
||||
const isActive: boolean = true;
|
||||
interface User { }
|
||||
type UserRole = 'admin' | 'user';
|
||||
```
|
||||
|
||||
**Rule**: Let the type system handle types. Names should describe purpose.
|
||||
|
||||
---
|
||||
|
||||
## Function Anti-Patterns
|
||||
|
||||
### 5. God Functions
|
||||
|
||||
**Problem**: Functions over 20 lines are hard to understand, test, and modify. They usually do too many things.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad - 50+ line function doing everything
|
||||
async function processOrder(order: Order) {
|
||||
// Validate order
|
||||
if (!order.items.length) throw new Error('Empty order');
|
||||
if (!order.customer) throw new Error('No customer');
|
||||
// ... 10 more validation lines
|
||||
|
||||
// Calculate totals
|
||||
let subtotal = 0;
|
||||
for (const item of order.items) {
|
||||
subtotal += item.price * item.quantity;
|
||||
// ... discount logic
|
||||
}
|
||||
// ... 15 more calculation lines
|
||||
|
||||
// Process payment
|
||||
const paymentResult = await stripe.charge(/* ... */);
|
||||
// ... 10 more payment lines
|
||||
|
||||
// Send notifications
|
||||
await sendEmail(/* ... */);
|
||||
await sendSMS(/* ... */);
|
||||
// ... more notification logic
|
||||
|
||||
// Update inventory
|
||||
// ... 10 more lines
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good - composed of small, focused functions
|
||||
async function processOrder(order: Order) {
|
||||
validateOrder(order);
|
||||
const totals = calculateOrderTotals(order);
|
||||
const payment = await processPayment(order.customer, totals);
|
||||
await sendOrderConfirmation(order, payment);
|
||||
await updateInventory(order.items);
|
||||
return { success: true, orderId: payment.orderId };
|
||||
}
|
||||
|
||||
function validateOrder(order: Order): void {
|
||||
if (!order.items.length) throw new Error('Empty order');
|
||||
if (!order.customer) throw new Error('No customer');
|
||||
}
|
||||
|
||||
function calculateOrderTotals(order: Order): OrderTotals {
|
||||
const subtotal = order.items.reduce(
|
||||
(sum, item) => sum + item.price * item.quantity, 0
|
||||
);
|
||||
return { subtotal, tax: subtotal * 0.1, total: subtotal * 1.1 };
|
||||
}
|
||||
```
|
||||
|
||||
**Rule**: Extract until each function does exactly one thing.
|
||||
|
||||
---
|
||||
|
||||
### 6. Too Many Parameters
|
||||
|
||||
**Problem**: Functions with 4+ parameters are hard to call correctly and often indicate the function does too much.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad - too many parameters, order matters
|
||||
function createUser(
|
||||
firstName: string,
|
||||
lastName: string,
|
||||
email: string,
|
||||
password: string,
|
||||
role: string,
|
||||
department: string,
|
||||
startDate: Date,
|
||||
managerId: string | null,
|
||||
isActive: boolean
|
||||
) {
|
||||
// ...
|
||||
}
|
||||
|
||||
// Callers must remember order
|
||||
createUser('John', 'Doe', 'john@example.com', 'secret', 'admin', 'Engineering', new Date(), null, true);
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good - use an options object
|
||||
interface CreateUserOptions {
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
email: string;
|
||||
password: string;
|
||||
role: UserRole;
|
||||
department: string;
|
||||
startDate?: Date;
|
||||
managerId?: string;
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
function createUser(options: CreateUserOptions) {
|
||||
const { firstName, lastName, email, role, isActive = true } = options;
|
||||
// ...
|
||||
}
|
||||
|
||||
// Callers have self-documenting code
|
||||
createUser({
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
email: 'john@example.com',
|
||||
password: 'secret',
|
||||
role: 'admin',
|
||||
department: 'Engineering',
|
||||
});
|
||||
```
|
||||
|
||||
**Rule**: More than 3 parameters? Use an options object.
|
||||
|
||||
---
|
||||
|
||||
### 7. Boolean Flag Parameters
|
||||
|
||||
**Problem**: Boolean parameters hide branching logic and make function calls unreadable.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad - what does `true` mean here?
|
||||
renderButton('Submit', true, false, true);
|
||||
|
||||
function renderButton(
|
||||
label: string,
|
||||
isPrimary: boolean,
|
||||
isDisabled: boolean,
|
||||
isLoading: boolean
|
||||
) {
|
||||
// Complex branching based on booleans
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good - use options object or separate functions
|
||||
renderButton({
|
||||
label: 'Submit',
|
||||
variant: 'primary',
|
||||
state: 'loading',
|
||||
});
|
||||
|
||||
// Or separate functions for distinct behaviors
|
||||
renderPrimaryButton('Submit');
|
||||
renderLoadingButton('Submit');
|
||||
|
||||
// Or use enums
|
||||
type ButtonVariant = 'primary' | 'secondary' | 'ghost';
|
||||
type ButtonState = 'default' | 'loading' | 'disabled';
|
||||
```
|
||||
|
||||
**Rule**: Boolean parameters should be in an options object with named properties.
|
||||
|
||||
---
|
||||
|
||||
### 8. Side Effects in Getters
|
||||
|
||||
**Problem**: Getters that modify state violate the principle of least surprise. Readers expect getters to be pure.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad - getter with hidden side effect
|
||||
class ShoppingCart {
|
||||
private _items: CartItem[] = [];
|
||||
private _lastAccessed: Date;
|
||||
|
||||
get items() {
|
||||
this._lastAccessed = new Date(); // Side effect!
|
||||
this.logAccess(); // Another side effect!
|
||||
return this._items;
|
||||
}
|
||||
|
||||
get totalPrice() {
|
||||
this.recalculateDiscounts(); // Mutation!
|
||||
return this._items.reduce((sum, i) => sum + i.price, 0);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good - getters are pure, side effects are explicit
|
||||
class ShoppingCart {
|
||||
private _items: CartItem[] = [];
|
||||
private _lastAccessed: Date;
|
||||
|
||||
get items() {
|
||||
return this._items;
|
||||
}
|
||||
|
||||
get totalPrice() {
|
||||
return this._items.reduce((sum, i) => sum + i.price, 0);
|
||||
}
|
||||
|
||||
recordAccess() {
|
||||
this._lastAccessed = new Date();
|
||||
this.logAccess();
|
||||
}
|
||||
|
||||
applyDiscounts() {
|
||||
this.recalculateDiscounts();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Rule**: Getters should be pure. Make side effects explicit with verbs.
|
||||
|
||||
---
|
||||
|
||||
### 9. Returning Different Types
|
||||
|
||||
**Problem**: Functions that return different types based on conditions make code unpredictable and hard to type.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad - return type depends on runtime condition
|
||||
function getUser(id: string) {
|
||||
const user = database.find(id);
|
||||
if (!user) {
|
||||
return false; // boolean
|
||||
}
|
||||
if (user.isDeleted) {
|
||||
return null; // null
|
||||
}
|
||||
return user; // User object
|
||||
}
|
||||
|
||||
// Caller must handle all cases
|
||||
const result = getUser('123');
|
||||
if (result === false) { /* not found */ }
|
||||
else if (result === null) { /* deleted */ }
|
||||
else { /* use result.name */ }
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good - consistent return type with discriminated union
|
||||
type GetUserResult =
|
||||
| { status: 'found'; user: User }
|
||||
| { status: 'not_found' }
|
||||
| { status: 'deleted' };
|
||||
|
||||
function getUser(id: string): GetUserResult {
|
||||
const user = database.find(id);
|
||||
if (!user) {
|
||||
return { status: 'not_found' };
|
||||
}
|
||||
if (user.isDeleted) {
|
||||
return { status: 'deleted' };
|
||||
}
|
||||
return { status: 'found', user };
|
||||
}
|
||||
|
||||
// Caller has type-safe handling
|
||||
const result = getUser('123');
|
||||
if (result.status === 'found') {
|
||||
console.log(result.user.name); // TypeScript knows user exists
|
||||
}
|
||||
```
|
||||
|
||||
**Rule**: Return consistent types. Use discriminated unions for multiple outcomes.
|
||||
|
||||
---
|
||||
|
||||
## Structure Anti-Patterns
|
||||
|
||||
### 10. Deep Nesting (Pyramid of Doom)
|
||||
|
||||
**Problem**: Deeply nested code is hard to follow and usually indicates missing abstractions.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad - 5 levels deep
|
||||
function processOrder(order: Order) {
|
||||
if (order) {
|
||||
if (order.items.length > 0) {
|
||||
if (order.customer) {
|
||||
if (order.customer.isVerified) {
|
||||
if (order.paymentMethod) {
|
||||
// Finally, the actual logic buried 5 levels deep
|
||||
return submitOrder(order);
|
||||
} else {
|
||||
throw new Error('No payment method');
|
||||
}
|
||||
} else {
|
||||
throw new Error('Customer not verified');
|
||||
}
|
||||
} else {
|
||||
throw new Error('No customer');
|
||||
}
|
||||
} else {
|
||||
throw new Error('No items');
|
||||
}
|
||||
} else {
|
||||
throw new Error('No order');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good - guard clauses flatten the structure
|
||||
function processOrder(order: Order) {
|
||||
if (!order) throw new Error('No order');
|
||||
if (!order.items.length) throw new Error('No items');
|
||||
if (!order.customer) throw new Error('No customer');
|
||||
if (!order.customer.isVerified) throw new Error('Customer not verified');
|
||||
if (!order.paymentMethod) throw new Error('No payment method');
|
||||
|
||||
return submitOrder(order);
|
||||
}
|
||||
```
|
||||
|
||||
**Rule**: Use guard clauses for early returns. Max 2 levels of nesting.
|
||||
|
||||
---
|
||||
|
||||
### 11. Premature Abstraction
|
||||
|
||||
**Problem**: Creating abstractions before you understand the problem leads to wrong abstractions that are hard to change.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad - abstraction created for one use case
|
||||
interface DataFetcher<T> {
|
||||
fetch(): Promise<T>;
|
||||
cache(): void;
|
||||
invalidate(): void;
|
||||
}
|
||||
|
||||
class GenericRepository<T> implements DataFetcher<T> {
|
||||
constructor(private adapter: StorageAdapter<T>) {}
|
||||
// ... complex implementation
|
||||
}
|
||||
|
||||
// Used exactly once:
|
||||
const userRepo = new GenericRepository(new UserAdapter());
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good - start simple, abstract when patterns emerge
|
||||
// First implementation: just fetch users
|
||||
async function fetchUsers(): Promise<User[]> {
|
||||
return await db.query('SELECT * FROM users');
|
||||
}
|
||||
|
||||
// Later, if you need caching, add it:
|
||||
async function fetchUsersWithCache(): Promise<User[]> {
|
||||
const cached = cache.get('users');
|
||||
if (cached) return cached;
|
||||
|
||||
const users = await db.query('SELECT * FROM users');
|
||||
cache.set('users', users);
|
||||
return users;
|
||||
}
|
||||
|
||||
// Abstract only when you see the SAME pattern 3+ times
|
||||
```
|
||||
|
||||
**Rule**: "Duplication is far cheaper than the wrong abstraction." - Sandi Metz
|
||||
|
||||
---
|
||||
|
||||
### 12. Over-Engineering
|
||||
|
||||
**Problem**: Building for hypothetical future requirements adds complexity without value.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad - enterprise FizzBuzz
|
||||
interface FizzBuzzStrategy {
|
||||
applies(n: number): boolean;
|
||||
execute(n: number): string;
|
||||
}
|
||||
|
||||
class FizzStrategy implements FizzBuzzStrategy {
|
||||
applies(n: number) { return n % 3 === 0; }
|
||||
execute() { return 'Fizz'; }
|
||||
}
|
||||
|
||||
class BuzzStrategy implements FizzBuzzStrategy {
|
||||
applies(n: number) { return n % 5 === 0; }
|
||||
execute() { return 'Buzz'; }
|
||||
}
|
||||
|
||||
class FizzBuzzProcessor {
|
||||
constructor(private strategies: FizzBuzzStrategy[]) {}
|
||||
process(n: number): string {
|
||||
return this.strategies
|
||||
.filter(s => s.applies(n))
|
||||
.map(s => s.execute(n))
|
||||
.join('') || String(n);
|
||||
}
|
||||
}
|
||||
|
||||
const processor = new FizzBuzzProcessor([
|
||||
new FizzStrategy(),
|
||||
new BuzzStrategy(),
|
||||
]);
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good - solve the actual problem
|
||||
function fizzBuzz(n: number): string {
|
||||
if (n % 15 === 0) return 'FizzBuzz';
|
||||
if (n % 3 === 0) return 'Fizz';
|
||||
if (n % 5 === 0) return 'Buzz';
|
||||
return String(n);
|
||||
}
|
||||
```
|
||||
|
||||
**Rule**: Solve today's problem. Refactor when requirements actually change.
|
||||
|
||||
---
|
||||
|
||||
### 13. Copy-Paste Programming
|
||||
|
||||
**Problem**: Duplicated code means duplicated bugs and maintenance burden.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad - same validation logic copied
|
||||
function createUser(data: UserInput) {
|
||||
if (!data.email) throw new Error('Email required');
|
||||
if (!data.email.includes('@')) throw new Error('Invalid email');
|
||||
if (data.email.length > 255) throw new Error('Email too long');
|
||||
// ... create user
|
||||
}
|
||||
|
||||
function updateUser(id: string, data: UserInput) {
|
||||
if (!data.email) throw new Error('Email required');
|
||||
if (!data.email.includes('@')) throw new Error('Invalid email');
|
||||
if (data.email.length > 255) throw new Error('Email too long');
|
||||
// ... update user
|
||||
}
|
||||
|
||||
function inviteUser(data: UserInput) {
|
||||
if (!data.email) throw new Error('Email required');
|
||||
if (!data.email.includes('@')) throw new Error('Invalid email');
|
||||
if (data.email.length > 255) throw new Error('Email too long');
|
||||
// ... invite user
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good - extract shared logic
|
||||
function validateEmail(email: string): void {
|
||||
if (!email) throw new Error('Email required');
|
||||
if (!email.includes('@')) throw new Error('Invalid email');
|
||||
if (email.length > 255) throw new Error('Email too long');
|
||||
}
|
||||
|
||||
function createUser(data: UserInput) {
|
||||
validateEmail(data.email);
|
||||
// ... create user
|
||||
}
|
||||
|
||||
function updateUser(id: string, data: UserInput) {
|
||||
validateEmail(data.email);
|
||||
// ... update user
|
||||
}
|
||||
|
||||
function inviteUser(data: UserInput) {
|
||||
validateEmail(data.email);
|
||||
// ... invite user
|
||||
}
|
||||
```
|
||||
|
||||
**Rule**: If you copy-paste, you're probably missing an abstraction.
|
||||
|
||||
---
|
||||
|
||||
### 14. God Objects
|
||||
|
||||
**Problem**: Classes that know too much and do too much become unmaintainable.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad - class does everything
|
||||
class ApplicationManager {
|
||||
users: User[] = [];
|
||||
orders: Order[] = [];
|
||||
products: Product[] = [];
|
||||
|
||||
// User operations
|
||||
createUser() { /* ... */ }
|
||||
deleteUser() { /* ... */ }
|
||||
authenticateUser() { /* ... */ }
|
||||
|
||||
// Order operations
|
||||
createOrder() { /* ... */ }
|
||||
cancelOrder() { /* ... */ }
|
||||
refundOrder() { /* ... */ }
|
||||
|
||||
// Product operations
|
||||
addProduct() { /* ... */ }
|
||||
updateInventory() { /* ... */ }
|
||||
|
||||
// Reporting
|
||||
generateSalesReport() { /* ... */ }
|
||||
generateUserReport() { /* ... */ }
|
||||
|
||||
// Notifications
|
||||
sendEmail() { /* ... */ }
|
||||
sendSMS() { /* ... */ }
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good - separate concerns
|
||||
class UserService {
|
||||
createUser() { /* ... */ }
|
||||
deleteUser() { /* ... */ }
|
||||
}
|
||||
|
||||
class AuthService {
|
||||
authenticate() { /* ... */ }
|
||||
}
|
||||
|
||||
class OrderService {
|
||||
createOrder() { /* ... */ }
|
||||
cancelOrder() { /* ... */ }
|
||||
}
|
||||
|
||||
class NotificationService {
|
||||
sendEmail() { /* ... */ }
|
||||
sendSMS() { /* ... */ }
|
||||
}
|
||||
```
|
||||
|
||||
**Rule**: Each class should have one reason to change.
|
||||
|
||||
---
|
||||
|
||||
## Comment Anti-Patterns
|
||||
|
||||
### 15. Commented-Out Code
|
||||
|
||||
**Problem**: Commented code is dead code. It confuses readers and never gets cleaned up.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad
|
||||
function calculateTotal(items: Item[]) {
|
||||
let total = 0;
|
||||
for (const item of items) {
|
||||
total += item.price;
|
||||
// total += item.price * item.quantity; // Old calculation
|
||||
// if (item.discount) {
|
||||
// total -= item.discount;
|
||||
// }
|
||||
}
|
||||
// return total * 1.1; // With tax
|
||||
// return total * 1.08; // Old tax rate
|
||||
return total;
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good - delete it, git remembers
|
||||
function calculateTotal(items: Item[]) {
|
||||
return items.reduce((total, item) => total + item.price, 0);
|
||||
}
|
||||
```
|
||||
|
||||
**Rule**: Delete commented code. Use version control for history.
|
||||
|
||||
---
|
||||
|
||||
### 16. Obvious Comments
|
||||
|
||||
**Problem**: Comments that restate the code add noise without value.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad - comments that add nothing
|
||||
// Increment counter
|
||||
counter++;
|
||||
|
||||
// Check if user is null
|
||||
if (user === null) {
|
||||
// Return early
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop through all items
|
||||
for (const item of items) {
|
||||
// Add item price to total
|
||||
total += item.price;
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good - code is self-documenting, comments explain WHY
|
||||
counter++; // No comment needed
|
||||
|
||||
if (!user) return;
|
||||
|
||||
const total = items.reduce((sum, item) => sum + item.price, 0);
|
||||
|
||||
// Business rule: Premium members get early access 48 hours before launch
|
||||
if (user.isPremium && hoursUntilLaunch < 48) {
|
||||
showEarlyAccess();
|
||||
}
|
||||
```
|
||||
|
||||
**Rule**: Don't comment WHAT, comment WHY (if not obvious).
|
||||
|
||||
---
|
||||
|
||||
### 17. TODO Sprawl
|
||||
|
||||
**Problem**: TODOs accumulate and never get done. They become invisible noise.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad - TODO graveyard
|
||||
function processPayment(amount: number) {
|
||||
// TODO: Add retry logic
|
||||
// TODO: Handle currency conversion
|
||||
// TODO: Add logging
|
||||
// TODO: Optimize this (added 2019)
|
||||
// FIXME: This is broken sometimes
|
||||
// HACK: Temporary fix, remove later
|
||||
// XXX: Why does this work?
|
||||
return charge(amount);
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good - TODOs are tracked in issues, not code
|
||||
function processPayment(amount: number) {
|
||||
// See JIRA-1234 for planned retry logic
|
||||
return charge(amount);
|
||||
}
|
||||
|
||||
// Or just fix it now:
|
||||
async function processPayment(amount: number) {
|
||||
return await retry(() => charge(amount), { attempts: 3 });
|
||||
}
|
||||
```
|
||||
|
||||
**Rule**: TODOs belong in your issue tracker, not your code.
|
||||
|
||||
---
|
||||
|
||||
### 18. Outdated Comments
|
||||
|
||||
**Problem**: Comments that contradict the code are actively harmful.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad - comment lies about the code
|
||||
// Returns the user's full name (first + last)
|
||||
function getUserName(user: User): string {
|
||||
return user.email; // Actually returns email!
|
||||
}
|
||||
|
||||
// Validates and saves the user
|
||||
function processUser(user: User) {
|
||||
// No validation, just saves
|
||||
database.save(user);
|
||||
}
|
||||
|
||||
// This function is deprecated, use newFunction() instead
|
||||
function oldFunction() {
|
||||
// Still actively used throughout codebase
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good - update or remove outdated comments
|
||||
function getUserEmail(user: User): string {
|
||||
return user.email;
|
||||
}
|
||||
|
||||
function saveUser(user: User) {
|
||||
database.save(user);
|
||||
}
|
||||
|
||||
/** @deprecated Use {@link newFunction} instead */
|
||||
function oldFunction() {
|
||||
console.warn('oldFunction is deprecated');
|
||||
return newFunction();
|
||||
}
|
||||
```
|
||||
|
||||
**Rule**: When you change code, update or delete related comments.
|
||||
|
||||
---
|
||||
|
||||
## Control Flow Anti-Patterns
|
||||
|
||||
### 19. Exception-Driven Control Flow
|
||||
|
||||
**Problem**: Using exceptions for normal control flow is slow and hard to follow.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad - exceptions for expected cases
|
||||
function findUser(id: string): User {
|
||||
try {
|
||||
return database.getUser(id);
|
||||
} catch {
|
||||
try {
|
||||
return cache.getUser(id);
|
||||
} catch {
|
||||
try {
|
||||
return createDefaultUser(id);
|
||||
} catch {
|
||||
throw new Error('Cannot get user');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good - explicit control flow
|
||||
function findUser(id: string): User | null {
|
||||
const dbUser = database.getUser(id);
|
||||
if (dbUser) return dbUser;
|
||||
|
||||
const cachedUser = cache.getUser(id);
|
||||
if (cachedUser) return cachedUser;
|
||||
|
||||
return createDefaultUser(id);
|
||||
}
|
||||
```
|
||||
|
||||
**Rule**: Exceptions are for exceptional situations, not control flow.
|
||||
|
||||
---
|
||||
|
||||
### 20. Stringly-Typed Code
|
||||
|
||||
**Problem**: Using strings where enums or types would be safer leads to typos and missing cases.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad - magic strings everywhere
|
||||
function handleStatus(status: string) {
|
||||
if (status === 'pending') { /* ... */ }
|
||||
else if (status === 'Pending') { /* ... */ } // Typo variant
|
||||
else if (status === 'active') { /* ... */ }
|
||||
else if (status === 'actve') { /* ... */ } // Typo!
|
||||
}
|
||||
|
||||
user.role = 'admni'; // Typo, no error!
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good - type-safe enums or unions
|
||||
type Status = 'pending' | 'active' | 'completed' | 'cancelled';
|
||||
type UserRole = 'admin' | 'user' | 'guest';
|
||||
|
||||
function handleStatus(status: Status) {
|
||||
switch (status) {
|
||||
case 'pending': /* ... */ break;
|
||||
case 'active': /* ... */ break;
|
||||
case 'completed': /* ... */ break;
|
||||
case 'cancelled': /* ... */ break;
|
||||
// TypeScript ensures exhaustive handling
|
||||
}
|
||||
}
|
||||
|
||||
user.role = 'admni'; // TypeScript error!
|
||||
```
|
||||
|
||||
**Rule**: Use types instead of strings for fixed sets of values.
|
||||
|
||||
---
|
||||
|
||||
### 21. Callback Hell
|
||||
|
||||
**Problem**: Nested callbacks create unreadable, hard-to-debug code.
|
||||
|
||||
```typescript
|
||||
// ❌ Bad - callback pyramid
|
||||
getUser(userId, (err, user) => {
|
||||
if (err) return handleError(err);
|
||||
getOrders(user.id, (err, orders) => {
|
||||
if (err) return handleError(err);
|
||||
getOrderDetails(orders[0].id, (err, details) => {
|
||||
if (err) return handleError(err);
|
||||
processDetails(details, (err, result) => {
|
||||
if (err) return handleError(err);
|
||||
sendNotification(result, (err) => {
|
||||
if (err) return handleError(err);
|
||||
console.log('Done!');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ✅ Good - async/await
|
||||
async function processUserOrder(userId: string) {
|
||||
try {
|
||||
const user = await getUser(userId);
|
||||
const orders = await getOrders(user.id);
|
||||
const details = await getOrderDetails(orders[0].id);
|
||||
const result = await processDetails(details);
|
||||
await sendNotification(result);
|
||||
console.log('Done!');
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Rule**: Use async/await for asynchronous code.
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Anti-Pattern | Fix |
|
||||
|--------------|-----|
|
||||
| Cryptic abbreviations | Spell it out |
|
||||
| Generic names | Reveal intent |
|
||||
| Misleading names | Match name to behavior |
|
||||
| Hungarian notation | Let types handle types |
|
||||
| God functions | Extract smaller functions |
|
||||
| Too many parameters | Use options object |
|
||||
| Boolean flags | Named options or separate functions |
|
||||
| Side effects in getters | Make mutations explicit |
|
||||
| Different return types | Use discriminated unions |
|
||||
| Deep nesting | Guard clauses |
|
||||
| Premature abstraction | Wait for patterns to emerge |
|
||||
| Over-engineering | Solve today's problem |
|
||||
| Copy-paste | Extract shared logic |
|
||||
| God objects | Single responsibility |
|
||||
| Commented-out code | Delete it |
|
||||
| Obvious comments | Let code self-document |
|
||||
| TODO sprawl | Use issue tracker |
|
||||
| Outdated comments | Update or delete |
|
||||
| Exception control flow | Explicit conditionals |
|
||||
| Stringly-typed | Use enums/unions |
|
||||
| Callback hell | async/await |
|
||||
Reference in New Issue
Block a user