Morten Sæther

← Blog

Episode 20: Performance Optimization & Monitoring

18 May 2026

Building Lightning-Fast, Scalable Portal Experiences

Introduction

Performance is critical for user adoption and business success. This comprehensive episode covers advanced performance optimization techniques including intelligent caching strategies, image optimization, code splitting, real-time monitoring, and scalability planning. You’ll learn to build portals that deliver exceptional performance across all devices and user scenarios while maintaining comprehensive monitoring for proactive issue detection.

Key Concepts

Learning Objectives

By the end of this episode, you will be able to:

Why Performance Optimization Matters

Portal performance directly impacts user satisfaction, conversion rates, search engine rankings, and business outcomes. Slow portals lose users, reduce engagement, and damage brand reputation. Advanced performance optimization ensures your portal provides exceptional experiences that keep users engaged and drive business success.

Step-by-Step Guide

1. Advanced Caching Architecture Implementation

Multi-Level Caching System:

// Advanced Caching Manager
class AdvancedCacheManager {
    constructor(config) {
        this.config = config;
        this.cacheStrategies = {
            browser: new BrowserCacheStrategy(),
            cdn: new CDNCacheStrategy(),
            application: new ApplicationCacheStrategy(),
            database: new DatabaseCacheStrategy()
        };
        this.performanceMonitor = new PerformanceMonitor();
        this.invalidationQueue = new InvalidationQueue();
    }
    
    // Intelligent cache retrieval with fallback strategy
    async get(key, options = {}) {
        const cacheKey = this.generateCacheKey(key, options);
        const strategy = options.strategy || this.determineBestStrategy(key, options);
        
        try {
            // Start performance measurement
            const startTime = performance.now();
            
            // Try cache layers in order of speed
            const cacheOrder = this.getCacheOrder(strategy);
            
            for (const cacheLevel of cacheOrder) {
                const result = await this.cacheStrategies[cacheLevel].get(cacheKey);
                
                if (result) {
                    // Record cache hit
                    const duration = performance.now() - startTime;
                    await this.performanceMonitor.recordCacheHit(cacheLevel, duration);
                    
                    // Populate higher-speed caches if hit was from slower cache
                    if (cacheOrder.indexOf(cacheLevel) > 0) {
                        await this.populateUpstreamCaches(cacheKey, result, cacheOrder, cacheLevel);
                    }
                    
                    return {
                        data: result.data,
                        metadata: {
                            cacheLevel: cacheLevel,
                            timestamp: result.timestamp,
                            ttl: result.ttl,
                            performance: { duration }
                        }
                    };
                }
            }
            
            // Cache miss - record and return null
            const duration = performance.now() - startTime;
            await this.performanceMonitor.recordCacheMiss(cacheKey, duration);
            
            return null;
            
        } catch (error) {
            await this.performanceMonitor.recordCacheError(cacheKey, error);
            throw new CacheError(`Cache retrieval failed: ${error.message}`);
        }
    }
    
    // Intelligent cache storage with optimal TTL
    async set(key, data, options = {}) {
        const cacheKey = this.generateCacheKey(key, options);
        const strategy = options.strategy || this.determineBestStrategy(key, options);
        const ttl = options.ttl || this.calculateOptimalTTL(key, data, options);
        
        try {
            const startTime = performance.now();
            
            // Prepare cache entry
            const cacheEntry = {
                data: data,
                timestamp: Date.now(),
                ttl: ttl,
                metadata: {
                    size: this.calculateDataSize(data),
                    compression: options.compression || 'gzip',
                    tags: options.tags || []
                }
            };
            
            // Compress data if beneficial
            if (cacheEntry.metadata.size > this.config.compressionThreshold) {
                cacheEntry.data = await this.compressData(data, cacheEntry.metadata.compression);
                cacheEntry.metadata.compressed = true;
            }
            
            // Store in appropriate cache levels
            const cacheOrder = this.getCacheOrder(strategy);
            const storagePromises = cacheOrder.map(async (cacheLevel) => {
                const levelConfig = this.config.levels[cacheLevel];
                const adjustedTTL = this.adjustTTLForLevel(ttl, cacheLevel);
                
                return this.cacheStrategies[cacheLevel].set(
                    cacheKey, 
                    cacheEntry, 
                    adjustedTTL,
                    levelConfig
                );
            });
            
            await Promise.allSettled(storagePromises);
            
            // Record performance
            const duration = performance.now() - startTime;
            await this.performanceMonitor.recordCacheWrite(cacheKey, duration, cacheEntry.metadata.size);
            
            // Schedule invalidation if needed
            if (options.invalidateAfter) {
                await this.scheduleInvalidation(cacheKey, options.invalidateAfter);
            }
            
            return {
                success: true,
                cacheKey: cacheKey,
                ttl: ttl,
                performance: { duration }
            };
            
        } catch (error) {
            await this.performanceMonitor.recordCacheError(cacheKey, error);
            throw new CacheError(`Cache storage failed: ${error.message}`);
        }
    }
    
    // Smart cache invalidation with dependency tracking
    async invalidate(pattern, options = {}) {
        try {
            const startTime = performance.now();
            
            // Resolve cache keys to invalidate
            const keysToInvalidate = await this.resolveCacheKeys(pattern, options);
            
            if (keysToInvalidate.length === 0) {
                return { invalidated: 0 };
            }
            
            // Batch invalidation for efficiency
            const batchSize = this.config.invalidationBatchSize || 100;
            const batches = this.createBatches(keysToInvalidate, batchSize);
            
            let totalInvalidated = 0;
            
            for (const batch of batches) {
                const invalidationPromises = Object.keys(this.cacheStrategies).map(async (cacheLevel) => {
                    return this.cacheStrategies[cacheLevel].invalidateBatch(batch);
                });
                
                const results = await Promise.allSettled(invalidationPromises);
                totalInvalidated += batch.length;
                
                // Log any invalidation failures
                results.forEach((result, index) => {
                    if (result.status === 'rejected') {
                        console.error(`Invalidation failed for ${Object.keys(this.cacheStrategies)[index]}:`, result.reason);
                    }
                });
            }
            
            // Record performance
            const duration = performance.now() - startTime;
            await this.performanceMonitor.recordCacheInvalidation(pattern, totalInvalidated, duration);
            
            // Handle dependent cache invalidation
            if (options.cascadeInvalidation) {
                await this.handleDependentInvalidation(keysToInvalidate);
            }
            
            return {
                invalidated: totalInvalidated,
                performance: { duration }
            };
            
        } catch (error) {
            await this.performanceMonitor.recordCacheError(pattern, error);
            throw new CacheError(`Cache invalidation failed: ${error.message}`);
        }
    }
    
