omgkit 2.0.6 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/package.json +6 -3
  2. package/plugin/agents/architect.md +357 -43
  3. package/plugin/agents/code-reviewer.md +481 -22
  4. package/plugin/agents/debugger.md +397 -30
  5. package/plugin/agents/docs-manager.md +431 -23
  6. package/plugin/agents/fullstack-developer.md +395 -34
  7. package/plugin/agents/git-manager.md +438 -20
  8. package/plugin/agents/oracle.md +329 -53
  9. package/plugin/agents/planner.md +275 -32
  10. package/plugin/agents/researcher.md +343 -21
  11. package/plugin/agents/scout.md +423 -18
  12. package/plugin/agents/sprint-master.md +418 -48
  13. package/plugin/agents/tester.md +551 -26
  14. package/plugin/skills/backend/api-architecture/SKILL.md +857 -0
  15. package/plugin/skills/backend/caching-strategies/SKILL.md +755 -0
  16. package/plugin/skills/backend/event-driven-architecture/SKILL.md +753 -0
  17. package/plugin/skills/backend/real-time-systems/SKILL.md +635 -0
  18. package/plugin/skills/databases/database-optimization/SKILL.md +571 -0
  19. package/plugin/skills/devops/monorepo-management/SKILL.md +595 -0
  20. package/plugin/skills/devops/observability/SKILL.md +622 -0
  21. package/plugin/skills/devops/performance-profiling/SKILL.md +905 -0
  22. package/plugin/skills/frontend/advanced-ui-design/SKILL.md +426 -0
  23. package/plugin/skills/integrations/ai-integration/SKILL.md +730 -0
  24. package/plugin/skills/integrations/payment-integration/SKILL.md +735 -0
  25. package/plugin/skills/methodology/problem-solving/SKILL.md +355 -0
  26. package/plugin/skills/methodology/research-validation/SKILL.md +668 -0
  27. package/plugin/skills/methodology/sequential-thinking/SKILL.md +260 -0
  28. package/plugin/skills/mobile/mobile-development/SKILL.md +756 -0
  29. package/plugin/skills/security/security-hardening/SKILL.md +633 -0
  30. package/plugin/skills/tools/document-processing/SKILL.md +916 -0
  31. package/plugin/skills/tools/image-processing/SKILL.md +748 -0
  32. package/plugin/skills/tools/mcp-development/SKILL.md +883 -0
  33. package/plugin/skills/tools/media-processing/SKILL.md +831 -0
