growthbook/growthbook
Open Source Feature Flags, Experimentation, and Product Analytics
13 hidden assumptions · 6-stage pipeline · 7 components
Like any codebase, this fullstack makes assumptions it never checks — most are routine. The ones worth your attention are below.
Routes experiments between users based on feature flags and analyzes statistical results
Feature definitions flow from backend MongoDB storage through API endpoints to client SDKs, which evaluate experiments locally and send tracking data back. Statistical analysis runs on aggregated experiment data in connected data warehouses, with results flowing back through the API to update bandit weights and display results in the web dashboard.
Under the hood, the system uses 3 feedback loops, 4 data pools, 4 control points to manage its runtime behavior.
A 7-component fullstack. 2153 files analyzed. Data flows through 6 distinct pipeline stages.
Hidden Assumptions
Most of what this code assumes is routine. These 3 are the ones most likely to cause trouble here. The rest are minor; they're under "Show everything".
License server API calls fail with authentication errors, breaking billing operations and payment updates, with no clear error handling for invalid credentials
Large seed values cause numpy.random.seed() to fail with ValueError, crashing bandit weight calculations and breaking experiment traffic allocation
When thousands of SDK instances refresh at once, backend API gets overwhelmed, causing 503 errors and stale feature flags serving to users
Show everything (10 more)
SDK feature requests contain project context in headers or query params, but router assumes this data is always present and valid
If this fails: Missing or invalid project context causes features to be served from wrong organization or default to empty feature set, leading to incorrect experiment assignments
packages/back-end/src/api/api.router.ts:feature endpoints
External data warehouse connections (BigQuery, Snowflake) have unlimited query capacity and always return results within Express request timeout
If this fails: Long-running metric aggregation queries time out or hit rate limits, causing statistical analysis to fail with incomplete data and blocking experiment result updates
packages/back-end/src/controllers/datasources.ts:data warehouse queries
min_variation_weight of 0.01 (1%) assumes experiments never need traffic allocation below this threshold, hardcoded in BanditConfig
If this fails: Experiments with many variations (>100) cannot allocate minimum viable traffic to each arm, causing statistical power issues and invalid bandit optimization
packages/stats/gbstats/bayesian/bandits.py:min_variation_weight
useExperiments hook returns experiments in consistent order for pagination, but assumes experiments array doesn't change between page renders
If this fails: User sees duplicate experiments or missing experiments when navigating pages if new experiments are added during browsing session
packages/front-end/pages/bandits/index.tsx:experiment filtering
Express trust proxy configuration assumes specific load balancer setup (AWS ALB, CloudFlare, etc.) without validating the proxy chain
If this fails: Incorrect client IP detection in production leads to wrong user attribution in experiments and security issues with rate limiting bypasses
packages/back-end/src/app.ts:EXPRESS_TRUST_PROXY_OPTS
Hardcoded cursor positions array assumes fixed UI dimensions and screen resolution, with exact pixel coordinates for interactive elements
If this fails: Animated cursors render in wrong positions on different screen sizes or when UI layout changes, breaking the managed warehouse onboarding flow
packages/front-end/pages/datasources/index.tsx:ManagedWarehouse cursors
TrackingCallback functions are synchronous or handle errors internally, but SDK assumes they never throw exceptions
If this fails: User-provided tracking callbacks that throw errors crash the SDK experiment evaluation, causing features to fallback to default values
packages/sdk-js/src/index.ts:TrackingCallback
UNLIMITED_USAGE.lastUpdated timestamp assumes current system time represents accurate billing period boundaries
If this fails: Server clock skew or timezone issues cause billing period calculations to be wrong, potentially allowing usage overages or incorrect quota enforcement
packages/back-end/src/enterprise/billing/index.ts:UNLIMITED_USAGE
Demo datasource exports assume specific data schema matches production warehouse table structures exactly
If this fails: Users testing with demo data get different statistical results than production, leading to incorrect experiment setup and misleading A/B test outcomes
packages/shared/src/demo-datasource/index.ts:demo data format
Design system page loads all component stories simultaneously, assuming browser can handle rendering dozens of complex React components at once
If this fails: Design system page becomes unresponsive on lower-end devices or with limited memory, making it impossible to preview UI components during development
packages/front-end/pages/design-system/index.tsx:component stories
Open the standalone hidden-assumptions report for growthbook →
How Data Flows Through the System
Feature definitions flow from backend MongoDB storage through API endpoints to client SDKs, which evaluate experiments locally and send tracking data back. Statistical analysis runs on aggregated experiment data in connected data warehouses, with results flowing back through the API to update bandit weights and display results in the web dashboard.
- Feature definition serving — Backend API serves feature definitions with embedded experiment rules from MongoDB to requesting SDKs via GET /api/features endpoint, including targeting conditions and variation configurations (config: datasources.warehouse.params, metrics.signups.type)
- Client-side feature evaluation — GrowthBook SDK evaluates feature flags and experiments by checking user attributes against targeting rules, assigning users to variations based on hash bucketing, and determining feature values [FeatureDefinition → FeatureResult]
- Experiment tracking — SDK calls trackingCallback with experiment exposure data including user ID, experiment details, and assigned variation, typically sent to analytics platforms or backend tracking endpoints [FeatureResult → TrackingData]
- Metric data aggregation — Backend queries connected data warehouses using DataSourceManager to aggregate user metrics, conversion rates, and experiment exposure data for statistical analysis input [TrackingData → Experiment statistics] (config: datasources.warehouse.settings.queries.exposure, datasources.warehouse.params.host)
- Statistical analysis — Python stats package runs Bayesian or frequentist tests on experiment data using methods like CUPED variance reduction and sequential testing to compute significance and confidence intervals [Experiment statistics → BanditResult] (config: bandit_weights_seed, top_two, min_variation_weight)
- Results presentation — Frontend retrieves statistical results via API and renders experiment dashboards showing conversion rates, significance levels, bandit weight updates, and traffic allocation recommendations [BanditResult]
Data Models
The data structures that flow between stages — the contracts that hold the system together.
packages/shared/types/feature.tsobject with id: string, key: string, rules: FeatureRule[], defaultValue: JSONValue, version: number, environments: Record<string, FeatureEnvironment>
Created in backend API, cached and served to SDKs, evaluated locally by clients based on user attributes
packages/shared/types/experiment.tsobject with id: string, name: string, hypothesis: string, variations: Variation[], targeting: ExperimentTargetingData, status: ExperimentStatus, metrics: string[], phases: ExperimentPhase[]
Defined in web UI, stored in MongoDB, embedded in feature rules, generates statistical results through Python analytics engine
packages/stats/gbstats/models/results.pydataclass with singleVariationResults: List[SingleVariationResult], currentWeights: List[float], updatedWeights: List[float], bestArmProbabilities: List[float], seed: int, updateMessage: str
Generated by Python bandit algorithms using experiment data, returned via API to frontend for display in bandit dashboards
packages/sdk-js/src/types/growthbook.tsobject with experiment: Experiment, result: Result, user: UserContext with userId, attributes, experimentBucket, variationId, variationName
Created when SDK evaluates experiments, sent to tracking callbacks, aggregated for statistical analysis of experiment results
System Behavior
How the system operates at runtime — where data accumulates, what loops, what waits, and what controls what.
Data Pools
Client-side cache storing feature definitions with TTL expiration and background refresh
Primary persistence layer storing organization data, experiments, feature flags, and user assignments
External analytics databases (BigQuery, Snowflake) containing user behavior and conversion metrics for statistical analysis
Runtime storage for current traffic allocation weights updated by bandit algorithms
Feedback Loops
- Bandit optimization (training-loop, reinforcing) — Trigger: New experiment data arrives. Action: Thompson sampling updates traffic allocation weights based on performance. Exit: Experiment ends or reaches statistical significance.
- Feature cache refresh (polling, balancing) — Trigger: Cache TTL expiration or manual refresh. Action: SDK fetches updated feature definitions from API. Exit: Cache updated with fresh data.
- Statistical recomputation (scheduled-job, balancing) — Trigger: New metric data or experiment changes. Action: Backend triggers Python stats engine to recompute experiment results. Exit: Results stored and served to frontend.
Delays
- Cache TTL (cache-ttl, ~configurable, default 60s) — SDKs serve potentially stale feature definitions until cache refreshes
- Statistical computation (async-processing, ~seconds to minutes) — Experiment results may lag behind real user data while Python engine processes statistics
- Data warehouse query (eventual-consistency, ~varies by warehouse) — Metric aggregation depends on external database query performance and data freshness
Control Points
- Bandit algorithm selection (hyperparameter) — Controls: Which optimization algorithm (Thompson sampling, top-two) is used for traffic allocation. Default: Thompson sampling with top_two=True
- Feature cache TTL (runtime-toggle) — Controls: How frequently SDKs refresh feature definitions from the backend. Default: 60 seconds default
- Statistical method (architecture-switch) — Controls: Whether to use Bayesian or frequentist analysis for experiment results. Default: Configurable per experiment
- Data warehouse connection (env-var) — Controls: Which external database is used for metric queries and user attribution. Default: MONGODB_URI and datasource configs
Technology Stack
HTTP server framework powering the backend API with middleware for authentication, routing, and request processing
React framework providing the web dashboard with server-side rendering and file-based routing
Primary database storing organizations, experiments, feature flags, and user data with Mongoose ODM
Statistical computation engine implementing Bayesian and frequentist analysis methods with NumPy and SciPy
Primary language providing type safety across backend API, frontend, and SDK packages
Containerization for self-hosted deployments with docker-compose orchestration
Process manager for production deployments with clustering and monitoring capabilities
Package manager with workspace support for managing dependencies across the monorepo packages
Key Components
- GrowthBook (orchestrator) — Core SDK class that fetches feature definitions, evaluates experiments based on user attributes, and tracks usage events
packages/sdk-js/src/GrowthBook.ts - ApiRouter (gateway) — Express router handling REST API endpoints for SDK feature requests and analytics data ingestion
packages/back-end/src/api/api.router.ts - BanditOptimizer (optimizer) — Implements Thompson sampling and other bandit algorithms to dynamically adjust experiment traffic allocation based on performance
packages/stats/gbstats/bayesian/bandits.py - ExperimentsController (orchestrator) — Handles CRUD operations for experiments, coordinates with statistical engine, and manages experiment lifecycle transitions
packages/back-end/src/controllers/experiments.ts - DataSourceManager (adapter) — Manages connections to various data warehouses (BigQuery, Snowflake, Databricks) for querying experiment metrics and user data
packages/back-end/src/controllers/datasources.ts - FeatureRepository (store) — Caches feature definitions locally in SDK, handles cache invalidation, and manages background refresh of feature data
packages/sdk-js/src/feature-repository.ts - StatisticalAnalyzer (processor) — Orchestrates statistical computations by calling appropriate Bayesian or frequentist test methods and formatting results
packages/stats/gbstats/gbstats.py
Package Structure
Express.js API server that manages organizations, experiments, feature flags, and statistical analyses, connecting to MongoDB for persistence and integrating with various data warehouses.
Next.js web application providing the GrowthBook dashboard UI for managing experiments, feature flags, and viewing analytics results.
JavaScript SDK that fetches feature definitions from the backend API and evaluates feature flags and experiments client-side based on user attributes.
React wrapper around the JavaScript SDK providing hooks and components for feature flag evaluation in React applications.
Common TypeScript types and utilities shared between backend and frontend packages.
Python package implementing Bayesian and frequentist statistical methods for analyzing A/B test results, including bandit algorithms and CUPED variance reduction.
Explore the interactive analysis
See the full architecture map, data flow, and code patterns visualization.
Analyze on CodeSeaRelated Fullstack Repositories
Frequently Asked Questions
What is growthbook used for?
Routes experiments between users based on feature flags and analyzes statistical results growthbook/growthbook is a 7-component fullstack written in TypeScript. Data flows through 6 distinct pipeline stages. The codebase contains 2153 files.
How is growthbook architected?
growthbook is organized into 4 architecture layers: Client SDKs, Web Dashboard, API Backend, Statistical Engine. Data flows through 6 distinct pipeline stages. This layered structure keeps concerns separated and modules independent.
How does data flow through growthbook?
Data moves through 6 stages: Feature definition serving → Client-side feature evaluation → Experiment tracking → Metric data aggregation → Statistical analysis → .... Feature definitions flow from backend MongoDB storage through API endpoints to client SDKs, which evaluate experiments locally and send tracking data back. Statistical analysis runs on aggregated experiment data in connected data warehouses, with results flowing back through the API to update bandit weights and display results in the web dashboard. This pipeline design reflects a complex multi-stage processing system.
What technologies does growthbook use?
The core stack includes Express.js (HTTP server framework powering the backend API with middleware for authentication, routing, and request processing), Next.js (React framework providing the web dashboard with server-side rendering and file-based routing), MongoDB (Primary database storing organizations, experiments, feature flags, and user data with Mongoose ODM), Python (Statistical computation engine implementing Bayesian and frequentist analysis methods with NumPy and SciPy), TypeScript (Primary language providing type safety across backend API, frontend, and SDK packages), Docker (Containerization for self-hosted deployments with docker-compose orchestration), and 2 more. A focused set of dependencies that keeps the build manageable.
What system dynamics does growthbook have?
growthbook exhibits 4 data pools (Feature definitions cache, MongoDB experiments), 3 feedback loops, 4 control points, 3 delays. The feedback loops handle training-loop and polling. These runtime behaviors shape how the system responds to load, failures, and configuration changes.
What design patterns does growthbook use?
4 design patterns detected: Feature Flag Evaluation, Multi-Armed Bandit, Warehouse-Native Analytics, SDK-First Architecture.
Analyzed on April 20, 2026 by CodeSea. Written by Karolina Sarna.