    // Determine optimal caching strategy based on data characteristics
    determineBestStrategy(key, options) {
        const dataType = options.dataType || this.inferDataType(key);
        const accessPattern = options.accessPattern || this.analyzeAccessPattern(key);
        const updateFrequency = options.updateFrequency || this.getUpdateFrequency(key);
        
        // High-frequency read, low-frequency write: Multi-level with long TTL
        if (accessPattern.reads > 100 && updateFrequency < 0.1) {
            return 'aggressive';
        }
        
        // Frequently updated: Browser cache only with short TTL
        if (updateFrequency > 10) {
            return 'conservative';
        }
        
        // User-specific data: Application cache with medium TTL
        if (dataType === 'user_specific') {
            return 'user_focused';
        }
        
        // Static content: Full CDN caching
        if (dataType === 'static') {
            return 'static_content';
        }
        
        return 'balanced';
    }
    
    // Calculate optimal TTL based on data characteristics and usage patterns
    calculateOptimalTTL(key, data, options) {
        const baselineMetrics = this.getBaselineTTL(options.dataType);
        const usagePattern = this.analyzeUsagePattern(key);
        const dataVolatility = this.assessDataVolatility(data, options);
        
        let optimalTTL = baselineMetrics.ttl;
        
        // Adjust based on access frequency
        if (usagePattern.accessFrequency > 50) {
            optimalTTL *= 1.5; // Cache longer for frequently accessed data
        }
        
        // Adjust based on data volatility
        if (dataVolatility > 0.8) {
            optimalTTL *= 0.5; // Shorter cache for volatile data
        }
        
        // Adjust based on data size
        const dataSize = this.calculateDataSize(data);
        if (dataSize > this.config.largeCacheThreshold) {
            optimalTTL *= 1.2; // Cache larger items longer to amortize storage cost
        }
        
        // Respect configured limits
        return Math.max(
            this.config.minTTL,
            Math.min(optimalTTL, this.config.maxTTL)
        );
    }
}

// Browser Cache Strategy Implementation
class BrowserCacheStrategy {
    constructor() {
        this.storage = {
            memory: new Map(),
            sessionStorage: window.sessionStorage,
            localStorage: window.localStorage,
            indexedDB: null
        };
        
        this.initializeIndexedDB();
    }
    
    async initializeIndexedDB() {
        if ('indexedDB' in window) {
            try {
                this.storage.indexedDB = await this.openIndexedDB();
            } catch (error) {
                console.warn('IndexedDB initialization failed:', error);
            }
        }
    }
    
    async get(key) {
        // Try memory cache first (fastest)
        if (this.storage.memory.has(key)) {
            const entry = this.storage.memory.get(key);
            if (Date.now() < entry.expiry) {
                return entry;
            } else {
                this.storage.memory.delete(key);
            }
        }
        
        // Try IndexedDB (larger capacity)
        if (this.storage.indexedDB) {
            try {
                const entry = await this.getFromIndexedDB(key);
                if (entry && Date.now() < entry.expiry) {
                    // Populate memory cache for faster subsequent access
                    this.storage.memory.set(key, entry);
                    return entry;
                }
            } catch (error) {
                console.warn('IndexedDB read error:', error);
            }
        }
        
        // Try localStorage (persistent)
        try {
            const stored = this.storage.localStorage.getItem(key);
            if (stored) {
                const entry = JSON.parse(stored);
                if (Date.now() < entry.expiry) {
                    this.storage.memory.set(key, entry);
                    return entry;
                } else {
                    this.storage.localStorage.removeItem(key);
                }
            }
        } catch (error) {
            console.warn('localStorage read error:', error);
        }
        
        return null;
    }
    
    async set(key, data, ttl, config = {}) {
        const expiry = Date.now() + (ttl * 1000);
        const entry = {
            data: data,
            timestamp: Date.now(),
            expiry: expiry,
            ttl: ttl
        };
        
        // Always store in memory for fastest access
        this.storage.memory.set(key, entry);
        
        // Manage memory cache size
        if (this.storage.memory.size > 1000) {
            this.evictOldestEntries(100);
        }
        
        // Store in IndexedDB for larger items
        const dataSize = this.calculateSize(data);
        if (dataSize > 1024 && this.storage.indexedDB) {
            try {
                await this.setInIndexedDB(key, entry);
            } catch (error) {
                console.warn('IndexedDB write error:', error);
            }
        }
        
        // Store in localStorage for persistence (smaller items only)
        if (dataSize < 5120) { // 5KB limit for localStorage
            try {
                this.storage.localStorage.setItem(key, JSON.stringify(entry));
            } catch (error) {
                if (error.name === 'QuotaExceededError') {
                    this.cleanupLocalStorage();
                    try {
                        this.storage.localStorage.setItem(key, JSON.stringify(entry));
                    } catch (retryError) {
                        console.warn('localStorage write failed after cleanup:', retryError);
                    }
                }
            }
        }
        
        return true;
    }
    
    evictOldestEntries(count) {
        const entries = Array.from(this.storage.memory.entries())
            .sort((a, b) => a[1].timestamp - b[1].timestamp);
        
        for (let i = 0; i < count && i < entries.length; i++) {
            this.storage.memory.delete(entries[i][0]);
        }
    }
    
    calculateSize(data) {
        return JSON.stringify(data).length;
    }
}

CDN Cache Integration:

// CDN Cache Strategy with Edge Computing
class CDNCacheStrategy {
    constructor(config) {
        this.config = config;
        this.edgeLocations = config.edgeLocations || [];
        this.distributionDomain = config.distributionDomain;
        this.performanceMonitor = new CDNPerformanceMonitor();
    }
    
