Performance Optimization Standards
Database Optimizasyonu
N+1 Query Problemi
typescript
// ❌ N+1 Query - Her user için ayrı query
const users = await userRepository.findAll()
for (const user of users) {
user.orders = await orderRepository.findByUserId(user.id) // N query!
}
// ✅ Eager Loading / Join
const users = await userRepository.findAll({
relations: ['orders'], // Tek query
})
// ✅ Batch Loading
const users = await userRepository.findAll()
const userIds = users.map(u => u.id)
const orders = await orderRepository.findByUserIds(userIds) // 2 query total
const ordersByUser = groupBy(orders, 'userId')
users.forEach(u => u.orders = ordersByUser[u.id] || [])
Index Stratejisi
sql
-- Sık sorgulanan kolonlar CREATE INDEX idx_users_email ON users(email); -- Composite index (sorgulama sırasına göre) CREATE INDEX idx_orders_user_status ON orders(user_id, status); -- Partial index (filtered queries için) CREATE INDEX idx_active_users ON users(created_at) WHERE is_active = true; -- Query explain EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'test@test.com';
Query Optimization
typescript
// ❌ Gereksiz veri çekme
const users = await db.query('SELECT * FROM users')
// ✅ Sadece gereken kolonlar
const users = await db.query('SELECT id, name, email FROM users')
// ❌ LIKE wildcard başta
WHERE name LIKE '%john%' // Full table scan
// ✅ Prefix match (index kullanır)
WHERE name LIKE 'john%'
// ✅ Full-text search
WHERE to_tsvector(name) @@ to_tsquery('john')
Caching Stratejileri
Cache Levels
code
┌─────────────────┐ │ Client Cache │ Browser cache, localStorage ├─────────────────┤ │ CDN Cache │ Static assets, API responses ├─────────────────┤ │ Application │ Redis, Memcached │ Cache │ ├─────────────────┤ │ Database Cache │ Query cache, connection pool └─────────────────┘
Redis Caching Pattern
typescript
// Cache-Aside Pattern
async function getUser(id: string): Promise<User> {
const cacheKey = `user:${id}`
// 1. Cache'den oku
const cached = await redis.get(cacheKey)
if (cached) {
return JSON.parse(cached)
}
// 2. DB'den oku
const user = await userRepository.findById(id)
if (!user) {
throw new NotFoundError('User', id)
}
// 3. Cache'e yaz (TTL: 5 dakika)
await redis.setex(cacheKey, 300, JSON.stringify(user))
return user
}
// Cache Invalidation
async function updateUser(id: string, data: UpdateUserDto): Promise<User> {
const user = await userRepository.update(id, data)
await redis.del(`user:${id}`) // Invalidate
return user
}
Cache Headers (HTTP)
typescript
// Static assets - Uzun cache
app.use('/static', express.static('public', {
maxAge: '1y',
immutable: true,
}))
// API responses - Kısa cache
res.set('Cache-Control', 'private, max-age=60')
// No cache (dynamic data)
res.set('Cache-Control', 'no-store')
Frontend Optimizasyonu
React Performance
tsx
// ✅ Memoization
const ExpensiveComponent = memo(({ data }) => {
// Heavy render
})
// ✅ useMemo for expensive calculations
const sortedItems = useMemo(() => {
return items.sort((a, b) => a.name.localeCompare(b.name))
}, [items])
// ✅ useCallback for stable references
const handleClick = useCallback(() => {
doSomething(id)
}, [id])
// ❌ Inline objects/arrays (her render'da yeni reference)
<Component style={{ color: 'red' }} /> // Her render'da yeni object
// ✅ Stable reference
const styles = useMemo(() => ({ color: 'red' }), [])
<Component style={styles} />
Code Splitting
tsx
// ✅ Lazy loading
const Dashboard = lazy(() => import('./pages/Dashboard'))
const Settings = lazy(() => import('./pages/Settings'))
// Route-based splitting
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
Bundle Size
bash
# Bundle analyze
npm run build -- --analyze
# Tree shaking - named imports kullan
import { debounce } from 'lodash-es' // ✅
import _ from 'lodash' // ❌ Tüm lodash
API Optimizasyonu
Pagination
typescript
// Cursor-based (performanslı, büyük veri)
interface PaginatedResponse<T> {
data: T[]
cursor: string | null
hasMore: boolean
}
async function getOrders(cursor?: string, limit = 20) {
const query = db.query('SELECT * FROM orders')
if (cursor) {
query.where('id > :cursor', { cursor })
}
const orders = await query.orderBy('id').limit(limit + 1).getMany()
const hasMore = orders.length > limit
return {
data: orders.slice(0, limit),
cursor: hasMore ? orders[limit - 1].id : null,
hasMore,
}
}
Response Compression
typescript
import compression from 'compression'
app.use(compression({
threshold: 1024, // 1KB üzeri compress
level: 6,
}))
Performance Checklist
Database
- • N+1 query kontrolü yapıldı
- • Gerekli index'ler oluşturuldu
- • EXPLAIN ANALYZE ile sorgular test edildi
- • Connection pooling aktif
Caching
- • Hot data cache'leniyor
- • Cache invalidation stratejisi var
- • TTL'ler uygun
Frontend
- • Bundle size < 250KB (initial)
- • Code splitting uygulandı
- • Images optimize edildi (WebP, lazy load)
- • Lighthouse score > 90
API
- • Response compression aktif
- • Pagination uygulandı
- • Rate limiting var