@@ -0,0 +1,755 @@
1
+ ---
2
+ name: caching-strategies
3
+ description: Multi-layer caching with Redis, CDN, and browser caching for optimal application performance
4
+ category: backend
5
+ triggers:
6
+ - caching strategies
7
+ - redis cache
8
+ - cdn caching
9
+ - cache invalidation
10
+ - http caching
11
+ - performance caching
12
+ ---
13
+
14
+ # Caching Strategies
15
+
16
+ Implement **multi-layer caching** for optimal performance. This skill covers Redis patterns, CDN configuration, HTTP cache headers, and cache invalidation strategies.
17
+
18
+ ## Purpose
19
+
20
+ Dramatically improve application performance through strategic caching:
21
+
22
+ - Reduce database load with application caching
23
+ - Minimize latency with edge caching
24
+ - Optimize bandwidth with browser caching
25
+ - Handle cache invalidation correctly
26
+ - Implement cache-aside and write-through patterns
27
+ - Monitor cache effectiveness
28
+
29
+ ## Features
30
+
31
+ ### 1. Redis Caching Patterns
32
+
33
+ ```typescript
34
+ import Redis from 'ioredis';
35
+
36
+ const redis = new Redis(process.env.REDIS_URL);
37
+
38
+ // Basic cache operations
39
+ class CacheService {
40
+ private prefix: string;
41
+ private defaultTTL: number;
42
+
43
+ constructor(prefix: string = 'app', defaultTTL: number = 3600) {
44
+ this.prefix = prefix;
45
+ this.defaultTTL = defaultTTL;
46
+ }
47
+
48
+ private key(key: string): string {
49
+ return `${this.prefix}:${key}`;
50
+ }
51
+
52
+ async get<T>(key: string): Promise<T | null> {
53
+ const data = await redis.get(this.key(key));
54
+ return data ? JSON.parse(data) : null;
55
+ }
56
+
57
+ async set<T>(key: string, value: T, ttl?: number): Promise<void> {
58
+ const serialized = JSON.stringify(value);
59
+ await redis.setex(this.key(key), ttl || this.defaultTTL, serialized);
60
+ }
61
+
62
+ async del(key: string): Promise<void> {
63
+ await redis.del(this.key(key));
64
+ }
65
+
66
+ async exists(key: string): Promise<boolean> {
67
+ return (await redis.exists(this.key(key))) === 1;
68
+ }
69
+
70
+ // Get or set pattern
71
+ async getOrSet<T>(
72
+ key: string,
73
+ factory: () => Promise<T>,
74
+ ttl?: number
75
+ ): Promise<T> {
76
+ const cached = await this.get<T>(key);
77
+ if (cached !== null) return cached;
78
+
79
+ const value = await factory();
80
+ await this.set(key, value, ttl);
81
+ return value;
82
+ }
83
+
84
+ // Bulk operations
85
+ async mget<T>(keys: string[]): Promise<(T | null)[]> {
86
+ const prefixedKeys = keys.map(k => this.key(k));
87
+ const values = await redis.mget(prefixedKeys);
88
+ return values.map(v => (v ? JSON.parse(v) : null));
89
+ }
90
+
91
+ async mset<T>(entries: Array<{ key: string; value: T; ttl?: number }>): Promise<void> {
92
+ const pipeline = redis.pipeline();
93
+
94
+ for (const entry of entries) {
95
+ pipeline.setex(
96
+ this.key(entry.key),
97
+ entry.ttl || this.defaultTTL,
98
+ JSON.stringify(entry.value)
99
+ );
100
+ }
101
+
102
+ await pipeline.exec();
103
+ }
104
+
105
+ // Pattern-based invalidation
106
+ async invalidatePattern(pattern: string): Promise<number> {
107
+ const keys = await redis.keys(this.key(pattern));
108
+ if (keys.length === 0) return 0;
109
+ return redis.del(...keys);
110
+ }
111
+ }
112
+
113
+ // Usage
114
+ const cache = new CacheService('users', 3600);
115
+
116
+ async function getUser(id: string): Promise<User> {
117
+ return cache.getOrSet(`user:${id}`, () => db.user.findUnique({ where: { id } }));
118
+ }
119
+ ```
120
+
121
+ ### 2. Cache-Aside Pattern
122
+
123
+ ```typescript
124
+ // Cache-aside with database fallback
125
+ class UserRepository {
126
+ private cache: CacheService;
127
+
128
+ constructor() {
129
+ this.cache = new CacheService('users', 3600);
130
+ }
131
+
132
+ async findById(id: string): Promise<User | null> {
133
+ // Try cache first
134
+ const cached = await this.cache.get<User>(`${id}`);
135
+ if (cached) {
136
+ metrics.increment('cache.hit', { type: 'user' });
137
+ return cached;
138
+ }
139
+
140
+ metrics.increment('cache.miss', { type: 'user' });
141
+
142
+ // Fetch from database
143
+ const user = await db.user.findUnique({ where: { id } });
144
+
145
+ // Cache the result (even null to prevent cache stampede)
146
+ if (user) {
147
+ await this.cache.set(`${id}`, user);
148
+ } else {
149
+ // Cache null with shorter TTL
150
+ await this.cache.set(`${id}`, null, 60);
151
+ }
152
+
153
+ return user;
154
+ }
155
+
156
+ async update(id: string, data: Partial<User>): Promise<User> {
157
+ const user = await db.user.update({ where: { id }, data });
158
+
159
+ // Invalidate cache
160
+ await this.cache.del(`${id}`);
161
+
162
+ return user;
163
+ }
164
+
165
+ async delete(id: string): Promise<void> {
166
+ await db.user.delete({ where: { id } });
167
+ await this.cache.del(`${id}`);
168
+ }
169
+ }
170
+
171
+ // Cache stampede prevention with locking
172
+ async function getWithLock<T>(
173
+ key: string,
174
+ factory: () => Promise<T>,
175
+ ttl: number = 3600
176
+ ): Promise<T> {
177
+ const cache = new CacheService();
178
+ const lockKey = `lock:${key}`;
179
+
180
+ // Try to get cached value
181
+ const cached = await cache.get<T>(key);
182
+ if (cached !== null) return cached;
183
+
184
+ // Try to acquire lock
185
+ const acquired = await redis.set(lockKey, '1', 'EX', 10, 'NX');
186
+
187
+ if (!acquired) {
188
+ // Another process is fetching, wait and retry
189
+ await new Promise(r => setTimeout(r, 100));
190
+ return getWithLock(key, factory, ttl);
191
+ }
192
+
193
+ try {
194
+ // Double-check after acquiring lock
195
+ const cached = await cache.get<T>(key);
196
+ if (cached !== null) return cached;
197
+
198
+ // Fetch and cache
199
+ const value = await factory();
200
+ await cache.set(key, value, ttl);
201
+ return value;
202
+ } finally {
203
+ await redis.del(lockKey);
204
+ }
205
+ }
206
+ ```
207
+
208
+ ### 3. Write-Through & Write-Behind
209
+
210
+ ```typescript
211
+ // Write-through cache
212
+ class WriteThroughCache<T> {
213
+ constructor(
214
+ private cache: CacheService,
215
+ private repository: Repository<T>
216
+ ) {}
217
+
218
+ async create(entity: T): Promise<T> {
219
+ // Write to database first
220
+ const saved = await this.repository.create(entity);
221
+
222
+ // Then update cache
223
+ await this.cache.set(this.getKey(saved), saved);
224
+
225
+ return saved;
226
+ }
227
+
228
+ async update(id: string, data: Partial<T>): Promise<T> {
229
+ // Write to database
230
+ const updated = await this.repository.update(id, data);
231
+
232
+ // Update cache
233
+ await this.cache.set(this.getKey(updated), updated);
234
+
235
+ return updated;
236
+ }
237
+
238
+ private getKey(entity: T): string {
239
+ return `${(entity as any).id}`;
240
+ }
241
+ }
242
+
243
+ // Write-behind (async write) cache
244
+ class WriteBehindCache<T> {
245
+ private writeQueue: Array<{ key: string; data: T }> = [];
246
+ private flushInterval: NodeJS.Timer;
247
+
248
+ constructor(
249
+ private cache: CacheService,
250
+ private repository: Repository<T>,
251
+ flushIntervalMs: number = 5000
252
+ ) {
253
+ this.flushInterval = setInterval(() => this.flush(), flushIntervalMs);
254
+ }
255
+
256
+ async set(key: string, data: T): Promise<void> {
257
+ // Write to cache immediately
258
+ await this.cache.set(key, data);
259
+
260
+ // Queue for async database write
261
+ this.writeQueue.push({ key, data });
262
+ }
263
+
264
+ private async flush(): Promise<void> {
265
+ if (this.writeQueue.length === 0) return;
266
+
267
+ const batch = this.writeQueue.splice(0, 100);
268
+
269
+ try {
270
+ await this.repository.bulkUpsert(batch.map(b => b.data));
271
+ } catch (error) {
272
+ // Re-queue failed items
273
+ this.writeQueue.unshift(...batch);
274
+ console.error('Write-behind flush failed:', error);
275
+ }
276
+ }
277
+
278
+ async stop(): Promise<void> {
279
+ clearInterval(this.flushInterval);
280
+ await this.flush();
281
+ }
282
+ }
283
+ ```
284
+
285
+ ### 4. HTTP Caching Headers
286
+
287
+ ```typescript
288
+ // Cache control middleware
289
+ interface CacheOptions {
290
+ maxAge?: number;
291
+ sMaxAge?: number;
292
+ staleWhileRevalidate?: number;
293
+ staleIfError?: number;
294
+ private?: boolean;
295
+ noStore?: boolean;
296
+ mustRevalidate?: boolean;
297
+ }
298
+
299
+ function cacheControl(options: CacheOptions) {
300
+ return (req: Request, res: Response, next: NextFunction) => {
301
+ const directives: string[] = [];
302
+
303
+ if (options.noStore) {
304
+ directives.push('no-store');
305
+ } else {
306
+ if (options.private) {
307
+ directives.push('private');
308
+ } else {
309
+ directives.push('public');
310
+ }
311
+
312
+ if (options.maxAge !== undefined) {
313
+ directives.push(`max-age=${options.maxAge}`);
314
+ }
315
+
316
+ if (options.sMaxAge !== undefined) {
317
+ directives.push(`s-maxage=${options.sMaxAge}`);
318
+ }
319
+
320
+ if (options.staleWhileRevalidate !== undefined) {
321
+ directives.push(`stale-while-revalidate=${options.staleWhileRevalidate}`);
322
+ }
323
+
324
+ if (options.staleIfError !== undefined) {
325
+ directives.push(`stale-if-error=${options.staleIfError}`);
326
+ }
327
+
328
+ if (options.mustRevalidate) {
329
+ directives.push('must-revalidate');
330
+ }
331
+ }
332
+
333
+ res.set('Cache-Control', directives.join(', '));
334
+ next();
335
+ };
336
+ }
337
+
338
+ // Route examples
339
+ // Static assets - cache for 1 year
340
+ app.use(
341
+ '/static',
342
+ cacheControl({ maxAge: 31536000 }),
343
+ express.static('public')
344
+ );
345
+
346
+ // API responses - no caching for dynamic data
347
+ app.get(
348
+ '/api/user/profile',
349
+ cacheControl({ noStore: true }),
350
+ profileHandler
351
+ );
352
+
353
+ // Public data - cache with revalidation
354
+ app.get(
355
+ '/api/products',
356
+ cacheControl({
357
+ maxAge: 60,
358
+ sMaxAge: 300,
359
+ staleWhileRevalidate: 86400,
360
+ }),
361
+ productsHandler
362
+ );
363
+
364
+ // ETag support
365
+ import etag from 'etag';
366
+
367
+ app.get('/api/products/:id', async (req, res) => {
368
+ const product = await getProduct(req.params.id);
369
+
370
+ if (!product) {
371
+ return res.status(404).json({ error: 'Not found' });
372
+ }
373
+
374
+ // Generate ETag
375
+ const body = JSON.stringify(product);
376
+ const tag = etag(body);
377
+
378
+ res.set('ETag', tag);
379
+ res.set('Cache-Control', 'private, max-age=0, must-revalidate');
380
+
381
+ // Check If-None-Match
382
+ if (req.headers['if-none-match'] === tag) {
383
+ return res.status(304).end();
384
+ }
385
+
386
+ res.json(product);
387
+ });
388
+ ```
389
+
390
+ ### 5. CDN Configuration
391
+
392
+ ```typescript
393
+ // Vercel Edge Config
394
+ // vercel.json
395
+ {
396
+ "headers": [
397
+ {
398
+ "source": "/api/public/(.*)",
399
+ "headers": [
400
+ {
401
+ "key": "Cache-Control",
402
+ "value": "public, s-maxage=60, stale-while-revalidate=86400"
403
+ }
404
+ ]
405
+ },
406
+ {
407
+ "source": "/_next/static/(.*)",
408
+ "headers": [
409
+ {
410
+ "key": "Cache-Control",
411
+ "value": "public, max-age=31536000, immutable"
412
+ }
413
+ ]
414
+ }
415
+ ]
416
+ }
417
+
418
+ // Cloudflare Cache Rules
419
+ // Using Page Rules or Cache Rules API
420
+ const cacheRules = {
421
+ rules: [
422
+ {
423
+ expression: '(http.request.uri.path matches "^/api/public/")',
424
+ action: 'set_cache_settings',
425
+ action_parameters: {
426
+ edge_ttl: {
427
+ mode: 'override_origin',
428
+ default: 300,
429
+ },
430
+ browser_ttl: {
431
+ mode: 'override_origin',
432
+ default: 60,
433
+ },
434
+ },
435
+ },
436
+ {
437
+ expression: '(http.request.uri.path matches "^/static/")',
438
+ action: 'set_cache_settings',
439
+ action_parameters: {
440
+ cache: true,
441
+ edge_ttl: { mode: 'override_origin', default: 86400 * 30 },
442
+ },
443
+ },
444
+ ],
445
+ };
446
+
447
+ // Purge cache via API
448
+ async function purgeCloudflareCache(urls: string[]): Promise<void> {
449
+ await fetch(
450
+ `https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/purge_cache`,
451
+ {
452
+ method: 'POST',
453
+ headers: {
454
+ 'Authorization': `Bearer ${CF_API_TOKEN}`,
455
+ 'Content-Type': 'application/json',
456
+ },
457
+ body: JSON.stringify({ files: urls }),
458
+ }
459
+ );
460
+ }
461
+
462
+ // Purge by tag
463
+ async function purgeCacheByTag(tags: string[]): Promise<void> {
464
+ await fetch(
465
+ `https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/purge_cache`,
466
+ {
467
+ method: 'POST',
468
+ headers: {
469
+ 'Authorization': `Bearer ${CF_API_TOKEN}`,
470
+ 'Content-Type': 'application/json',
471
+ },
472
+ body: JSON.stringify({ tags }),
473
+ }
474
+ );
475
+ }
476
+ ```
477
+
478
+ ### 6. Cache Invalidation
479
+
480
+ ```typescript
481
+ // Event-based cache invalidation
482
+ import { EventEmitter } from 'events';
483
+
484
+ class CacheInvalidator extends EventEmitter {
485
+ private cache: CacheService;
486
+
487
+ constructor() {
488
+ super();
489
+ this.cache = new CacheService();
490
+ this.setupListeners();
491
+ }
492
+
493
+ private setupListeners(): void {
494
+ // User events
495
+ this.on('user:updated', async (userId: string) => {
496
+ await this.cache.del(`user:${userId}`);
497
+ await this.cache.invalidatePattern(`user:${userId}:*`);
498
+ });
499
+
500
+ this.on('user:deleted', async (userId: string) => {
501
+ await this.cache.invalidatePattern(`user:${userId}*`);
502
+ });
503
+
504
+ // Product events
505
+ this.on('product:updated', async (productId: string) => {
506
+ await this.cache.del(`product:${productId}`);
507
+ // Also invalidate category cache
508
+ const product = await db.product.findUnique({ where: { id: productId } });
509
+ if (product) {
510
+ await this.cache.del(`category:${product.categoryId}:products`);
511
+ }
512
+ });
513
+
514
+ // Bulk invalidation
515
+ this.on('cache:purge:all', async () => {
516
+ await redis.flushdb();
517
+ });
518
+ }
519
+ }
520
+
521
+ const invalidator = new CacheInvalidator();
522
+
523
+ // Use in services
524
+ class ProductService {
525
+ async update(id: string, data: UpdateProductInput): Promise<Product> {
526
+ const product = await db.product.update({ where: { id }, data });
527
+ invalidator.emit('product:updated', id);
528
+ return product;
529
+ }
530
+ }
531
+
532
+ // Time-based invalidation with scheduled jobs
533
+ import { CronJob } from 'cron';
534
+
535
+ // Invalidate daily stats cache at midnight
536
+ new CronJob('0 0 * * *', async () => {
537
+ await cache.invalidatePattern('stats:daily:*');
538
+ }).start();
539
+
540
+ // Refresh popular products cache every hour
541
+ new CronJob('0 * * * *', async () => {
542
+ const products = await getPopularProducts();
543
+ await cache.set('products:popular', products, 3600);
544
+ }).start();
545
+ ```
546
+
547
+ ### 7. Multi-Layer Caching
548
+
549
+ ```typescript
550
+ // L1: In-memory (fastest, smallest)
551
+ // L2: Redis (fast, shared)
552
+ // L3: Database (slowest, source of truth)
553
+
554
+ import LRUCache from 'lru-cache';
555
+
556
+ class MultiLayerCache<T> {
557
+ private l1: LRUCache<string, T>;
558
+ private l2: CacheService;
559
+
560
+ constructor(options: {
561
+ l1MaxSize: number;
562
+ l1TTL: number;
563
+ l2Prefix: string;
564
+ l2TTL: number;
565
+ }) {
566
+ this.l1 = new LRUCache({
567
+ max: options.l1MaxSize,
568
+ ttl: options.l1TTL * 1000,
569
+ });
570
+ this.l2 = new CacheService(options.l2Prefix, options.l2TTL);
571
+ }
572
+
573
+ async get(key: string): Promise<T | null> {
574
+ // Check L1 (in-memory)
575
+ const l1Value = this.l1.get(key);
576
+ if (l1Value !== undefined) {
577
+ metrics.increment('cache.l1.hit');
578
+ return l1Value;
579
+ }
580
+
581
+ // Check L2 (Redis)
582
+ const l2Value = await this.l2.get<T>(key);
583
+ if (l2Value !== null) {
584
+ metrics.increment('cache.l2.hit');
585
+ // Promote to L1
586
+ this.l1.set(key, l2Value);
587
+ return l2Value;
588
+ }
589
+
590
+ metrics.increment('cache.miss');
591
+ return null;
592
+ }
593
+
594
+ async set(key: string, value: T, l1TTL?: number, l2TTL?: number): Promise<void> {
595
+ // Set in both layers
596
+ this.l1.set(key, value, { ttl: l1TTL ? l1TTL * 1000 : undefined });
597
+ await this.l2.set(key, value, l2TTL);
598
+ }
599
+
600
+ async getOrSet(
601
+ key: string,
602
+ factory: () => Promise<T>,
603
+ l1TTL?: number,
604
+ l2TTL?: number
605
+ ): Promise<T> {
606
+ const cached = await this.get(key);
607
+ if (cached !== null) return cached;
608
+
609
+ const value = await factory();
610
+ await this.set(key, value, l1TTL, l2TTL);
611
+ return value;
612
+ }
613
+
614
+ async invalidate(key: string): Promise<void> {
615
+ this.l1.delete(key);
616
+ await this.l2.del(key);
617
+ }
618
+ }
619
+
620
+ // Usage
621
+ const userCache = new MultiLayerCache<User>({
622
+ l1MaxSize: 1000,
623
+ l1TTL: 60, // 1 minute in memory
624
+ l2Prefix: 'users',
625
+ l2TTL: 3600, // 1 hour in Redis
626
+ });
627
+
628
+ async function getUser(id: string): Promise<User | null> {
629
+ return userCache.getOrSet(
630
+ `user:${id}`,
631
+ () => db.user.findUnique({ where: { id } })
632
+ );
633
+ }
634
+ ```
635
+
636
+ ## Use Cases
637
+
638
+ ### 1. API Response Caching
639
+
640
+ ```typescript
641
+ // Middleware for caching API responses
642
+ function apiCache(options: {
643
+ ttl: number;
644
+ keyGenerator?: (req: Request) => string;
645
+ condition?: (req: Request) => boolean;
646
+ }) {
647
+ const cache = new CacheService('api');
648
+
649
+ return async (req: Request, res: Response, next: NextFunction) => {
650
+ // Skip caching for non-GET or if condition fails
651
+ if (req.method !== 'GET' || (options.condition && !options.condition(req))) {
652
+ return next();
653
+ }
654
+
655
+ const key = options.keyGenerator?.(req) || req.originalUrl;
656
+ const cached = await cache.get<{ body: any; headers: Record<string, string> }>(key);
657
+
658
+ if (cached) {
659
+ Object.entries(cached.headers).forEach(([k, v]) => res.set(k, v));
660
+ res.set('X-Cache', 'HIT');
661
+ return res.json(cached.body);
662
+ }
663
+
664
+ // Capture response
665
+ const originalJson = res.json.bind(res);
666
+ res.json = (body: any) => {
667
+ cache.set(key, { body, headers: res.getHeaders() as any }, options.ttl);
668
+ res.set('X-Cache', 'MISS');
669
+ return originalJson(body);
670
+ };
671
+
672
+ next();
673
+ };
674
+ }
675
+
676
+ // Apply to routes
677
+ app.get('/api/products', apiCache({ ttl: 300 }), getProducts);
678
+ ```
679
+
680
+ ### 2. Session Caching
681
+
682
+ ```typescript
683
+ // Redis session store
684
+ import session from 'express-session';
685
+ import RedisStore from 'connect-redis';
686
+
687
+ app.use(session({
688
+ store: new RedisStore({ client: redis }),
689
+ secret: process.env.SESSION_SECRET!,
690
+ resave: false,
691
+ saveUninitialized: false,
692
+ cookie: {
693
+ secure: process.env.NODE_ENV === 'production',
694
+ httpOnly: true,
695
+ maxAge: 24 * 60 * 60 * 1000, // 24 hours
696
+ },
697
+ }));
698
+ ```
699
+
700
+ ## Best Practices
701
+
702
+ ### Do's
703
+
704
+ - **Cache close to the user** - Browser > CDN > App > Database
705
+ - **Use appropriate TTLs** - Balance freshness vs. performance
706
+ - **Implement cache warming** - Pre-populate for critical paths
707
+ - **Monitor hit rates** - Target > 90% for hot data
708
+ - **Plan invalidation** - Know when and how to invalidate
709
+ - **Use consistent hashing** - For distributed caches
710
+
711
+ ### Don'ts
712
+
713
+ - Don't cache sensitive data in shared caches
714
+ - Don't forget cache key namespacing
715
+ - Don't ignore cache stampede scenarios
716
+ - Don't cache errors with long TTLs
717
+ - Don't skip monitoring
718
+ - Don't assume cache is always available
719
+
720
+ ### Cache Strategy Checklist
721
+
722
+ ```markdown
723
+ ## Cache Implementation Checklist
724
+
725
+ ### Design
726
+ - [ ] Identified cacheable data
727
+ - [ ] Defined appropriate TTLs
728
+ - [ ] Planned invalidation strategy
729
+ - [ ] Considered cache layers
730
+
731
+ ### Implementation
732
+ - [ ] Added cache-aside logic
733
+ - [ ] Implemented stampede prevention
734
+ - [ ] Set up monitoring
735
+ - [ ] Added cache headers
736
+
737
+ ### Operations
738
+ - [ ] Monitoring hit/miss ratios
739
+ - [ ] Alerting on cache failures
740
+ - [ ] Regular cache analysis
741
+ - [ ] Invalidation testing
742
+ ```
743
+
744
+ ## Related Skills
745
+
746
+ - **redis** - Redis operations
747
+ - **performance-profiling** - Measuring cache impact
748
+ - **api-architecture** - API caching patterns
749
+
750
+ ## Reference Resources
751
+
752
+ - [Redis Documentation](https://redis.io/documentation)
753
+ - [HTTP Caching MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching)
754
+ - [Cloudflare Caching](https://developers.cloudflare.com/cache/)
755
+ - [Cache-Control Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control)