    async get(key) {
        const cacheUrl = this.buildCacheUrl(key);
        
        try {
            const startTime = performance.now();
            
            // Use fetch with cache-first strategy
            const response = await fetch(cacheUrl, {
                method: 'GET',
                headers: {
                    'Cache-Control': 'max-age=3600',
                    'X-Cache-Key': key
                },
                cache: 'force-cache'
            });
            
            if (response.ok) {
                const data = await response.json();
                const duration = performance.now() - startTime;
                
                // Record CDN performance
                await this.performanceMonitor.recordCDNHit(key, duration, response.headers);
                
                return {
                    data: data,
                    timestamp: Date.now(),
                    source: 'cdn',
                    edge: response.headers.get('X-Edge-Location'),
                    cacheStatus: response.headers.get('X-Cache-Status')
                };
            }
            
            return null;
            
        } catch (error) {
            await this.performanceMonitor.recordCDNError(key, error);
            return null;
        }
    }
    
    async set(key, data, ttl, config = {}) {
        const cacheUrl = this.buildCacheUrl(key);
        
        try {
            const startTime = performance.now();
            
            // Store in CDN with appropriate headers
            const response = await fetch(cacheUrl, {
                method: 'PUT',
                headers: {
                    'Content-Type': 'application/json',
                    'Cache-Control': `max-age=${ttl}`,
                    'X-Cache-TTL': ttl.toString(),
                    'X-Cache-Tags': (config.tags || []).join(',')
                },
                body: JSON.stringify(data)
            });
            
            const duration = performance.now() - startTime;
            
            if (response.ok) {
                await this.performanceMonitor.recordCDNWrite(key, duration, JSON.stringify(data).length);
                return true;
            }
            
            return false;
            
        } catch (error) {
            await this.performanceMonitor.recordCDNError(key, error);
            return false;
        }
    }
    
    // Intelligent cache warming
    async warmCache(keys, priority = 'normal') {
        const warmingPromises = keys.map(async (key) => {
            try {
                // Pre-fetch content to edge locations
                const warmingUrl = this.buildWarmingUrl(key, priority);
                
                const response = await fetch(warmingUrl, {
                    method: 'POST',
                    headers: {
                        'X-Cache-Warming': 'true',
                        'X-Priority': priority
                    }
                });
                
                return {
                    key: key,
                    success: response.ok,
                    edges: response.headers.get('X-Warmed-Edges')?.split(',') || []
                };
                
            } catch (error) {
                return {
                    key: key,
                    success: false,
                    error: error.message
                };
            }
        });
        
        const results = await Promise.allSettled(warmingPromises);
        
        return {
            total: keys.length,
            successful: results.filter(r => r.status === 'fulfilled' && r.value.success).length,
            failed: results.filter(r => r.status === 'rejected' || !r.value.success).length,
            details: results.map(r => r.status === 'fulfilled' ? r.value : { error: r.reason })
        };
    }
    
    buildCacheUrl(key) {
        return `https://${this.distributionDomain}/cache/${encodeURIComponent(key)}`;
    }
    
    buildWarmingUrl(key, priority) {
        return `https://${this.distributionDomain}/warm/${encodeURIComponent(key)}?priority=${priority}`;
    }
}

2. Image Optimization and Asset Management

Intelligent Image Optimization:

// Advanced Image Optimization Manager
class ImageOptimizationManager {
    constructor(config) {
        this.config = config;
        this.supportedFormats = ['webp', 'avif', 'jpeg', 'png'];
        this.deviceProfiles = this.initializeDeviceProfiles();
        this.compressionWorker = new CompressionWorker();
    }
    
    // Intelligent image serving with format selection
    async optimizeImageDelivery(imageUrl, context = {}) {
        try {
            const deviceProfile = this.getDeviceProfile(context);
            const optimalFormat = this.selectOptimalFormat(context.supportedFormats, deviceProfile);
            const optimalSize = this.calculateOptimalSize(deviceProfile, context.containerSize);
            
            // Build optimized image URL
            const optimizedUrl = this.buildOptimizedUrl(imageUrl, {
                format: optimalFormat,
                width: optimalSize.width,
                height: optimalSize.height,
                quality: this.getOptimalQuality(optimalFormat, deviceProfile),
                compression: this.getCompressionSettings(optimalFormat, deviceProfile)
            });
            
            return {
                url: optimizedUrl,
                format: optimalFormat,
                size: optimalSize,
                estimatedSize: this.estimateFileSize(optimalSize, optimalFormat),
                fallbackUrl: this.buildFallbackUrl(imageUrl, optimalSize)
            };
            
        } catch (error) {
            console.error('Image optimization failed:', error);
            return { url: imageUrl, fallback: true };
        }
    }
    
    // Progressive image loading implementation
    createProgressiveImage(container, imageOptions) {
        const img = document.createElement('img');
        const placeholder = this.createPlaceholder(imageOptions);
        
        // Insert placeholder first
        container.appendChild(placeholder);
        
        // Create image loading strategy
        const loadingStrategy = this.determineLoadingStrategy(imageOptions);
        
        switch (loadingStrategy) {
            case 'lazy':
                this.implementLazyLoading(img, imageOptions, placeholder);
                break;
            case 'progressive':
                this.implementProgressiveLoading(img, imageOptions, placeholder);
                break;
            case 'immediate':
                this.loadImage(img, imageOptions, placeholder);
                break;
        }
        
        return img;
    }
    
