# Performance Optimization Guide This guide provides comprehensive strategies to optimize application performance. --- ## 1. Frontend Performance ### 1.1 Code Splitting (IMPLEMENTED) The `App.lazy.tsx` file implements route-based code splitting. To use it: ```typescript // frontend/src/index.tsx import AppLazy from './App.lazy'; root.render( ); ``` **Expected Results**: - Initial bundle size reduced by 60-70% - Faster Time to Interactive (TTI) - Better Lighthouse scores ### 1.2 Image Optimization #### Server-Side Image Processing ```go // pkg/utils/image.go package utils import ( "image" "image/jpeg" "image/png" "os" "github.com/nfnt/resize" ) type ImageSize struct { Width uint Height uint Name string } var ThumbnailSizes = []ImageSize{ {Width: 150, Height: 150, Name: "thumb"}, {Width: 400, Height: 400, Name: "small"}, {Width: 800, Height: 800, Name: "medium"}, {Width: 1200, Height: 0, Name: "large"}, } func GenerateThumbnails(sourcePath string, outputDir string) (map[string]string, error) { file, err := os.Open(sourcePath) if err != nil { return nil, err } defer file.Close() img, format, err := image.Decode(file) if err != nil { return nil, err } results := make(map[string]string) for _, size := range ThumbnailSizes { resized := resize.Resize(size.Width, size.Height, img, resize.Lanczos3) outputPath := fmt.Sprintf("%s/%s_%s.jpg", outputDir, filepath.Base(sourcePath), size.Name) out, err := os.Create(outputPath) if err != nil { continue } if format == "png" { png.Encode(out, resized) } else { jpeg.Encode(out, resized, &jpeg.Options{Quality: 85}) } out.Close() results[size.Name] = outputPath } return results, nil } ``` #### WebP Conversion ```bash # Install webp tools # Ubuntu: sudo apt-get install webp # Mac: brew install webp # Convert images cwebp input.jpg -q 80 -o output.webp ``` #### Frontend: Responsive Images ```tsx // components/OptimizedImage.tsx import React from 'react'; import { Box, Image } from '@chakra-ui/react'; interface OptimizedImageProps { src: string; alt: string; sizes?: string; } export const OptimizedImage: React.FC = ({ src, alt, sizes }) => { const basePath = src.replace(/\.[^.]+$/, ''); return ( {/* WebP sources */} {/* Fallback JPEG */} {/* Ultimate fallback */} {alt} ); }; ``` ### 1.3 Lazy Loading Images ```tsx // Use Intersection Observer for lazy loading import { useEffect, useRef, useState } from 'react'; export const useLazyLoad = () => { const [isVisible, setIsVisible] = useState(false); const ref = useRef(null); useEffect(() => { const observer = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting) { setIsVisible(true); observer.disconnect(); } }, { rootMargin: '50px' } ); if (ref.current) { observer.observe(ref.current); } return () => observer.disconnect(); }, []); return { ref, isVisible }; }; // Usage const LazyImage = ({ src, alt }: { src: string; alt: string }) => { const { ref, isVisible } = useLazyLoad(); return (
{isVisible ? ( {alt} ) : (
)}
); }; ``` ### 1.4 Font Optimization ```html ``` Or use local fonts: ```css /* frontend/src/fonts.css */ @font-face { font-family: 'Inter'; src: url('/fonts/Inter-Regular.woff2') format('woff2'); font-weight: 400; font-display: swap; } @font-face { font-family: 'Inter'; src: url('/fonts/Inter-Bold.woff2') format('woff2'); font-weight: 700; font-display: swap; } ``` ### 1.5 Bundle Size Reduction #### Analyze Bundle ```bash cd frontend npm run build npx source-map-explorer 'build/static/js/*.js' ``` #### Tree Shaking ```javascript // Import only what you need // ❌ Bad import _ from 'lodash'; // ✅ Good import debounce from 'lodash/debounce'; ``` #### Remove Unused Dependencies ```bash npm install -g depcheck depcheck ``` ### 1.6 Caching Strategy ```typescript // frontend/src/services/api.ts import { QueryClient } from '@tanstack/react-query'; export const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 5 * 60 * 1000, // 5 minutes cacheTime: 10 * 60 * 1000, // 10 minutes refetchOnWindowFocus: false, retry: 1, // Add cache keys queryKeyHashFn: (queryKey) => JSON.stringify(queryKey), }, }, }); // Prefetch critical data queryClient.prefetchQuery(['settings'], fetchSettings); ``` --- ## 2. Backend Performance ### 2.1 Database Query Optimization #### Use Database Indexes ```sql -- Add indexes for frequently queried columns CREATE INDEX idx_articles_published ON articles(published, published_at DESC); CREATE INDEX idx_articles_slug ON articles(slug); CREATE INDEX idx_articles_category ON articles(category_id); CREATE INDEX idx_players_team ON players(team_id); CREATE INDEX idx_matches_date ON matches(match_date); -- Composite indexes CREATE INDEX idx_articles_published_featured ON articles(published, featured, published_at DESC); ``` #### N+1 Query Prevention ```go // ❌ Bad - N+1 queries var articles []models.Article db.Find(&articles) for _, article := range articles { db.Model(&article).Association("Category").Find(&article.Category) } // ✅ Good - Single query with joins var articles []models.Article db.Preload("Category").Preload("Author").Find(&articles) ``` #### Pagination ```go func GetArticlesPaginated(c *gin.Context, db *gorm.DB) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) if page < 1 { page = 1 } if pageSize > 100 { pageSize = 100 // Prevent excessive queries } var articles []models.Article var total int64 db.Model(&models.Article{}).Where("published = ?", true).Count(&total) offset := (page - 1) * pageSize db.Where("published = ?", true). Order("published_at DESC"). Limit(pageSize). Offset(offset). Preload("Category"). Find(&articles) c.JSON(http.StatusOK, gin.H{ "items": articles, "page": page, "page_size": pageSize, "total": total, "total_pages": (total + int64(pageSize) - 1) / int64(pageSize), }) } ``` ### 2.2 Caching Layer #### Redis Integration ```go // pkg/cache/redis.go package cache import ( "context" "encoding/json" "time" "github.com/redis/go-redis/v9" ) var RedisClient *redis.Client func InitRedis(addr string) { RedisClient = redis.NewClient(&redis.Options{ Addr: addr, DB: 0, }) } func Get(ctx context.Context, key string, dest interface{}) error { val, err := RedisClient.Get(ctx, key).Result() if err != nil { return err } return json.Unmarshal([]byte(val), dest) } func Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error { json, err := json.Marshal(value) if err != nil { return err } return RedisClient.Set(ctx, key, json, expiration).Err() } // Usage in controller func (bc *BaseController) GetArticles(c *gin.Context) { cacheKey := "articles:published" var articles []models.Article err := cache.Get(c, cacheKey, &articles) if err == nil { c.JSON(http.StatusOK, articles) return } // Cache miss - fetch from DB bc.DB.Where("published = ?", true).Find(&articles) cache.Set(c, cacheKey, articles, 5*time.Minute) c.JSON(http.StatusOK, articles) } ``` #### In-Memory Cache ```go // Simple in-memory cache for small datasets type Cache struct { sync.RWMutex data map[string]cacheItem } type cacheItem struct { value interface{} expiration time.Time } func (c *Cache) Get(key string) (interface{}, bool) { c.RLock() defer c.RUnlock() item, exists := c.data[key] if !exists || time.Now().After(item.expiration) { return nil, false } return item.value, true } func (c *Cache) Set(key string, value interface{}, ttl time.Duration) { c.Lock() defer c.Unlock() c.data[key] = cacheItem{ value: value, expiration: time.Now().Add(ttl), } } ``` ### 2.3 Connection Pooling ```go // main.go or database initialization sqlDB, _ := db.DB() // Set connection pool parameters sqlDB.SetMaxIdleConns(10) sqlDB.SetMaxOpenConns(100) sqlDB.SetConnMaxLifetime(time.Hour) sqlDB.SetConnMaxIdleTime(10 * time.Minute) ``` ### 2.4 Response Compression ```go // Use gzip middleware import "github.com/gin-contrib/gzip" func main() { r := gin.Default() r.Use(gzip.Gzip(gzip.DefaultCompression)) // ... } ``` ### 2.5 HTTP Caching Headers ```go func (bc *BaseController) GetPublicData(c *gin.Context) { // Set cache headers for public data c.Header("Cache-Control", "public, max-age=3600, stale-while-revalidate=86400") c.Header("ETag", generateETag(data)) // Check If-None-Match header if c.GetHeader("If-None-Match") == etag { c.Status(http.StatusNotModified) return } c.JSON(http.StatusOK, data) } ``` --- ## 3. Database Performance ### 3.1 Query Optimization ```sql -- Use EXPLAIN ANALYZE to check query performance EXPLAIN ANALYZE SELECT * FROM articles WHERE published = true ORDER BY published_at DESC LIMIT 20; -- Add missing indexes based on EXPLAIN output ``` ### 3.2 Vacuum and Analyze ```sql -- Regular maintenance VACUUM ANALYZE articles; VACUUM ANALYZE players; VACUUM ANALYZE matches; -- Schedule in cron 0 2 * * * psql -d fotbal_club -c "VACUUM ANALYZE" ``` ### 3.3 Connection Pooling with PgBouncer ```ini # pgbouncer.ini [databases] fotbal_club = host=localhost port=5432 dbname=fotbal_club [pgbouncer] listen_port = 6432 listen_addr = * auth_type = md5 auth_file = /etc/pgbouncer/userlist.txt pool_mode = transaction max_client_conn = 1000 default_pool_size = 20 ``` --- ## 4. Monitoring & Profiling ### 4.1 Go Profiling ```go import _ "net/http/pprof" func main() { // Enable pprof in development if config.AppConfig.Debug { go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) }() } // ... rest of main } ``` Access profiles: - CPU: `http://localhost:6060/debug/pprof/profile?seconds=30` - Memory: `http://localhost:6060/debug/pprof/heap` - Goroutines: `http://localhost:6060/debug/pprof/goroutine` Analyze: ```bash go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30 ``` ### 4.2 Frontend Performance Monitoring ```typescript // frontend/src/reportWebVitals.ts import { Metric } from 'web-vitals'; const reportWebVitals = (onPerfEntry?: (metric: Metric) => void) => { if (onPerfEntry && onPerfEntry instanceof Function) { import('web-vitals').then(({ onCLS, onFID, onFCP, onLCP, onTTFB }) => { onCLS(onPerfEntry); onFID(onPerfEntry); onFCP(onPerfEntry); onLCP(onPerfEntry); onTTFB(onPerfEntry); }); } }; // Send to analytics reportWebVitals((metric) => { console.log(metric); // Send to analytics endpoint fetch('/api/v1/analytics/vitals', { method: 'POST', body: JSON.stringify(metric), }); }); ``` ### 4.3 Database Query Logging ```go // Enable query logging in development if config.AppConfig.Debug { db = db.Debug() } // Or custom logger newLogger := logger.New( log.New(os.Stdout, "\r\n", log.LstdFlags), logger.Config{ SlowThreshold: 200 * time.Millisecond, LogLevel: logger.Warn, IgnoreRecordNotFoundError: true, Colorful: true, }, ) db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{ Logger: newLogger, }) ``` --- ## 5. CDN & Static Assets ### 5.1 Use CDN for Static Assets ```nginx # nginx.conf location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; } ``` ### 5.2 Asset Versioning ```javascript // In build process - add hash to filenames output: { filename: '[name].[contenthash].js', chunkFilename: '[name].[contenthash].chunk.js', } ``` --- ## 6. Performance Budget Set performance budgets to prevent regression: ```javascript // budget.json { "budgets": [ { "resourceSizes": [ { "resourceType": "script", "budget": 300 }, { "resourceType": "total", "budget": 500 } ], "resourceCounts": [ { "resourceType": "third-party", "budget": 10 } ] } ] } ``` --- ## Performance Checklist ### Frontend - [ ] Code splitting implemented - [ ] Images optimized (WebP, responsive) - [ ] Lazy loading for images/components - [ ] Fonts optimized (display=swap, preconnect) - [ ] Bundle size < 300KB (gzipped) - [ ] No console.logs in production - [ ] Service worker for caching - [ ] Debounced search inputs ### Backend - [ ] Database indexes on query columns - [ ] Connection pooling configured - [ ] Response compression enabled - [ ] Cache headers set appropriately - [ ] N+1 queries eliminated - [ ] API pagination implemented - [ ] Redis cache for hot data - [ ] Slow query logging enabled ### Database - [ ] Indexes created - [ ] Regular VACUUM ANALYZE - [ ] PgBouncer for connection pooling - [ ] Query performance analyzed - [ ] Backup strategy defined ### Monitoring - [ ] Application metrics tracked - [ ] Error tracking configured - [ ] Performance alerts set - [ ] Database metrics monitored - [ ] CDN/cache hit rates tracked --- ## Expected Improvements After implementing these optimizations: - **Page Load Time**: 50-70% faster - **Time to Interactive**: 60-80% faster - **First Contentful Paint**: 40-60% faster - **Bundle Size**: 50-70% smaller - **Server Response Time**: 30-50% faster - **Database Query Time**: 40-70% faster - **Lighthouse Score**: 90+ on all metrics