Type Safety for GrowthBook Clients - #126
Open
madhuchavva wants to merge 15 commits into
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Make wrong usage of the SDK visible at development time
Summary
The SDK already has
mypychecks and broader annotations, but the goal is to build compile-time safety for most-used parts of the public API. Nothing about runtime behavior changes.How things were vs. how they are now
Feature values. Before, the value was untyped — this passed every check and blew up (or silently misbehaved) at runtime:
Experiments. Before,
result.valuewas untyped. Now the result type comes from the variations you pass in:Callbacks. The sync client calls tracking callback with keyword arguments, so a callback with different parameter names type-checked fine but crashed at runtime. Now the expected names are part of the signature:
Misspelled arguments.
ExperimentandFeatureRuleused to swallow unknown keyword arguments silently (needed for server payloads). They still do at runtime, but a hand-typed typo is now caught:Feature keys (opt-in). New generator, equivalent to the JS SDK's
GrowthBook<AppFeatures>+ CLI type generation. Point it at the features JSON and usetypedclient that knows feature names and their value types:The generated classes add zero runtime behavior — they inherit everything and only carry type information. Regenerate when your feature list changes (works as a CI step).
Why this matters for editors and coding agents
Both consume the same signal. In VS Code/PyCharm this is red squiggles and correct autocomplete. For coding agents (Claude Code, Cursor, Copilot) it's the checker output they run after generating code — which means an agent that writes
gb.get_feature_value("banner", "blue") + 1now gets an error back and fixes itself, instead of shipping the bug. Before this PR the checkers had nothing to say about any of it. A regression suite (tests/test_typing.py) pins every example above: correct usage must check clean and each wrong usage must produce an error, under both mypy and pyright.What's NOT changed
from growthbook import PoolManager) no longer works — those were never documented API; (2) the async client now invokeson_experiment_viewedwith keyword arguments, matching what the sync client has done since v1.2.0 — an async callback whose parameters aren't namedexperiment/result/user_contextneeds a rename. The sync client is unaffected: its keyword call already required these names, which is also why the README's old two-argument example has been silently broken since v1.4.0 addeduser_context— this PR fixes those examples too.trackingCallback(the legacy constructor argument) now emits aDeprecationWarningpointing aton_experiment_viewed, consistent with the other deprecated aliases.Dictsignatures. We deliberately did not narrow those base classes to stricter dict types: doing so would make existing third-party implementations fail type checking. The JS SDK hit exactly this with its generated types ([Bug] the auto generated types don't work withuseFeatureValuegrowthbook#1729) and had to walk it back; we skipped the mistake.Out of scope
Dict[str, Any]on purpose, same as the JS SDK'sAttributes.