    // Lazy loading with Intersection Observer
    implementLazyLoading(img, imageOptions, placeholder) {
        const observer = new IntersectionObserver((entries) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    this.loadImage(img, imageOptions, placeholder);
                    observer.unobserve(entry.target);
                }
            });
        }, {
            rootMargin: '50px' // Start loading 50px before image enters viewport
        });
        
        observer.observe(placeholder);
    }
    
    // Progressive loading with low-quality preview
    implementProgressiveLoading(img, imageOptions, placeholder) {
        // Load low-quality preview first
        const previewOptions = {
            ...imageOptions,
            quality: 20,
            blur: 5
        };
        
        const preview = new Image();
        preview.onload = () => {
            placeholder.style.backgroundImage = `url(${preview.src})`;
            placeholder.style.filter = 'blur(5px)';
            
            // Load full-quality image
            this.loadImage(img, imageOptions, placeholder);
        };
        
        preview.src = this.buildOptimizedUrl(imageOptions.src, previewOptions);
    }
    
    // Full image loading with error handling
    loadImage(img, imageOptions, placeholder) {
        const startTime = performance.now();
        
        img.onload = () => {
            const loadTime = performance.now() - startTime;
            
            // Apply smooth transition
            img.style.opacity = '0';
            img.style.transition = 'opacity 0.3s ease-in-out';
            
            placeholder.parentNode.replaceChild(img, placeholder);
            
            // Fade in the image
            requestAnimationFrame(() => {
                img.style.opacity = '1';
            });
            
            // Record performance metrics
            this.recordImageLoadMetrics(imageOptions.src, loadTime, img.naturalWidth, img.naturalHeight);
        };
        
        img.onerror = () => {
            // Load fallback image
            if (imageOptions.fallback) {
                img.src = imageOptions.fallback;
            } else {
                placeholder.classList.add('image-error');
                placeholder.innerHTML = '<span>Image unavailable</span>';
            }
        };
        
        // Set up responsive source sets
        if (imageOptions.responsive) {
            this.setupResponsiveSources(img, imageOptions);
        }
        
        img.src = imageOptions.optimizedUrl;
        img.alt = imageOptions.alt || '';
        img.className = imageOptions.className || '';
    }
    
    // Create responsive source sets
    setupResponsiveSources(img, imageOptions) {
        const picture = document.createElement('picture');
        
        // Add sources for different screen densities and sizes
        const breakpoints = [
            { media: '(max-width: 768px)', width: 768 },
            { media: '(max-width: 1024px)', width: 1024 },
            { media: '(min-width: 1025px)', width: 1920 }
        ];
        
        breakpoints.forEach(breakpoint => {
            const source = document.createElement('source');
            source.media = breakpoint.media;
            
            // Generate srcset for different densities
            const srcset = [1, 1.5, 2].map(density => {
                const width = Math.round(breakpoint.width * density);
                const url = this.buildOptimizedUrl(imageOptions.src, {
                    ...imageOptions,
                    width: width
                });
                return `${url} ${density}x`;
            }).join(', ');
            
            source.srcset = srcset;
            picture.appendChild(source);
        });
        
        picture.appendChild(img);
        return picture;
    }
    
    selectOptimalFormat(supportedFormats, deviceProfile) {
        // Check browser support and device capabilities
        const browserSupported = supportedFormats || this.detectSupportedFormats();
        
        // Prefer newer, more efficient formats
        if (browserSupported.includes('avif') && deviceProfile.modernCodecs) {
            return 'avif';
        }
        
        if (browserSupported.includes('webp')) {
            return 'webp';
        }
        
        // Fallback to traditional formats
        return deviceProfile.preferJPEG ? 'jpeg' : 'png';
    }
    
    calculateOptimalSize(deviceProfile, containerSize) {
        const dpr = window.devicePixelRatio || 1;
        const baseWidth = containerSize?.width || deviceProfile.defaultWidth;
        const baseHeight = containerSize?.height || deviceProfile.defaultHeight;
        
        // Account for device pixel ratio but cap at reasonable limits
        const maxDPR = deviceProfile.highDensity ? 2 : 1.5;
        const effectiveDPR = Math.min(dpr, maxDPR);
        
        return {
            width: Math.round(baseWidth * effectiveDPR),
            height: Math.round(baseHeight * effectiveDPR),
            dpr: effectiveDPR
        };
    }
    
    getOptimalQuality(format, deviceProfile) {
        const baseQuality = {
            'avif': 75,
            'webp': 80,
            'jpeg': 85,
            'png': 95
        };
        
        let quality = baseQuality[format] || 85;
        
        // Adjust for device characteristics
        if (deviceProfile.lowBandwidth) {
            quality -= 15; // Lower quality for slow connections
        }
        
        if (deviceProfile.highDensity) {
            quality -= 5; // Slight reduction for high-DPI displays
        }
        
        return Math.max(30, Math.min(95, quality));
    }
}

3. Real-Time Performance Monitoring

Comprehensive Performance Monitoring System:

// Advanced Performance Monitor
class AdvancedPerformanceMonitor {
    constructor(config) {
        this.config = config;
        this.metrics = new Map();
        this.observers = new Map();
        this.alertThresholds = config.alertThresholds || {};
        this.reportingEndpoint = config.reportingEndpoint;
        
        this.initializeMonitoring();
    }
    
    initializeMonitoring() {
        // Core Web Vitals monitoring
        this.setupCoreWebVitals();
        
        // Custom performance metrics
        this.setupCustomMetrics();
        
        // Resource timing monitoring
        this.setupResourceTiming();
        
        // Error tracking
        this.setupErrorTracking();
        
        // User interaction monitoring
        this.setupInteractionTracking();
        
        // Start reporting
        this.startPeriodicReporting();
    }
    
    setupCoreWebVitals() {
        // Largest Contentful Paint (LCP)
        this.observeLCP();
        
        // First Input Delay (FID)
        this.observeFID();
        
        // Cumulative Layout Shift (CLS)
        this.observeCLS();
        
        // First Contentful Paint (FCP)
        this.observeFCP();
        
        // Time to First Byte (TTFB)
        this.observeTTFB();
    }
    
    observeLCP() {
        if ('PerformanceObserver' in window) {
            const observer = new PerformanceObserver((entryList) => {
                const entries = entryList.getEntries();
                const lastEntry = entries[entries.length - 1];
                
                this.recordMetric('lcp', {
                    value: lastEntry.startTime,
                    rating: this.rateLCP(lastEntry.startTime),
                    element: lastEntry.element?.tagName,
                    url: lastEntry.url,
                    timestamp: Date.now()
                });
                
                // Check for performance issues
                if (lastEntry.startTime > this.alertThresholds.lcp) {
                    this.triggerAlert('lcp_threshold_exceeded', {
                        value: lastEntry.startTime,
                        threshold: this.alertThresholds.lcp
                    });
                }
            });
            
            observer.observe({ entryTypes: ['largest-contentful-paint'] });
            this.observers.set('lcp', observer);
        }
    }
    
