@variantlab/core 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,279 @@
1
+ # Bundle size analysis
2
+
3
+ How we plan to hit **< 3 KB gzipped for `@variantlab/core`** and the other budgets in [`ARCHITECTURE.md`](../../ARCHITECTURE.md).
4
+
5
+ ## Table of contents
6
+
7
+ - [Why bundle size matters](#why-bundle-size-matters)
8
+ - [The budgets](#the-budgets)
9
+ - [Techniques we use](#techniques-we-use)
10
+ - [Techniques we avoid](#techniques-we-avoid)
11
+ - [Measurement methodology](#measurement-methodology)
12
+ - [Per-feature cost estimates](#per-feature-cost-estimates)
13
+ - [What happens when we exceed the budget](#what-happens-when-we-exceed-the-budget)
14
+
15
+ ---
16
+
17
+ ## Why bundle size matters
18
+
19
+ 1. **First-contentful-paint on mobile web.** Every KB in your JS bundle delays FCP by ~10-20 ms on median 4G connections. A 30 KB A/B testing SDK costs half a second before your users see anything.
20
+ 2. **React Native cold-start time.** Hermes parses JS on every cold launch. Larger bundles = slower launches. Users notice 100 ms delays.
21
+ 3. **Edge runtime limits.** Cloudflare Workers have a 1 MB unzipped bundle limit. Every KB matters.
22
+ 4. **Dependency hell avoidance.** Small bundles can be copy-pasted as a file. Large bundles force you to dependency-manage.
23
+ 5. **Honesty.** Most A/B testing SDKs bloat over time. Committing to a hard budget at the start keeps us honest.
24
+
25
+ ---
26
+
27
+ ## The budgets
28
+
29
+ | Package | Budget (gzipped) | Rationale |
30
+ |---|---:|---|
31
+ | `@variantlab/core` | **3 KB** | The entire engine. This is the number people will benchmark us on. |
32
+ | `@variantlab/react` | **1.5 KB** | Just hooks + context + render-prop components. |
33
+ | `@variantlab/react-native` | **4 KB** | Includes debug overlay; tree-shaken in prod. |
34
+ | `@variantlab/next` | **2 KB** | Server + middleware + client re-exports. |
35
+ | `@variantlab/remix` | **1.5 KB** | Loader helpers + cookie stickiness. |
36
+ | `@variantlab/vue` | **1.5 KB** | Composables + components. |
37
+ | `@variantlab/svelte` | **1 KB** | Svelte compiles most of the runtime away. |
38
+ | `@variantlab/solid` | **1 KB** | Same reason. |
39
+ | `@variantlab/vanilla` | **0.5 KB** | Just a hook over the engine. |
40
+ | `@variantlab/astro` | **1 KB** | Astro integration is very thin. |
41
+ | `@variantlab/nuxt` | **1.5 KB** | Nuxt module wrapping Vue adapter. |
42
+
43
+ All budgets are **enforced in CI** by `size-limit`. A PR that exceeds any budget is blocked.
44
+
45
+ ---
46
+
47
+ ## Techniques we use
48
+
49
+ ### 1. Zero runtime dependencies
50
+
51
+ Every runtime dependency adds at minimum a few hundred bytes of import overhead and often much more due to transitive bloat. We ship zero.
52
+
53
+ **What we would pull in if we were careless**:
54
+
55
+ | Dep | Purpose | Cost (gzipped) |
56
+ |---|---|---:|
57
+ | `zod` | Schema validation | ~12 KB |
58
+ | `valibot` | Schema validation | ~2 KB |
59
+ | `lodash.merge` | Deep merge | ~1 KB |
60
+ | `nanoid` | ID generation | ~130 B |
61
+ | `ms` | Duration parsing | ~250 B |
62
+ | `semver` | Version range matching | ~6 KB |
63
+ | `glob` | Pattern matching | ~4 KB |
64
+
65
+ Total if we used all of these: ~25 KB.
66
+
67
+ **What we do instead**: Write hand-rolled replacements. Each is 100-400 bytes and purpose-built for our use case. Total cost: ~1.5 KB for all replacements combined.
68
+
69
+ ### 2. Pure ESM with tree-shaking
70
+
71
+ - Every module exports only what it needs
72
+ - No barrel files that re-export everything
73
+ - No side effects marked at package.json level: `"sideEffects": false`
74
+ - Bundlers (esbuild, Rollup, Vite, Webpack 5+) automatically drop unused code
75
+
76
+ ### 3. No class inheritance hierarchies
77
+
78
+ Classes with inheritance force bundlers to include the whole chain. We use a single `VariantEngine` class with composition via injected interfaces (`Storage`, `Fetcher`, `Telemetry`).
79
+
80
+ ### 4. No generics at runtime
81
+
82
+ TypeScript generics are compile-time only. We never use reflection, `Object.keys`, or runtime type checks beyond simple `typeof` guards.
83
+
84
+ ### 5. Hand-rolled schema validator
85
+
86
+ The schema validator is ~400 bytes. It's a switch statement over primitive types plus an allow-list check for object keys. That's it. No recursive descent parsers, no grammar, no regex. It rejects anything it doesn't understand.
87
+
88
+ ### 6. Hand-rolled semver matcher
89
+
90
+ We support only the operators that matter for targeting: `>=`, `<`, `<=`, `>`, `=`, `^`, `~`, range dashes. Total implementation: ~250 bytes. Full `semver` package is 6 KB. We lose nothing practical.
91
+
92
+ ### 7. Hand-rolled glob matcher
93
+
94
+ We support `/foo`, `/foo/*`, `/foo/**`, and `/foo/:param`. No character classes, no negation, no brace expansion. Total implementation: ~150 bytes. The `glob` or `minimatch` packages are 4+ KB.
95
+
96
+ ### 8. Hand-rolled hash function
97
+
98
+ For sticky assignment we need a stable hash of `(userId, experimentId)`. We use a simplified 32-bit FNV-1a implementation: ~80 bytes. `murmurhash` is 500 bytes.
99
+
100
+ ### 9. Web Crypto API for HMAC
101
+
102
+ `crypto.subtle.verify` is available in every modern runtime — browsers, Node 18+, Deno, Bun, Cloudflare Workers, React Native Hermes (via `react-native-quick-crypto` peer dep). We use the platform, not a polyfill. Zero bytes for HMAC.
103
+
104
+ ### 10. Dead code elimination via `__DEV__`
105
+
106
+ Debug overlay code and extended error messages are gated behind `typeof __DEV__ !== "undefined"` or `process.env.NODE_ENV !== "production"`. Bundlers replace these at build time with constants and drop unreachable branches.
107
+
108
+ Example:
109
+
110
+ ```ts
111
+ function validateExperiment(exp) {
112
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
113
+ if (!exp.id) {
114
+ throw new Error(
115
+ `Experiment missing ID. Experiments need a unique string ID to track assignments. Example: { id: "my-experiment", ... }`
116
+ );
117
+ }
118
+ } else {
119
+ if (!exp.id) throw new Error("no id");
120
+ }
121
+ }
122
+ ```
123
+
124
+ In production, the verbose error message is eliminated entirely.
125
+
126
+ ### 11. Minification-friendly APIs
127
+
128
+ - Short internal names
129
+ - Exported names are the only ones that survive minification
130
+ - No computed property names in hot paths
131
+ - No `arguments` object usage
132
+
133
+ ### 12. Separate entry points
134
+
135
+ Instead of one big `index.ts`, each feature is a separate entry point:
136
+
137
+ ```json
138
+ {
139
+ "exports": {
140
+ ".": "./dist/index.js",
141
+ "./debug": "./dist/debug.js",
142
+ "./crypto": "./dist/crypto.js",
143
+ "./storage/memory": "./dist/storage-memory.js"
144
+ }
145
+ }
146
+ ```
147
+
148
+ Users import only what they need. `@variantlab/core/debug` is separate from `@variantlab/core` so debug code never ships to production.
149
+
150
+ ---
151
+
152
+ ## Techniques we avoid
153
+
154
+ ### Class transforms and polyfills
155
+
156
+ No `Object.entries` polyfills (required since ES2017). No `class` polyfills (required since ES2015). Target ES2020 baseline — Hermes, Node 18, every modern browser.
157
+
158
+ ### JSON Schema libraries
159
+
160
+ JSON Schema validators are 20-100+ KB. We use our hand-rolled validator for runtime + publish the `experiments.schema.json` only for IDE tooling.
161
+
162
+ ### Runtime TypeScript features
163
+
164
+ - No `enum` (produces runtime object)
165
+ - No `namespace` (produces runtime object)
166
+ - No decorators
167
+ - No reflect-metadata
168
+
169
+ ### Prototype chains
170
+
171
+ A single class. No inheritance. No mixins.
172
+
173
+ ### Eager initialization
174
+
175
+ Storage is lazy. Fetcher polling is lazy. Crash-rollback counters allocate on first crash. Nothing runs until you call into the engine.
176
+
177
+ ---
178
+
179
+ ## Measurement methodology
180
+
181
+ ### Tooling
182
+
183
+ - **`size-limit`** with the `@size-limit/esbuild` plugin — matches what users actually ship
184
+ - **Bundlephobia** referenced for competitive comparison
185
+ - **Webpack stats-analyzer** for identifying unexpected bloat
186
+
187
+ ### Automation
188
+
189
+ - Every PR runs `size-limit` as a required check
190
+ - Size changes are reported as a PR comment
191
+ - A release cannot ship if any package exceeds budget
192
+
193
+ ### Real-world measurement
194
+
195
+ We test bundle impact in real apps:
196
+
197
+ 1. **Drishtikon Mobile** (React Native, Expo)
198
+ 2. **Next.js App Router example**
199
+ 3. **SvelteKit example**
200
+ 4. **Vite + React example**
201
+
202
+ Each example publishes its production bundle size to a tracking doc so we see *actual user impact*, not just synthetic benchmarks.
203
+
204
+ ---
205
+
206
+ ## Per-feature cost estimates
207
+
208
+ Rough estimates based on our hand-rolled implementations:
209
+
210
+ | Feature | Estimated cost (gzipped) |
211
+ |---|---:|
212
+ | `VariantEngine` class + constructor | ~500 B |
213
+ | Schema validator | ~400 B |
214
+ | Targeting matcher | ~300 B |
215
+ | Semver matcher | ~250 B |
216
+ | Glob route matcher | ~150 B |
217
+ | Sticky hash function | ~80 B |
218
+ | Assignment strategies | ~200 B |
219
+ | Storage interface + memory impl | ~150 B |
220
+ | Fetcher interface | ~80 B |
221
+ | Telemetry interface | ~50 B |
222
+ | Crash rollback logic | ~200 B |
223
+ | Time-travel recording | ~150 B (off by default) |
224
+ | HMAC verification (WebCrypto wrapper) | ~150 B |
225
+ | Error classes | ~150 B |
226
+ | Type exports (zero runtime) | 0 B |
227
+ | Boilerplate / glue | ~200 B |
228
+ | **Total core** | **~3000 B** |
229
+
230
+ Fits within the 3 KB budget with ~100 B to spare. Any new feature must either stay under that headroom or displace an existing feature.
231
+
232
+ ---
233
+
234
+ ## What happens when we exceed the budget
235
+
236
+ A PR that exceeds the budget is blocked by CI. The author must either:
237
+
238
+ 1. **Optimize** the change to fit
239
+ 2. **Remove** an existing feature to make room
240
+ 3. **Move** the change to a separate entry point (e.g., `@variantlab/core/extra`)
241
+ 4. **Request a budget increase** via a GitHub discussion, requiring 2 maintainer approvals
242
+
243
+ We expect to reject the majority of budget-increase requests. The budget is the contract.
244
+
245
+ ---
246
+
247
+ ## Comparison: what we avoid
248
+
249
+ If we used common conveniences, our core would be:
250
+
251
+ | Luxury | Cost | Running total |
252
+ |---|---:|---:|
253
+ | Starting point (hand-rolled) | 3.0 KB | 3.0 KB |
254
+ | Add `zod` for schema | +12 KB | 15 KB |
255
+ | Add `semver` | +6 KB | 21 KB |
256
+ | Add `minimatch` | +4 KB | 25 KB |
257
+ | Add `lodash.merge` | +1 KB | 26 KB |
258
+ | Add `eventemitter3` | +1 KB | 27 KB |
259
+ | Add `nanoid` | +0.5 KB | 27.5 KB |
260
+
261
+ **We would be nearly 10x our budget** before we wrote a line of actual logic. This is why zero-dependency is the founding principle, not an optimization we add later.
262
+
263
+ ---
264
+
265
+ ## Future work
266
+
267
+ - **Brotli-compressed size as a secondary metric.** Browsers support Brotli; npm does not measure it. We should publish both numbers.
268
+ - **ESBuild minification tuning.** Terser may squeeze out a few more bytes in corner cases.
269
+ - **Constant folding for literal configs.** If a user passes a literal JSON config, we can inline it at build time via a plugin.
270
+ - **Subsetting for fixed-feature builds.** If someone uses only value experiments and never render experiments, we could ship a smaller build.
271
+
272
+ ---
273
+
274
+ ## See also
275
+
276
+ - [`ARCHITECTURE.md`](../../ARCHITECTURE.md#size-budgets) — the budget table
277
+ - [`docs/design/api-philosophy.md`](../design/api-philosophy.md) — how API choices affect bundle size
278
+ - Bundlephobia: https://bundlephobia.com (for competitor measurements)
279
+ - size-limit: https://github.com/ai/size-limit
@@ -0,0 +1,327 @@
1
+ # Competitor analysis
2
+
3
+ A detailed comparison of variantlab to every existing A/B testing and feature-flagging solution we evaluated. The goal of this document is to be honest: where competitors do something better, we say so, and we plan to match or exceed them.
4
+
5
+ ## Table of contents
6
+
7
+ - [Methodology](#methodology)
8
+ - [Summary matrix](#summary-matrix)
9
+ - [Firebase Remote Config](#firebase-remote-config)
10
+ - [GrowthBook](#growthbook)
11
+ - [Statsig](#statsig)
12
+ - [LaunchDarkly](#launchdarkly)
13
+ - [Amplitude Experiment](#amplitude-experiment)
14
+ - [PostHog Feature Flags](#posthog-feature-flags)
15
+ - [Flagsmith](#flagsmith)
16
+ - [Unleash](#unleash)
17
+ - [ConfigCat](#configcat)
18
+ - [react-native-ab](#react-native-ab)
19
+ - [Why a new package](#why-a-new-package)
20
+
21
+ ---
22
+
23
+ ## Methodology
24
+
25
+ For each competitor we evaluated:
26
+
27
+ 1. **Pricing** — free tier, paid tier, what you give up for free
28
+ 2. **Framework support** — which frameworks have official SDKs, which are community-maintained
29
+ 3. **SSR support** — does it work in Next.js App Router, Remix, SvelteKit without hydration mismatches
30
+ 4. **Bundle size** — minified + gzipped for the primary SDK
31
+ 5. **Runtime dependencies** — transitive supply chain exposure
32
+ 6. **Self-hosting** — can you run the whole thing on your own infra
33
+ 7. **Debug tooling** — what do you get for local development
34
+ 8. **Security model** — HMAC, encryption, CSP compatibility
35
+ 9. **Privacy** — what telemetry does the SDK send
36
+ 10. **Type safety** — are experiment IDs typed
37
+
38
+ Sources: official documentation, npm package analysis, GitHub repos, pricing pages, community forums (as of early 2026).
39
+
40
+ ---
41
+
42
+ ## Summary matrix
43
+
44
+ | Tool | Free forever | Self-host | Bundle (gz) | Deps | Multi-framework | SSR | Type safety | Privacy |
45
+ |---|:-:|:-:|---:|---:|:-:|:-:|:-:|:-:|
46
+ | **variantlab** | ✅ | ✅ | ~3 KB | 0 | 10+ | ✅ | ✅ codegen | ✅ zero telemetry |
47
+ | Firebase Remote Config | Limited | ❌ | ~18 KB | many | 3 | Partial | ❌ | ❌ Google telemetry |
48
+ | GrowthBook | ✅ OSS | ✅ | ~7 KB | few | 5 | Partial | Partial | Partial |
49
+ | Statsig | Limited free | ❌ | ~14 KB | some | 4 | Partial | Partial | ❌ |
50
+ | LaunchDarkly | ❌ $$$ | ❌ | ~25 KB | some | 8 | ✅ | Partial | ❌ |
51
+ | Amplitude Experiment | Enterprise | ❌ | ~20 KB | many | 3 | Partial | ❌ | ❌ |
52
+ | PostHog Feature Flags | ✅ OSS | ✅ | ~30 KB | many | 4 | Partial | ❌ | Partial |
53
+ | Flagsmith | ✅ OSS | ✅ | ~8 KB | few | 4 | Partial | ❌ | Partial |
54
+ | Unleash | ✅ OSS | ✅ | ~10 KB | some | 5 | Partial | ❌ | Partial |
55
+ | ConfigCat | Limited | ❌ | ~12 KB | few | 5 | Partial | ❌ | ❌ |
56
+ | react-native-ab | ✅ OSS | N/A | ~2 KB | 0 | 1 | N/A | ❌ | ✅ |
57
+
58
+ Numbers are approximate and reflect the primary SDK (`firebase/remote-config`, `@growthbook/growthbook-react`, etc.) at the time of research.
59
+
60
+ ---
61
+
62
+ ## Firebase Remote Config
63
+
64
+ **Pricing**: Free under the Spark plan with limits; paid under Blaze.
65
+
66
+ **Strengths**:
67
+ - Mature, battle-tested, used by billions of users
68
+ - Tight integration with other Firebase products (Analytics, Crashlytics)
69
+ - A/B Testing Console has a decent UI
70
+ - Works offline with local caching
71
+
72
+ **Weaknesses for us to beat**:
73
+ - **Lock-in** — tied to Google Cloud. You can't migrate away without rewriting.
74
+ - **Bundle size** — the Web SDK pulls in 18 KB+ of Firebase app initialization code even for a single `getValue()` call.
75
+ - **Telemetry-by-default** — sends analytics events on every config fetch.
76
+ - **No true SSR support** — Firebase Web SDK assumes a browser global.
77
+ - **No screen-size targeting** — you have to implement it yourself.
78
+ - **No type safety** — values are stringly-typed; you cast on read.
79
+ - **No debug picker UI** — you build your own.
80
+ - **No self-host option** — you cannot run Remote Config without Google Cloud.
81
+ - **Config signing is proprietary** — verified by Google's infrastructure, not cryptographically auditable by the client.
82
+
83
+ **Verdict**: Great if you're already all-in on Firebase. Wrong choice for anyone who wants portability, small bundles, or strong privacy.
84
+
85
+ ---
86
+
87
+ ## GrowthBook
88
+
89
+ **Pricing**: Free open-source + paid SaaS.
90
+
91
+ **Strengths**:
92
+ - Open source core, self-hostable
93
+ - Well-designed A/B test result analysis (Bayesian + frequentist)
94
+ - React, Vue, and Node SDKs
95
+ - Visual editor (paid)
96
+ - Feature flag concept is well-modeled
97
+
98
+ **Weaknesses for us to beat**:
99
+ - **Web-first, React Native is an afterthought** — the RN SDK is a wrapper around the JS SDK with known issues around offline mode.
100
+ - **No Svelte, Solid, or Astro support.**
101
+ - **Bundle size is ~7 KB gzipped** for the React SDK, which is better than Firebase but still 2x our target.
102
+ - **No crash-triggered rollback.**
103
+ - **No debug overlay out of the box** — you get a "debug panel" in the dashboard, not on-device.
104
+ - **No deep link override.**
105
+ - **SSR works but is manual** — you must hydrate context yourself.
106
+ - **Type safety is partial** — you can type feature keys in TS but it's not auto-generated from config.
107
+
108
+ **Verdict**: The best existing OSS option, and a worthy benchmark. variantlab's differentiation is (a) wider framework support, (b) smaller bundle, (c) better RN story, (d) on-device debug tooling, (e) HMAC signing.
109
+
110
+ ---
111
+
112
+ ## Statsig
113
+
114
+ **Pricing**: Free tier up to a modest event volume, then paid.
115
+
116
+ **Strengths**:
117
+ - Fastest-growing in the space
118
+ - Excellent real-time experiment dashboards
119
+ - Native SDKs for iOS and Android (not just React Native)
120
+ - Pulse analysis for automated decisioning
121
+
122
+ **Weaknesses for us to beat**:
123
+ - **Telemetry is mandatory** — Statsig's core value prop is sending events back to their dashboard
124
+ - **No self-host** in the free tier
125
+ - **Bundle size** ~14 KB
126
+ - **Framework support** is limited to React, React Native, Node, and browsers
127
+ - **Target audience is teams with data scientists** — overkill for a solo developer
128
+
129
+ **Verdict**: Excellent product for a funded startup. Overkill and underfit for our audience of developers who want a free, lightweight, self-contained toolkit.
130
+
131
+ ---
132
+
133
+ ## LaunchDarkly
134
+
135
+ **Pricing**: Starts at ~$8.33/seat/month, enterprise features much higher.
136
+
137
+ **Strengths**:
138
+ - Industry standard for feature flags in regulated enterprises
139
+ - Best-in-class approval workflows and audit logs
140
+ - Wide framework support (8+ SDKs)
141
+ - Excellent SSR support including streaming
142
+ - Crash-triggered rollback (in enterprise tier)
143
+
144
+ **Weaknesses for us to beat**:
145
+ - **Not free.** At all. For anyone.
146
+ - **Bundle size ~25 KB** gzipped for the browser SDK
147
+ - **Telemetry mandatory** — core value prop is shipping events to the dashboard
148
+ - **Vendor lock-in** — their proprietary targeting DSL does not map cleanly to anything else
149
+ - **Adoption cost is high** — requires org-wide buy-in
150
+
151
+ **Verdict**: The feature set we want to match for free. LaunchDarkly's crash-rollback, approval workflows, and SSR streaming are the north star.
152
+
153
+ ---
154
+
155
+ ## Amplitude Experiment
156
+
157
+ **Pricing**: Enterprise tier of Amplitude Analytics. Not sold standalone.
158
+
159
+ **Strengths**:
160
+ - Tight integration with Amplitude's behavioral analytics
161
+ - Sophisticated cohort-based targeting
162
+
163
+ **Weaknesses for us to beat**:
164
+ - **Enterprise-only**
165
+ - **Bundle size ~20 KB** plus Amplitude's core SDK
166
+ - **Telemetry-heavy**
167
+ - **React Native support is limited**
168
+
169
+ **Verdict**: Not relevant to our audience. Listed for completeness.
170
+
171
+ ---
172
+
173
+ ## PostHog Feature Flags
174
+
175
+ **Pricing**: Free tier generous; paid for high volume.
176
+
177
+ **Strengths**:
178
+ - Open source and self-hostable
179
+ - Part of the broader PostHog analytics platform
180
+ - Good React and RN SDKs
181
+
182
+ **Weaknesses for us to beat**:
183
+ - **Bundle size ~30 KB** for the primary SDK because it's coupled to PostHog's analytics
184
+ - **Telemetry coupling** — feature flags require PostHog's event pipeline
185
+ - **No type safety from config**
186
+ - **No on-device debug picker**
187
+ - **No SSR story for Next.js App Router**
188
+
189
+ **Verdict**: Good choice if you already use PostHog for analytics. Heavy if you don't.
190
+
191
+ ---
192
+
193
+ ## Flagsmith
194
+
195
+ **Pricing**: Free OSS + paid SaaS.
196
+
197
+ **Strengths**:
198
+ - Open source and self-hostable
199
+ - Multi-environment support built in
200
+ - Segment-based targeting
201
+
202
+ **Weaknesses for us to beat**:
203
+ - **Bundle size ~8 KB**
204
+ - **SSR requires manual hydration**
205
+ - **React Native SDK has network-request-per-flag antipattern**
206
+ - **No type safety**
207
+ - **No on-device debug tooling**
208
+
209
+ **Verdict**: Solid for server-side feature flags. Weak on mobile.
210
+
211
+ ---
212
+
213
+ ## Unleash
214
+
215
+ **Pricing**: Free OSS + paid SaaS.
216
+
217
+ **Strengths**:
218
+ - Open source and self-hostable
219
+ - Focus on enterprise feature flags (as opposed to A/B testing)
220
+ - Well-designed API
221
+
222
+ **Weaknesses for us to beat**:
223
+ - **Weak A/B testing story** — designed as a flagging tool, not an experiment tool
224
+ - **No React Native SDK** (as of early 2026)
225
+ - **No debug overlay**
226
+ - **No type safety**
227
+
228
+ **Verdict**: Enterprise-flag focused. Different problem space.
229
+
230
+ ---
231
+
232
+ ## ConfigCat
233
+
234
+ **Pricing**: Free tier up to 10 feature flags.
235
+
236
+ **Strengths**:
237
+ - Simple, focused on feature flags
238
+ - Wide SDK coverage
239
+
240
+ **Weaknesses for us to beat**:
241
+ - **10-flag limit on free tier**
242
+ - **Bundle size ~12 KB**
243
+ - **No A/B test analysis**
244
+ - **No debug picker**
245
+ - **Telemetry by default**
246
+
247
+ **Verdict**: Too limited for our audience.
248
+
249
+ ---
250
+
251
+ ## react-native-ab
252
+
253
+ **Pricing**: Free OSS.
254
+
255
+ **Strengths**:
256
+ - Tiny (~2 KB)
257
+ - Zero dependencies
258
+ - Simple API
259
+
260
+ **Weaknesses for us to beat**:
261
+ - **Unmaintained** — last release 2+ years ago
262
+ - **React Native only**
263
+ - **No targeting beyond random split**
264
+ - **No persistence**
265
+ - **No debug picker**
266
+ - **No SSR**
267
+ - **No type safety**
268
+
269
+ **Verdict**: The closest thing to what we want, and the direct inspiration for variantlab. But it hasn't grown up — variantlab is what `react-native-ab` could have been.
270
+
271
+ ---
272
+
273
+ ## Why a new package
274
+
275
+ Looking at the matrix, every existing option fails at least one of the following:
276
+
277
+ 1. **Free forever with no usage caps**
278
+ 2. **Zero runtime dependencies**
279
+ 3. **Smaller than 5 KB gzipped**
280
+ 4. **Full multi-framework support**
281
+ 5. **Strong SSR correctness**
282
+ 6. **On-device debug tooling**
283
+ 7. **Type safety from codegen**
284
+ 8. **Zero telemetry by default**
285
+
286
+ variantlab's thesis: it is possible to do all of these at once, and no one has. That's the opportunity.
287
+
288
+ ### What we should copy
289
+
290
+ - **LaunchDarkly's crash rollback** — best-in-class, bring it to free
291
+ - **GrowthBook's Bayesian analysis story** — reuse for post-v1.0 analytics integrations
292
+ - **Statsig's real-time targeting evaluation UI** — bring that to our debug overlay
293
+ - **Firebase's offline caching** — our Storage adapters already do this
294
+ - **PostHog's self-host-first ethos** — we do the same, but lighter
295
+
296
+ ### What we should avoid
297
+
298
+ - **Firebase's bundle bloat** from an over-engineered SDK initialization story
299
+ - **Statsig's telemetry coupling** — we separate engine from analytics
300
+ - **LaunchDarkly's proprietary DSL** — we use plain JSON
301
+ - **GrowthBook's manual SSR hydration** — we automate it
302
+ - **Everyone's lack of on-device debug pickers** — this is our wedge
303
+
304
+ ### Where we will lose (at first)
305
+
306
+ - **Dashboard UX** — we don't ship a hosted dashboard in v0.1. GrowthBook, Statsig, LaunchDarkly all do. We bet that the CLI + JSON config workflow is enough for early adopters.
307
+ - **Analytics integration depth** — PostHog ships with full event tracking. We only call `onExposure` hooks; integrators wire the rest.
308
+ - **Marketing** — we're a 1-person OSS project vs funded startups.
309
+
310
+ These are acceptable losses for v0.1. Post-v1.0 we can address the first two.
311
+
312
+ ---
313
+
314
+ ## Sources
315
+
316
+ - Firebase Remote Config docs: https://firebase.google.com/docs/remote-config
317
+ - GrowthBook docs: https://docs.growthbook.io
318
+ - Statsig docs: https://docs.statsig.com
319
+ - LaunchDarkly docs: https://docs.launchdarkly.com
320
+ - Amplitude Experiment docs: https://www.docs.developers.amplitude.com/experiment/
321
+ - PostHog docs: https://posthog.com/docs/feature-flags
322
+ - Flagsmith docs: https://docs.flagsmith.com
323
+ - Unleash docs: https://docs.getunleash.io
324
+ - ConfigCat docs: https://configcat.com/docs
325
+ - react-native-ab on npm: https://www.npmjs.com/package/react-native-ab
326
+
327
+ Bundle sizes measured via Bundlephobia and direct npm tarball inspection.