💻

Development Practices

Write better code, ship with confidence. Git workflows, TDD, clean code, security, and the habits that separate senior engineers from juniors.

Git Branching Strategies

Your branching strategy defines how your team collaborates, how often you ship, and how stable your main branch stays. Choose based on your release frequency and team size.

StrategyHow It WorksBest ForUsed By
Git Flow main + develop + feature/* + release/* + hotfix/*. Merges go develop → release → main Versioned releases, mobile apps with app store review Large enterprise, teams with scheduled releases
GitHub Flow main + feature branches. PR → review → merge to main → deploy Web apps with continuous delivery GitHub, Netlify, most SaaS companies
Trunk-Based Everyone commits to main (or very short-lived branches). Feature flags hide unfinished work High-velocity teams, CI/CD at scale Google, Facebook, Netflix
# GitHub Flow — simple and effective for most teams

# 1. Create a feature branch from main
git checkout main
git pull origin main
git checkout -b feature/add-expense-categories

# 2. Make commits with clear messages
git commit -m "feat: add category dropdown to expense form"
git commit -m "test: add unit tests for category validation"
git commit -m "fix: prevent empty category submission"

# 3. Push and open PR
git push origin feature/add-expense-categories
# Open PR → get 2 reviews → merge → auto-deploy

# Conventional Commits format
# feat: new feature | fix: bug fix | docs: docs only
# refactor: code change | test: add tests | chore: build/deps
⚠️ Never Force-Push to Main Force-pushing to the main branch rewrites history and can destroy your teammates' work. Set branch protection rules: require PR reviews, require CI to pass, and disable force-push on main. At Facebook, pushing directly to main triggers an automated rollback and an incident investigation.

Test-Driven Development (TDD)

Write the test before you write the code. TDD forces you to think about the interface and expected behavior before implementation — resulting in simpler, more testable code.

🔴
Write a failing test
🟢
Write minimum code to pass
🔵
Refactor without breaking tests
// Step 1: RED — Write failing test first
describe('BudgetCalculator', () => {
  it('warns when spending exceeds 80% of budget', () => {
    const calc = new BudgetCalculator({ limit: 1000 });
    calc.addExpense(850);
    expect(calc.isNearLimit()).toBe(true);
  });
});
// → Test fails: BudgetCalculator is not defined ❌

// Step 2: GREEN — Write minimum code to pass
class BudgetCalculator {
  constructor({ limit }) { this.limit = limit; this.spent = 0; }
  addExpense(amount) { this.spent += amount; }
  isNearLimit() { return (this.spent / this.limit) >= 0.8; }
}
// → Test passes ✅

// Step 3: REFACTOR — Improve without breaking tests
class BudgetCalculator {
  constructor({ limit, warningThreshold = 0.8 }) {
    this.limit = limit;
    this.warningThreshold = warningThreshold;
    this.spent = 0;
  }
  addExpense(amount) {
    if (amount <= 0) throw new Error('Amount must be positive');
    this.spent += amount;
  }
  get spentPercentage() { return this.spent / this.limit; }
  isNearLimit() { return this.spentPercentage >= this.warningThreshold; }
}
// → Still passes ✅ — now cleaner and more configurable

Clean Code Principles

Code is read 10× more than it's written. Clean code is not about aesthetics — it's about reducing the cognitive load for every engineer who touches it after you.

Before & After Examples

❌ Unclear code
function calc(d, r) {
  let x = 0;
  for (let i = 0; i < d.length; i++) {
    if (d[i].t === 1) {
      x += d[i].a * r;
    }
  }
  return x;
}
✅ Clean code
function calculateTaxableIncome(
  expenses,
  taxRate
) {
  return expenses
    .filter(e => e.type === EXPENSE_TYPE.BUSINESS)
    .reduce((total, e) =>
      total + (e.amount * taxRate), 0
    );
}
PrincipleRuleMetric
Function sizeFunctions should do ONE thingMax 20 lines
Function argsFewer arguments = easier to testMax 3 params (use objects for more)
NamingNames should reveal intentgetUserById not getU or fetch
CommentsComment WHY, not WHAT (code explains what)Remove all "noise" comments
NestingDeep nesting = complexity. Use early returnsMax 3 levels deep
DRYDon't Repeat Yourself — extract duplicationIf copy-pasted: extract function/module

OWASP Top 10 — Security Vulnerabilities

The Open Web Application Security Project publishes the most critical web vulnerabilities. Every developer must understand these — security is not just the security team's job.

A01

Broken Access Control

Users can access resources they shouldn't. Example: User A accesses /api/invoices/456 which belongs to User B by changing the ID.

// ❌ Vulnerable: trusts client-provided ID
app.get('/api/expenses/:id', async (req, res) => {
  const expense = await findById(req.params.id); // any user can access any expense!
  res.json(expense);
});

// ✅ Secure: always filter by authenticated user
app.get('/api/expenses/:id', async (req, res) => {
  const expense = await findByIdAndUser(req.params.id, req.user.id);
  if (!expense) return res.status(404).json({ error: 'Not found' });
  res.json(expense);
});
A03

SQL Injection

Malicious SQL injected into queries. Classic example: ' OR '1'='1 in a login form bypasses authentication.

// ❌ Vulnerable: string concatenation
const query = `SELECT * FROM users WHERE email = '${email}'`;
// Input: admin'-- → SELECT * FROM users WHERE email = 'admin'--'

// ✅ Secure: parameterized queries
const query = 'SELECT * FROM users WHERE email = $1';
const result = await db.query(query, [email]); // input sanitized automatically
A07

Identification and Authentication Failures

Weak passwords, no rate limiting on login, session tokens in URLs, no MFA for privileged accounts.

// ✅ Secure authentication checklist:
// 1. Hash passwords with bcrypt (cost factor 12+)
const hash = await bcrypt.hash(password, 12);

// 2. Rate limit login attempts (max 5 per 15 min per IP)
app.use('/api/auth/login', rateLimit({ windowMs: 15 * 60000, max: 5 }));

// 3. Use httpOnly + secure + sameSite cookies for sessions
res.cookie('session', token, { httpOnly: true, secure: true, sameSite: 'strict' });
⚠️ The $700M Equifax Breach (2017) Attackers exploited a known Apache Struts vulnerability (CVE-2017-5638) that had a patch available for 2 months. The company failed to update their dependencies. Lesson: run npm audit, pip-audit, or Dependabot on every project. Unpatched dependencies are the #1 cause of breaches.