    observeFID() {
        if ('PerformanceObserver' in window) {
            const observer = new PerformanceObserver((entryList) => {
                const entries = entryList.getEntries();
                
                entries.forEach(entry => {
                    this.recordMetric('fid', {
                        value: entry.processingStart - entry.startTime,
                        rating: this.rateFID(entry.processingStart - entry.startTime),
                        eventType: entry.name,
                        timestamp: Date.now()
                    });
                });
            });
            
            observer.observe({ entryTypes: ['first-input'] });
            this.observers.set('fid', observer);
        }
    }
    
    observeCLS() {
        if ('PerformanceObserver' in window) {
            let clsValue = 0;
            let sessionValue = 0;
            let sessionEntries = [];
            
            const observer = new PerformanceObserver((entryList) => {
                const entries = entryList.getEntries();
                
                entries.forEach(entry => {
                    // Only count layout shifts without recent user input
                    if (!entry.hadRecentInput) {
                        const firstSessionEntry = sessionEntries[0];
                        const lastSessionEntry = sessionEntries[sessionEntries.length - 1];
                        
                        // If the entry occurred less than 1 second after the previous entry
                        // and less than 5 seconds after the first entry in the session,
                        // include it in the current session
                        if (sessionValue &&
                            entry.startTime - lastSessionEntry.startTime < 1000 &&
                            entry.startTime - firstSessionEntry.startTime < 5000) {
                            sessionValue += entry.value;
                            sessionEntries.push(entry);
                        } else {
                            sessionValue = entry.value;
                            sessionEntries = [entry];
                        }
                        
                        // If the current session value is larger than the current CLS value,
                        // update CLS and report it
                        if (sessionValue > clsValue) {
                            clsValue = sessionValue;
                            
                            this.recordMetric('cls', {
                                value: clsValue,
                                rating: this.rateCLS(clsValue),
                                entries: sessionEntries.length,
                                timestamp: Date.now()
                            });
                        }
                    }
                });
            });
            
            observer.observe({ entryTypes: ['layout-shift'] });
            this.observers.set('cls', observer);
        }
    }
    
    setupCustomMetrics() {
        // Time to Interactive (TTI)
        this.measureTTI();
        
        // Custom loading metrics
        this.measureCustomTimings();
        
        // JavaScript execution time
        this.measureJavaScriptPerformance();
        
        // Memory usage monitoring
        this.monitorMemoryUsage();
    }
    
    measureTTI() {
        // Simplified TTI measurement
        const observer = new PerformanceObserver((entryList) => {
            const entries = entryList.getEntries();
            
            // Look for long tasks after main content load
            entries.forEach(entry => {
                if (entry.duration > 50) { // Long task threshold
                    this.recordMetric('long_task', {
                        duration: entry.duration,
                        startTime: entry.startTime,
                        name: entry.name,
                        timestamp: Date.now()
                    });
                }
            });
        });
        
        if ('PerformanceObserver' in window) {
            observer.observe({ entryTypes: ['longtask'] });
            this.observers.set('longtask', observer);
        }
    }
    
    measureCustomTimings() {
        // Measure critical application milestones
        window.addEventListener('load', () => {
            // DOM ready time
            const domReady = performance.timing.domContentLoadedEventEnd - performance.timing.navigationStart;
            this.recordMetric('dom_ready', { value: domReady, timestamp: Date.now() });
            
            // Full page load time
            const pageLoad = performance.timing.loadEventEnd - performance.timing.navigationStart;
            this.recordMetric('page_load', { value: pageLoad, timestamp: Date.now() });
            
            // JavaScript bundle size and load time
            this.measureBundlePerformance();
        });
    }
    
    measureBundlePerformance() {
        const scriptEntries = performance.getEntriesByType('resource')
            .filter(entry => entry.initiatorType === 'script');
        
        let totalBundleSize = 0;
        let totalLoadTime = 0;
        
        scriptEntries.forEach(entry => {
            totalBundleSize += entry.transferSize || 0;
            totalLoadTime += entry.duration;
            
            this.recordMetric('script_load', {
                url: entry.name,
                size: entry.transferSize,
                duration: entry.duration,
                timestamp: Date.now()
            });
        });
        
        this.recordMetric('bundle_performance', {
            totalSize: totalBundleSize,
            totalLoadTime: totalLoadTime,
            scriptCount: scriptEntries.length,
            timestamp: Date.now()
        });
    }
    
    monitorMemoryUsage() {
        if ('memory' in performance) {
            const measureMemory = () => {
                const memInfo = performance.memory;
                
                this.recordMetric('memory_usage', {
                    usedJSSize: memInfo.usedJSSize,
                    totalJSSize: memInfo.totalJSSize,
                    jsLimit: memInfo.jsHeapSizeLimit,
                    utilization: (memInfo.usedJSSize / memInfo.jsHeapSizeLimit) * 100,
                    timestamp: Date.now()
                });
                
                // Alert on high memory usage
                const utilization = (memInfo.usedJSSize / memInfo.jsHeapSizeLimit) * 100;
                if (utilization > 90) {
                    this.triggerAlert('high_memory_usage', {
                        utilization: utilization,
                        usedMB: Math.round(memInfo.usedJSSize / 1024 / 1024)
                    });
                }
            };
            
            // Measure initially and then periodically
            measureMemory();
            setInterval(measureMemory, 30000); // Every 30 seconds
        }
    }
    
    setupResourceTiming() {
        const observer = new PerformanceObserver((entryList) => {
            const entries = entryList.getEntries();
            
            entries.forEach(entry => {
                // Analyze resource loading performance
                const resourceMetrics = {
                    name: entry.name,
                    type: entry.initiatorType,
                    size: entry.transferSize,
                    duration: entry.duration,
                    domainLookup: entry.domainLookupEnd - entry.domainLookupStart,
                    connect: entry.connectEnd - entry.connectStart,
                    request: entry.responseStart - entry.requestStart,
                    response: entry.responseEnd - entry.responseStart,
                    timestamp: Date.now()
                };
                
                this.recordMetric('resource_timing', resourceMetrics);
                
                // Check for slow resources
                if (entry.duration > this.alertThresholds.slowResource) {
                    this.triggerAlert('slow_resource', {
                        resource: entry.name,
                        duration: entry.duration,
                        type: entry.initiatorType
                    });
                }
            });
        });
        
        observer.observe({ entryTypes: ['resource'] });
        this.observers.set('resource', observer);
    }
    
    recordMetric(name, data) {
        if (!this.metrics.has(name)) {
            this.metrics.set(name, []);
        }
        
        const metricData = {
            ...data,
            timestamp: data.timestamp || Date.now(),
            sessionId: this.getSessionId(),
            userId: this.getUserId(),
            pageUrl: window.location.href,
            userAgent: navigator.userAgent
        };
        
        this.metrics.get(name).push(metricData);
        
        // Keep only recent metrics in memory
        const maxEntries = 1000;
        const entries = this.metrics.get(name);
        if (entries.length > maxEntries) {
            entries.splice(0, entries.length - maxEntries);
        }
        
        // Trigger real-time processing
        this.processMetricInRealTime(name, metricData);
    }
    
    processMetricInRealTime(name, data) {
        // Calculate running averages and trends
        const entries = this.metrics.get(name);
        
        if (entries.length >= 5) {
            const recent = entries.slice(-5);
            const average = recent.reduce((sum, entry) => sum + (entry.value || 0), 0) / recent.length;
            const trend = this.calculateTrend(recent);
            
            // Check for performance degradation
            if (trend.direction === 'worsening' && trend.magnitude > 0.2) {
                this.triggerAlert('performance_degradation', {
                    metric: name,
                    trend: trend,
                    currentAverage: average
                });
            }
        }
    }
    
    startPeriodicReporting() {
        // Send metrics to backend every 60 seconds
        setInterval(() => {
            this.sendMetricsReport();
        }, 60000);
        
        // Send report before page unload
        window.addEventListener('beforeunload', () => {
            this.sendMetricsReport(true);
        });
    }
    
    async sendMetricsReport(immediate = false) {
        if (!this.reportingEndpoint) return;
        
        const report = this.generateMetricsReport();
        
        try {
            if (immediate && 'sendBeacon' in navigator) {
                // Use sendBeacon for reliable delivery during page unload
                navigator.sendBeacon(this.reportingEndpoint, JSON.stringify(report));
            } else {
                // Regular fetch for periodic reporting
                await fetch(this.reportingEndpoint, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify(report)
                });
            }
            
            // Clear sent metrics
            this.clearOldMetrics();
            
        } catch (error) {
            console.error('Failed to send performance report:', error);
        }
    }
    
    generateMetricsReport() {
        const report = {
            sessionId: this.getSessionId(),
            userId: this.getUserId(),
            timestamp: Date.now(),
            url: window.location.href,
            userAgent: navigator.userAgent,
            connection: this.getConnectionInfo(),
            metrics: {}
        };
        
        // Aggregate metrics
        this.metrics.forEach((entries, name) => {
            if (entries.length === 0) return;
            
            const values = entries.map(entry => entry.value).filter(v => v !== undefined);
            
            if (values.length > 0) {
                report.metrics[name] = {
                    count: entries.length,
                    average: values.reduce((sum, val) => sum + val, 0) / values.length,
                    min: Math.min(...values),
                    max: Math.max(...values),
                    latest: entries[entries.length - 1],
                    p50: this.calculatePercentile(values, 50),
                    p90: this.calculatePercentile(values, 90),
                    p95: this.calculatePercentile(values, 95)
                };
            }
        });
        
        return report;
    }
    
    rateLCP(value) {
        if (value <= 2500) return 'good';
        if (value <= 4000) return 'needs-improvement';
        return 'poor';
    }
    
    rateFID(value) {
        if (value <= 100) return 'good';
        if (value <= 300) return 'needs-improvement';
        return 'poor';
    }
    
    rateCLS(value) {
        if (value <= 0.1) return 'good';
        if (value <= 0.25) return 'needs-improvement';
        return 'poor';
    }
}

Practical Example: Complete Performance Dashboard

Let’s create a comprehensive performance monitoring dashboard:

<!-- performance-dashboard.html -->
<div class="performance-dashboard">
    <!-- Core Web Vitals Overview -->
    <div class="core-vitals-section">
        <h2>Core Web Vitals</h2>
        
        <div class="vitals-grid">
            <div class="vital-card lcp">
                <div class="vital-header">
                    <h3>Largest Contentful Paint</h3>
                    <span class="vital-info" title="Measures loading performance">ℹ️</span>
                </div>
                <div class="vital-value" id="lcp-value">--</div>
                <div class="vital-rating" id="lcp-rating">measuring...</div>
                <div class="vital-chart">
                    <canvas id="lcp-chart" width="200" height="60"></canvas>
                </div>
            </div>
            
            <div class="vital-card fid">
                <div class="vital-header">
                    <h3>First Input Delay</h3>
                    <span class="vital-info" title="Measures interactivity">ℹ️</span>
                </div>
                <div class="vital-value" id="fid-value">--</div>
                <div class="vital-rating" id="fid-rating">measuring...</div>
                <div class="vital-chart">
                    <canvas id="fid-chart" width="200" height="60"></canvas>
                </div>
            </div>
            
            <div class="vital-card cls">
                <div class="vital-header">
                    <h3>Cumulative Layout Shift</h3>
                    <span class="vital-info" title="Measures visual stability">ℹ️</span>
                </div>
                <div class="vital-value" id="cls-value">--</div>
                <div class="vital-rating" id="cls-rating">measuring...</div>
                <div class="vital-chart">
                    <canvas id="cls-chart" width="200" height="60"></canvas>
                </div>
            </div>
        </div>
    </div>
    
    <!-- Performance Metrics -->
    <div class="performance-metrics-section">
        <h3>Detailed Performance Metrics</h3>
        
        <div class="metrics-grid">
            <div class="metric-group">
                <h4>Loading Performance</h4>
                <div class="metric-item">
                    <span class="metric-label">Time to First Byte</span>
                    <span class="metric-value" id="ttfb-value">--</span>
                </div>
                <div class="metric-item">
                    <span class="metric-label">First Contentful Paint</span>
                    <span class="metric-value" id="fcp-value">--</span>
                </div>
                <div class="metric-item">
                    <span class="metric-label">DOM Content Loaded</span>
                    <span class="metric-value" id="dcl-value">--</span>
                </div>
                <div class="metric-item">
                    <span class="metric-label">Page Load Complete</span>
                    <span class="metric-value" id="load-value">--</span>
                </div>
            </div>
            
            <div class="metric-group">
                <h4>Resource Performance</h4>
                <div class="metric-item">
                    <span class="metric-label">Total Bundle Size</span>
                    <span class="metric-value" id="bundle-size">--</span>
                </div>
                <div class="metric-item">
                    <span class="metric-label">Script Load Time</span>
                    <span class="metric-value" id="script-load-time">--</span>
                </div>
                <div class="metric-item">
                    <span class="metric-label">Image Load Time</span>
                    <span class="metric-value" id="image-load-time">--</span>
                </div>
                <div class="metric-item">
                    <span class="metric-label">CSS Load Time</span>
                    <span class="metric-value" id="css-load-time">--</span>
                </div>
            </div>
            
            <div class="metric-group">
                <h4>Memory & CPU</h4>
                <div class="metric-item">
                    <span class="metric-label">Memory Usage</span>
                    <span class="metric-value" id="memory-usage">--</span>
                </div>
                <div class="metric-item">
                    <span class="metric-label">JavaScript Heap Size</span>
                    <span class="metric-value" id="heap-size">--</span>
                </div>
                <div class="metric-item">
                    <span class="metric-label">Long Tasks Count</span>
                    <span class="metric-value" id="long-tasks">--</span>
                </div>
                <div class="metric-item">
                    <span class="metric-label">CPU Utilization</span>
                    <span class="metric-value" id="cpu-usage">--</span>
                </div>
            </div>
        </div>
    </div>
    
    <!-- Real-time Performance Chart -->
    <div class="performance-chart-section">
        <h3>Real-time Performance Trends</h3>
        <div class="chart-controls">
            <select id="chart-metric">
                <option value="lcp">Largest Contentful Paint</option>
                <option value="fid">First Input Delay</option>
                <option value="cls">Cumulative Layout Shift</option>
                <option value="memory">Memory Usage</option>
            </select>
            <select id="chart-timeframe">
                <option value="5m">Last 5 minutes</option>
                <option value="15m">Last 15 minutes</option>
                <option value="1h">Last hour</option>
                <option value="24h">Last 24 hours</option>
            </select>
        </div>
        <div class="chart-container">
            <canvas id="performance-trend-chart" width="800" height="300"></canvas>
        </div>
    </div>
    
    <!-- Performance Insights -->
    <div class="performance-insights-section">
        <h3>Performance Insights & Recommendations</h3>
        <div id="insights-container">
            <!-- Insights will be populated dynamically -->
        </div>
    </div>
