Skip to content

feat: build feature flags system with deterministic percentage rollouts - #449

Merged
portableDD merged 3 commits into
Nexacore-Org:v2from
mallison031:gamp/v2-feature-flags
Jun 29, 2026
Merged

feat: build feature flags system with deterministic percentage rollouts#449
portableDD merged 3 commits into
Nexacore-Org:v2from
mallison031:gamp/v2-feature-flags

Conversation

@mallison031

Copy link
Copy Markdown
Contributor

Closes #415

Summary

Builds an internal feature flag system allowing per-environment toggles,

percentage-based rollouts, and specific user targeting, backed by a 60-
second Redis cache. This introduces the FeatureFlag entity, an admin/user
flag controller, and a FeatureFlagGuard to restrict access without
revealing features via 404s.

### What Changed
- **Dependencies**: Added `@nestjs/cache-manager` and `cache-manager-

redis-yetfor strict caching requirements. - **Database**: Addedfeature_flagstable and migration. - **Service & Caching**: AddedFlagsService using deterministic hashing (userId+flagKey) for stable percentage rollouts. Flag definitions are cached to prevent any per-request DB hits. - **Guard**: Implemented a NestJS mixin FeatureFlagGuardthat integrates smoothly withJwtAuthGuard. - **Gates**: Applied dao_votingfeature flag gates toProposalController` routes.

### Key Design Decisions
- **Deterministic Rollout**: We use `crypto.createHash('sha256')`

converted to an integer mod 100. This guarantees a stable rollout
distribution that is immune to random variance across requests for the same
user.
- Redis Fallback: Since Redis was not previously configured, the
FlagsModule falls back to an in-memory cache if REDIS_URL is absent,
preventing pipeline breaks in existing setups.
- Soft Deletion: Instead of native TypeORM @DeleteDateColumn(),
deleting archives the key explicitly to avoid unique constraint violations
if an admin later re-creates a flag with the same name.

### Acceptance Criteria
- [x] Flag with `rolloutPercent=50` gives consistent result for same

userId across multiple calls
- [x] Flag with targetUserIds=[userId] enables the feature for that user
even at 0% rollout
- [x] Disabled flag causes route to return 404 — not 403
- [x] Flag state cached in Redis — DB not queried on every API request
- [x] GET /flags returns correct enabled/disabled state for
authenticated user
- [x] Updating a flag via admin clears the Redis cache immediately

### Tests & Coverage
- Added unit tests for `FlagsService`, `FlagsController`, and

FeatureFlagGuard.
- Passed all tests. New files achieved 100% Line Coverage.
- Note: The global CI might show some compilation errors/lint warnings,
but these are inherited from existing issues on the v2 upstream branch.
The newly added feature flag code typechecks and lints completely cleanly.

### Codebase Mismatch / Follow-ups
- The issue mentioned gating "Savings vault" and "Batch payment" routes.

These modules/controllers do not currently exist in the v2 codebase, so
the guard was only applied to the DAO ProposalController. They can be
gated trivially with @FeatureFlagGuard('key') once created.

### Security Note
- Ensure `REDIS_URL` is configured securely in the production environment.

@drips-wave

drips-wave Bot commented Jun 25, 2026

Copy link
Copy Markdown

@mallison031 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@portableDD

Copy link
Copy Markdown
Contributor

@mallison031 please resolve conflicts

@portableDD

Copy link
Copy Markdown
Contributor

This PR has merge conflicts that need to be resolved. Please rebase your branch on the latest v2 and fix the conflicts so it can be reviewed and merged.

@portableDD

Copy link
Copy Markdown
Contributor

This PR has merge conflicts. Please rebase on latest v2 and fix the conflicts.

@portableDD portableDD left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@portableDD
portableDD merged commit fc4e96f into Nexacore-Org:v2 Jun 29, 2026
3 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants