Skip to content

Commit c9338da

Browse files
authored
Merge pull request #8567 from Couchers-org/backend/refactor/growthbook-experimentation
Replace Statsig with GrowthBook for experimentation
2 parents b5b1a42 + 9db8053 commit c9338da

22 files changed

Lines changed: 365 additions & 556 deletions

app/backend.dev.env

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ ACTIVENESS_PROBES_ENABLED=1
8787
# Experimentation (feature flags)
8888
EXPERIMENTATION_ENABLED=0
8989
EXPERIMENTATION_PASS_ALL_GATES=1
90-
STATSIG_SERVER_SECRET_KEY=secret-...
91-
STATSIG_ENVIRONMENT=development
90+
GROWTHBOOK_API_HOST=https://gbapi.couchershq.org
91+
GROWTHBOOK_CLIENT_KEY=sdk-cGmljeXu1BCzHffE
9292

9393
# Moderation auto-approval deadline in seconds (0 to disable)
9494
MODERATION_AUTO_APPROVE_DEADLINE_SECONDS=10

app/backend/pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ dependencies = [
4141
"stripe>=13.1.0",
4242
"ua-parser>=1.0.1",
4343
"user-agents>=2.2.0",
44-
"statsig-python-core>=0.8.0",
44+
"growthbook>=1.2.0",
4545
]
4646

4747
[project.scripts]
@@ -136,7 +136,7 @@ module = ["couchers.migrations.versions.*"]
136136
ignore_errors = true
137137

138138
[[tool.mypy.overrides]]
139-
module = ["http_ece", "py_vapid", "luhn", "sqlalchemy_utils.*", "statsig_python_core", "user_agents"]
139+
module = ["http_ece", "py_vapid", "luhn", "sqlalchemy_utils.*", "growthbook", "user_agents"]
140140
# These libraries don't have type stubs or py.typed markers
141141
ignore_missing_imports = true
142142

app/backend/src/app.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,6 @@ def main() -> None:
9696
setup_tracing()
9797

9898
# Initialize the experimentation framework for feature flags in the main process.
99-
# IMPORTANT: This MUST be called AFTER worker processes are spawned (above).
100-
# The underlying SDK uses internal threading that doesn't survive fork().
10199
# Worker processes initialize their own instance in _run_forever().
102100
setup_experimentation()
103101

app/backend/src/couchers/config.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,13 @@
115115
("RECAPTHCA_SITE_KEY", str),
116116
# Whether we're in test
117117
("IN_TEST", bool, "0"),
118-
# Experimentation (feature flags via Statsig)
118+
# Experimentation (feature flags via GrowthBook)
119119
("EXPERIMENTATION_ENABLED", bool, "0"),
120120
# When enabled, all feature gates return True (useful for development/testing)
121121
("EXPERIMENTATION_PASS_ALL_GATES", bool, "0"),
122-
# Statsig SDK configuration
123-
("STATSIG_SERVER_SECRET_KEY", str, ""),
124-
("STATSIG_ENVIRONMENT", str, "development"),
122+
# GrowthBook SDK configuration
123+
("GROWTHBOOK_API_HOST", str, "https://cdn.growthbook.io"),
124+
("GROWTHBOOK_CLIENT_KEY", str, ""),
125125
# Moderation auto-approval deadline in seconds (0 to disable auto-approval)
126126
("MODERATION_AUTO_APPROVE_DEADLINE_SECONDS", int),
127127
# User ID of the bot user for automated moderation actions
@@ -171,8 +171,8 @@ def check_config(cfg: dict[str, Any]) -> None:
171171
raise Exception("MyPostcard API credentials not configured but postal verification enabled")
172172

173173
if cfg["EXPERIMENTATION_ENABLED"]:
174-
if not cfg["STATSIG_SERVER_SECRET_KEY"]:
175-
raise Exception("No Statsig server secret key but experimentation enabled")
174+
if not cfg["GROWTHBOOK_CLIENT_KEY"]:
175+
raise Exception("No GrowthBook client key but experimentation enabled")
176176

177177

178178
def make_config() -> dict[str, Any]:

app/backend/src/couchers/context.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
from typing import NoReturn, cast
1+
from typing import TYPE_CHECKING, NoReturn, cast
22

33
import grpc
44

55
from couchers.i18n import LocalizationContext
66

7+
if TYPE_CHECKING:
8+
from growthbook import GrowthBook
9+
710

811
class NonInteractiveContextException(Exception):
912
"""If this exception is raised, it is a programming error"""
@@ -79,6 +82,7 @@ def __init__(
7982
self.__is_interactive = is_interactive
8083
self.__logged_in = self._user_id is not None
8184
self.__cookies: list[str] = []
85+
self._growthbook: GrowthBook | None = None
8286

8387
if self.__is_interactive:
8488
if not self._grpc_context:

0 commit comments

Comments
 (0)