Skip to content

Architecture Decision Record: Device Detection Strategy

Status: Accepted
Date: 2025-01-21
Author: Feature Flags Team

Context and Problem Statement

The feature flags plugin needs device detection capability for targeting rules (e.g., showing features only to mobile users, A/B testing by platform). The initial implementation used regex-heavy detection which raised performance concerns.

Should we use a full-featured library like ua-parser-js or implement optimized built-in detection?

Decision Drivers

  • Performance: Must work efficiently in serverless/edge environments
  • Bundle Size: Critical for cold starts and deployment limits
  • Accuracy: Should correctly identify common devices/browsers
  • Maintenance: Minimize ongoing maintenance burden
  • Flexibility: Support various deployment scenarios

Considered Options

Option 1: Always Use ua-parser-js

  • ✅ 99%+ accuracy
  • ✅ Regular updates
  • ✅ Rich data (versions, models)
  • ❌ 60KB bundle size
  • ❌ Slower cold starts
  • ❌ External dependency

Option 2: Built-in Detection Only

  • ✅ 2KB bundle size
  • ✅ Fast performance
  • ✅ No dependencies
  • ❌ 95% accuracy
  • ❌ Manual maintenance
  • ❌ Limited data

Option 3: Dual Approach (Selected)

  • ✅ 2KB default size
  • ✅ Optional enhanced mode
  • ✅ User choice
  • ✅ Best of both worlds
  • ⚠️ More complex
  • ⚠️ Two code paths

Decision Outcome

Chosen option: Option 3 - Dual Approach

Built-in optimized detection as default, with optional ua-parser-js integration.

Implementation

typescript
// Default: Built-in detection (2KB)
const uaCache = new Map<string, UserAgentInfo>();

function parseUserAgent(userAgent: string): UserAgentInfo {
  // Check cache
  const cached = uaCache.get(userAgent);
  if (cached) return cached;

  // Optimized pattern matching
  const info = {
    device: detectDevice(ua),
    browser: detectBrowser(ua),
    os: detectOS(ua),
    platform: detectPlatform(ua),
  };

  // LRU cache
  uaCache.set(userAgent, info);
  return info;
}

Positive Consequences

  • Performance: 10x faster for cached requests
  • Flexibility: Users choose accuracy vs performance
  • Compatibility: Works in all environments
  • Bundle: Minimal default impact

Negative Consequences

  • Complexity: Two code paths to maintain
  • Documentation: Must explain both options
  • Testing: More test scenarios

Performance Metrics

MetricBuilt-inua-parser-js
Bundle Size+2KB+60KB
First Parse0.1ms2ms
Cached Parse0.01ms0.01ms
Accuracy95%99%+
Cold Start ImpactNegligible+50-100ms

Migration Path

  1. Default remains lightweight
  2. Users can opt-in to enhanced:
    bash
    bun add ua-parser-js
  3. Configure plugin:
    typescript
    featureFlags({
      parsing: { useEnhancedUA: true },
    });

References