# 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 */}
);
};
```
### 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 ? (

) : (
)}
);
};
```
### 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