Architecture

SDK modules, managers, and initialization flow

The Convert Fullstack SDK is built as a set of specialized modules (called "Managers") coordinated by a central Core. Each manager handles one concern — bucketing, rules, data, API communication, etc. — and they collaborate to deliver the full experimentation workflow.

Module Dependency Map

flowchart TD
    A0["ConvertSDK / Core"]
    A1["Context"]
    A2["DataManager"]
    A3["RuleManager"]
    A4["BucketingManager"]
    A5["ApiManager"]
    A6["ExperienceManager"]
    A7["FeatureManager"]
    A8["EventManager"]
    A9["SegmentsManager"]
    A10["Config / Types"]
    A0 -- "Creates" --> A1
    A0 -- "Fetches config via" --> A5
    A0 -- "Fires events via" --> A8
    A1 -- "Runs experiments via" --> A6
    A1 -- "Runs features via" --> A7
    A1 -- "Accesses data via" --> A2
    A1 -- "Evaluates segments via" --> A9
    A1 -- "Releases queues via" --> A5
    A2 -- "Buckets via" --> A4
    A2 -- "Matches rules via" --> A3
    A2 -- "Enqueues tracking via" --> A5
    A2 -- "Fires events via" --> A8
    A2 -- "Uses types from" --> A10
    A6 -- "Gets data/buckets via" --> A2
    A7 -- "Gets data/buckets via" --> A2
    A9 -- "Matches rules via" --> A3
    A9 -- "Stores data via" --> A2
    A0 -- "Uses" --> A2

The Orchestra Conductor Analogy

Think of the ConvertSDK / Core as the conductor of an orchestra. It's the main starting point you interact with when you first use the SDK.

Just like a conductor:

  1. Gets the Music Sheet (Configuration): It takes your initial setup instructions (like your unique project key, called sdkKey) when it starts.
  2. Gathers the Musicians (Managers): It creates and organizes all the specialized helper components needed to run experiments.
  3. Starts the Performance (Initialization): It fetches the necessary experiment data from Convert servers (if you provided an sdkKey) or uses data you provide directly.
  4. Directs the Sections (Provides Context): It allows you to create specific "sessions" for each visitor, ensuring they see the correct variations and their actions are tracked properly. We call these sessions Context objects.

Core Modules

ModuleRoleAnalogy
ConvertSDK / CoreEntry point. Initializes the SDK, creates managers, fetches config, provides createContext().Orchestra conductor
ContextRepresents a single visitor's session. Runs experiments, features, and tracks conversions for that visitor.A visitor's personal guide
DataManagerCentral data store. Holds project config, coordinates bucketing, caches visitor decisions.Librarian
ExperienceManagerRetrieves A/B test details and variation assignments for a visitor.A/B test director
FeatureManagerResolves feature flag status and variable values for a visitor.Feature flag controller
BucketingManagerDeterministically assigns visitors to variations using MurmurHash3 hashing.Sorting Hat
RuleManagerEvaluates targeting rules (audiences) and matching conditions against visitor attributes.Bouncer
ApiManagerHandles HTTP communication — fetches config from CDN, batches and sends tracking events.Messenger
EventManagerInternal pub/sub event system for SDK lifecycle events (Ready, Bucketing, Conversion, etc.).Announcer
SegmentsManagerEvaluates and categorizes visitors into segments for reporting.Sorting desk
Config / TypesType definitions, enums, and DTOs used across all modules.Blueprint

Initialization Flow

When the SDK is initialized, this sequence unfolds:

  1. Configuration — The SDK reads the config you provided and sets defaults for unspecified options.
  2. Manager Creation — All specialized managers are instantiated and wired together.
  3. Data Fetch — If an sdkKey is present, the ApiManager fetches project configuration from Convert's CDN. If a data object is provided directly, it's used immediately (skipping the fetch).
  4. Ready State — The EventManager fires the Ready event. The SDK is now ready to create visitor contexts.
sequenceDiagram
    participant UserCode as Your Code
    participant SDK as ConvertSDK
    participant ApiMgr as ApiManager
    participant DataMgr as DataManager
    participant EventMgr as EventManager

    UserCode->>+SDK: Create SDK instance with config
    SDK->>SDK: Create all managers
    SDK->>+ApiMgr: Fetch config from CDN
    ApiMgr-->>-SDK: Return config data
    SDK->>+DataMgr: Store config data
    DataMgr-->>-SDK: Done
    SDK->>+EventMgr: Fire Ready event
    EventMgr-->>-SDK: Done
    SDK-->>-UserCode: SDK is ready

Platform-Specific Differences

While the architecture is identical across SDKs, there are platform-specific adaptations:

AspectJavaScript SDKPHP SDKAndroid SDKRuby SDKiOS SDKPython SDK
Initializationnew ConvertSDK(config) — async, use onReady() PromiseConvertSDK::create($config) — synchronous, returns ready instanceConvertSDK.builder(context)…build() — async, use onReady { } callbackConvertSdk.create(sdk_key:) — synchronous fetch + install, fires the ready eventConvertSwiftSDK(configuration:) — async, await sdk.ready() (or a completion-handler overload)Core(SDKConfig(…)).initialize() — synchronous; direct data is offline, an sdk_key fetches over HTTPS
Config RefreshsetTimeout for periodic background re-fetchPSR-16 cache TTL handles stalenessdataRefreshInterval timer re-fetches while the app is in the foreground; last-good config cached on diskBackground data_refresh_interval timer (default 300s) re-fetches and fires config.updated; settable to nil (timer off) for Lambda / CLIdataRefreshIntervalMs timer (default 300s) re-fetches in the background; last-good config cached on disk (Application Support)Opt-in RefreshConfig background thread (interval_seconds, default 300s, with jitter + exponential backoff) fires CONFIG_UPDATED; off entirely when unset (Lambda / CLI)
HTTP ClientBuilt-in fetch/XMLHttpRequestPSR-18 HTTP client (auto-discovered)OkHttpStandard-library Net::HTTP (zero runtime deps)URLSession (system framework, no third-party deps)httpx (the only runtime dependency); pluggable via the Transport Protocol
Event Queue FlushTimer-based batchingregister_shutdown_function (auto-flush on script end)Timer-based batching in the foreground; flush on app background, WorkManager retry for offline eventsTimer-based batching (flush_interval, default 1s) plus a PID-guarded at_exit auto-flushTimer-based batching (eventsReleaseIntervalMs, default 1s); on background the queue flushes and survivors persist to a coordinated on-disk file for a background URLSession to deliverIn-process batch queue (batch_size auto-release + opt-in auto_flush_interval_ms daemon timer) plus a best-effort atexit final flush
Data PersistenceOptional custom DataStore interfacePSR-16 cache (dual-purpose: config + visitor data)Built-in: SharedPreferences (visitor + sticky state) and app-private files (config cache, offline event queue)In-memory by default; optional pluggable store: (duck-typed get/set), with a built-in RedisStoreKeychain (visitor id, system of record) + app-private coordinated files (config cache + durable offline event queue)InMemoryDataStore by default; pluggable via the DataStore Protocol (data_store option)
LoggingBuilt-in LogManagerPSR-3 logger integrationBuilt-in logger with logLevel(LogLevel) builder optionBuilt-in LogManager with the log_level option and an optional sinkBuilt-in logger with the logLevel option (LogLevel, default .warn)Standard-library logging via the convert_sdk logger namespace

Next Steps