</div>

<script>
    // Performance Dashboard Implementation
    class PerformanceDashboard {
        constructor() {
            this.charts = new Map();
            this.metrics = new Map();
            this.performanceMonitor = new AdvancedPerformanceMonitor({
                reportingEndpoint: '/api/performance/metrics',
                alertThresholds: {
                    lcp: 2500,
                    fid: 100,
                    cls: 0.1,
                    slowResource: 1000
                }
            });
            
            this.initializeDashboard();
            this.startRealTimeUpdates();
        }
        
        initializeDashboard() {
            // Initialize charts
            this.initializeCharts();
            
            // Setup metric listeners
            this.setupMetricListeners();
            
            // Load initial data
            this.loadInitialMetrics();
            
            // Setup controls
            this.setupControls();
        }
        
        initializeCharts() {
            // Initialize Core Web Vitals charts
            this.charts.set('lcp', this.createVitalChart('lcp-chart', '#4285f4'));
            this.charts.set('fid', this.createVitalChart('fid-chart', '#34a853'));
            this.charts.set('cls', this.createVitalChart('cls-chart', '#fbbc04'));
            
            // Initialize main performance trend chart
            this.charts.set('trend', this.createTrendChart('performance-trend-chart'));
        }
        
        createVitalChart(canvasId, color) {
            const canvas = document.getElementById(canvasId);
            const ctx = canvas.getContext('2d');
            
            return new Chart(ctx, {
                type: 'line',
                data: {
                    labels: [],
                    datasets: [{
                        data: [],
                        borderColor: color,
                        backgroundColor: color + '20',
                        fill: true,
                        tension: 0.3
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    scales: {
                        x: { display: false },
                        y: { display: false }
                    },
                    plugins: {
                        legend: { display: false }
                    },
                    elements: {
                        point: { radius: 0 }
                    }
                }
            });
        }
        
        setupMetricListeners() {
            // Listen for Core Web Vitals updates
            this.performanceMonitor.on('lcp', (data) => this.updateVital('lcp', data));
            this.performanceMonitor.on('fid', (data) => this.updateVital('fid', data));
            this.performanceMonitor.on('cls', (data) => this.updateVital('cls', data));
            
            // Listen for other performance metrics
            this.performanceMonitor.on('memory_usage', (data) => this.updateMemoryMetrics(data));
            this.performanceMonitor.on('bundle_performance', (data) => this.updateBundleMetrics(data));
            this.performanceMonitor.on('resource_timing', (data) => this.updateResourceMetrics(data));
        }
        
        updateVital(vitalName, data) {
            // Update vital card display
            document.getElementById(`${vitalName}-value`).textContent = this.formatVitalValue(vitalName, data.value);
            document.getElementById(`${vitalName}-rating`).textContent = data.rating;
            document.getElementById(`${vitalName}-rating`).className = `vital-rating ${data.rating}`;
            
            // Update vital chart
            const chart = this.charts.get(vitalName);
            chart.data.labels.push(new Date().toLocaleTimeString());
            chart.data.datasets[0].data.push(data.value);
            
            // Keep only last 20 data points
            if (chart.data.labels.length > 20) {
                chart.data.labels.shift();
                chart.data.datasets[0].data.shift();
            }
            
            chart.update('none');
            
            // Generate insights
            this.generateInsights(vitalName, data);
        }
        
        formatVitalValue(vitalName, value) {
            switch (vitalName) {
                case 'lcp':
                case 'fid':
                    return `${Math.round(value)}ms`;
                case 'cls':
                    return value.toFixed(3);
                default:
                    return value.toString();
            }
        }
        
        generateInsights(metricName, data) {
            const insights = [];
            
            // Generate specific insights based on metric performance
            if (metricName === 'lcp' && data.rating === 'poor') {
                insights.push({
                    type: 'warning',
                    title: 'Slow Largest Contentful Paint',
                    description: 'Your LCP is above 4 seconds. Consider optimizing images and reducing server response times.',
                    recommendations: [
                        'Optimize critical images with modern formats (WebP, AVIF)',
                        'Implement proper image lazy loading',
                        'Reduce server response times',
                        'Remove unused CSS and JavaScript'
                    ]
                });
            }
            
            if (metricName === 'fid' && data.rating === 'poor') {
                insights.push({
                    type: 'error',
                    title: 'High First Input Delay',
                    description: 'Users are experiencing delays when interacting with your site.',
                    recommendations: [
                        'Break up long-running JavaScript tasks',
                        'Use web workers for heavy computations',
                        'Optimize third-party scripts',
                        'Implement code splitting'
                    ]
                });
            }
            
            if (metricName === 'cls' && data.rating === 'poor') {
                insights.push({
                    type: 'warning',
                    title: 'Layout Instability Detected',
                    description: 'Elements are shifting during page load, affecting user experience.',
                    recommendations: [
                        'Set explicit dimensions for images and videos',
                        'Reserve space for dynamic content',
                        'Avoid inserting content above existing content',
                        'Use CSS transforms instead of changing layout properties'
                    ]
                });
            }
            
            // Update insights display
            this.updateInsightsDisplay(insights);
        }
        
        updateInsightsDisplay(insights) {
            const container = document.getElementById('insights-container');
            
            insights.forEach(insight => {
                const insightElement = document.createElement('div');
                insightElement.className = `insight-card ${insight.type}`;
                insightElement.innerHTML = `
                    <div class="insight-header">
                        <h4>${insight.title}</h4>
                        <span class="insight-type">${insight.type}</span>
                    </div>
                    <p class="insight-description">${insight.description}</p>
                    <div class="insight-recommendations">
                        <h5>Recommendations:</h5>
                        <ul>
                            ${insight.recommendations.map(rec => `<li>${rec}</li>`).join('')}
                        </ul>
                    </div>
                    <button class="btn btn-sm btn-outline-primary dismiss-insight" 
                            onclick="this.parentElement.remove()">
                        Dismiss
                    </button>
                `;
                
                container.appendChild(insightElement);
            });
        }
        
        startRealTimeUpdates() {
            // Update dashboard every 5 seconds
            setInterval(() => {
                this.updateDashboard();
            }, 5000);
        }
        
        updateDashboard() {
            // Update current performance metrics
            this.updateCurrentMetrics();
            
            // Update trend chart if visible
            this.updateTrendChart();
            
            // Check for performance alerts
            this.checkPerformanceAlerts();
        }
        
        updateCurrentMetrics() {
            // Update loading performance metrics
            const timing = performance.timing;
            const now = Date.now();
            
            if (timing.responseStart > 0) {
                const ttfb = timing.responseStart - timing.navigationStart;
                document.getElementById('ttfb-value').textContent = `${ttfb}ms`;
            }
            
            if (timing.domContentLoadedEventEnd > 0) {
                const dcl = timing.domContentLoadedEventEnd - timing.navigationStart;
                document.getElementById('dcl-value').textContent = `${dcl}ms`;
            }
            
            if (timing.loadEventEnd > 0) {
                const load = timing.loadEventEnd - timing.navigationStart;
                document.getElementById('load-value').textContent = `${load}ms`;
            }
            
            // Update memory metrics if available
            if ('memory' in performance) {
                const memory = performance.memory;
                document.getElementById('memory-usage').textContent = 
                    `${Math.round(memory.usedJSSize / 1024 / 1024)}MB`;
                document.getElementById('heap-size').textContent = 
                    `${Math.round(memory.totalJSSize / 1024 / 1024)}MB`;
            }
        }
    }
    
    // Initialize performance dashboard when DOM is ready
    document.addEventListener('DOMContentLoaded', function() {
        window.performanceDashboard = new PerformanceDashboard();
    });
</script>

Troubleshooting Common Issues

Performance Optimization Debugging:

  1. Slow Initial Load: Implement critical resource prioritization and eliminate render-blocking resources
  2. High Memory Usage: Optimize JavaScript execution and implement proper garbage collection
  3. Layout Shifts: Set explicit dimensions and reserve space for dynamic content
  4. Cache Misses: Optimize cache strategies and implement intelligent pre-loading
  5. Mobile Performance: Implement device-specific optimizations and adaptive loading

Performance Best Practices

Building High-Performance Portal Architecture:

  1. Critical Resource Prioritization: Load essential content first using resource hints and preloading
  2. Progressive Enhancement: Build baseline functionality that works everywhere, then enhance
  3. Adaptive Loading: Adjust content delivery based on device capabilities and network conditions
  4. Monitoring-Driven Optimization: Use real-time data to guide optimization decisions
  5. Performance Budgets: Set and enforce performance constraints throughout development

Key Takeaways

Performance optimization is an ongoing process that requires comprehensive monitoring, intelligent caching, and adaptive delivery strategies. Focus on Core Web Vitals, implement multi-level caching, optimize assets for different device profiles, and maintain real-time performance visibility to ensure your portal delivers exceptional experiences that drive user engagement and business success.

This concludes the Advanced Customization section of our learning series. In the next section, you’ll dive into Pro-Code Development with advanced JavaScript frameworks, custom components, and enterprise integration patterns.

4. Documentation and Maintenance

Set yourself up for long-term success:

Practical Example

Consider implementing this episode’s techniques for a customer portal where users need enhanced functionality beyond basic templates. The implementation should:

Apply the specific techniques from this episode while following the general principles of good portal development.

Common Implementation Patterns

Pattern When to Use Benefits Considerations
Progressive Enhancement Adding advanced features Ensures basic functionality always works Requires careful planning
Responsive Design Multi-device support Consistent experience across devices May increase complexity
Modular Development Complex customizations Easier maintenance and updates Requires good architecture
Performance Monitoring All implementations Early detection of issues Ongoing effort required

Troubleshooting Tips

Advanced Considerations

As you implement these intermediate techniques, consider:

Summary

Performance optimization is an ongoing process, not a one-time task. Implement monitoring from the start, optimize proactively rather than reactively, and always consider performance impact when adding new features. Congratulations on completing the intermediate section! In the advanced section, you’ll learn pro-code development techniques.