Skip to content

feat(analytics): add request-aware feature flag helpers (#3487)#3491

Merged
PierreBrisorgueil merged 2 commits into
masterfrom
feat/analytics-featureflag-request-helpers-3487
Apr 23, 2026
Merged

feat(analytics): add request-aware feature flag helpers (#3487)#3491
PierreBrisorgueil merged 2 commits into
masterfrom
feat/analytics-featureflag-request-helpers-3487

Conversation

@PierreBrisorgueil

@PierreBrisorgueil PierreBrisorgueil commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add analytics.getFeatureFlagForRequest(flag, req, options) and analytics.isFeatureEnabledForRequest(flag, req, options) — sugar wrappers that resolve the PostHog distinctId from an Express request.
  • Resolution chain: req.user?.idreq.sessionID'anonymous'. req == null also resolves to 'anonymous' so the helper is safe to call defensively.
  • Existing getFeatureFlag / isFeatureEnabled stay public for cron / worker / job callers without a req. Fully non-breaking.

Why

Every route was repeating:

const id = req.user?.id ?? req.sessionID ?? 'anonymous';
await analytics.getFeatureFlag('checkout-v2', id);

Verbose, and the anonymous fallback is easy to forget — a missed fallback silently evaluates the flag with no user, returning the default.

Usage

import analytics from '../../../lib/services/analytics.js';

// Route handler
if (await analytics.isFeatureEnabledForRequest('billing-portal', req)) {
  // ...
}

const variant = await analytics.getFeatureFlagForRequest('checkout-v2', req, {
  personProperties: { plan: 'pro' },
});

Test plan

  • npm run lint — clean
  • npm run test:unit — 44 suites / 519 tests passing (508 existing + 11 new)
  • New file lib/services/tests/analytics.forRequest.unit.tests.js covers:
    • Authenticated user (req.user.id)
    • Anonymous browsing (req.sessionID only)
    • No req (defensive fallback to 'anonymous')
    • null / undefined req
    • req.user without .id (falls back to sessionID)
    • Numeric user.id coerced to string
    • PostHog not configured → undefined passthrough

Migration

MIGRATIONS.md entry added. Action for downstream: optional, pull via /update-stack; existing route code keeps working.

Coordination

#3486 (observability fan-out, open in another agent) also modifies lib/services/analytics.js (adds captureException). Overlap is at the default-export block — expected small merge conflict handled via rebase at merge time.

Closes #3487

Summary by CodeRabbit

  • Documentation

    • Updated migration guide with new request-aware feature flag helpers and fallback behavior documentation
  • Tests

    • Added comprehensive unit test coverage for request-scoped feature flag functionality, including edge cases and error handling

Copilot AI review requested due to automatic review settings April 23, 2026 07:37
@coderabbitai

coderabbitai Bot commented Apr 23, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8f42598e-895d-4793-83d0-df27d8da71b6

📥 Commits

Reviewing files that changed from the base of the PR and between ef1f6de and 753d142.

📒 Files selected for processing (3)
  • MIGRATIONS.md
  • lib/services/analytics.js
  • lib/services/tests/analytics.forRequest.unit.tests.js

Walkthrough

Adds request-scoped feature-flag helper functions to the analytics service that automatically derive the PostHog distinctId from an Express request object, with fallback logic (user.idsessionID'anonymous'). Includes comprehensive unit tests covering fallback precedence and edge cases, plus migration documentation.

Changes

Cohort / File(s) Summary
Analytics Service Implementation
lib/services/analytics.js
Adds resolveDistinctId(req) helper that extracts PostHog distinctId from request (prioritizes req.user?.id, then req.sessionID, defaults to 'anonymous'). Introduces two async wrappers—getFeatureFlagForRequest and isFeatureEnabledForRequest—that delegate to existing flag APIs while auto-applying request-derived distinctId. Both new methods exported on default export.
Request-Aware Helpers Test Suite
lib/services/tests/analytics.forRequest.unit.tests.js
New Jest unit test suite covering distinctId resolution precedence (user.idsessionID'anonymous'), null/undefined request handling, type coercion of user.id to string, edge values (0, ""), and behavior when PostHog is not configured (returns undefined).
Migration Documentation
MIGRATIONS.md
Dated entry documenting new request-aware helpers, their fallback behavior, that existing distinctId-based APIs remain unchanged, and guidance for route handlers to adopt *ForRequest variants.

Sequence Diagram

sequenceDiagram
    participant Handler as Route Handler
    participant Analytics as Analytics Service
    participant PostHog as PostHog API

    Handler->>Analytics: getFeatureFlagForRequest(flag, req, options)
    Analytics->>Analytics: resolveDistinctId(req)
    Note over Analytics: req.user?.id → req.sessionID → 'anonymous'
    Analytics->>PostHog: getFeatureFlag(flag, distinctId, options)
    PostHog-->>Analytics: flag value
    Analytics-->>Handler: flag value
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/analytics-featureflag-request-helpers-3487

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Apr 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.99%. Comparing base (f4ba797) to head (753d142).
⚠️ Report is 11 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3491      +/-   ##
==========================================
+ Coverage   85.85%   85.99%   +0.14%     
==========================================
  Files         115      116       +1     
  Lines        2919     2957      +38     
  Branches      809      829      +20     
==========================================
+ Hits         2506     2543      +37     
- Misses        327      328       +1     
  Partials       86       86              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds request-aware wrappers to the analytics service so Express routes can evaluate PostHog feature flags without manually deriving a distinctId from req (while keeping the existing request-agnostic APIs for jobs/cron).

Changes:

  • Added getFeatureFlagForRequest() / isFeatureEnabledForRequest() plus a resolveDistinctId(req) helper in lib/services/analytics.js.
  • Added unit tests covering authenticated, session-only, and defensive/no-request cases.
  • Documented the optional downstream migration in MIGRATIONS.md.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
lib/services/analytics.js Adds request-aware feature-flag helpers that derive distinctId from an Express request.
lib/services/tests/analytics.forRequest.unit.tests.js New unit test suite for the request-aware helpers and edge cases.
MIGRATIONS.md Adds an upgrade note describing the new optional helpers and recommended usage.

Comment thread lib/services/analytics.js Outdated
@PierreBrisorgueil

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Apr 23, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@codacy-production codacy-production Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull Request Overview

The implementation of the request-aware feature flag helpers follows the intended logic for fallback resolution, but the PR lacks the necessary unit tests to verify these new code paths. Codacy reports indicate that coverage requirements are missing, and no specific test scenarios were identified in the PR. While the structural quality is up to standards, the logic for distinctId coercion and fallback (authenticated user -> session -> anonymous) remains unverified through automation. These tests must be added to prevent regression in user tracking.

About this PR

  • The code changes were not fully accessible during the intent verification phase. Please ensure that the actual implementation of the fallback chain (req.user.id -> req.sessionID -> 'anonymous') exactly matches the specification, as it could not be cross-referenced with the source code.
  • Jira ticket metadata fields (Key, Title, Description) are currently empty. Please update the PR or the linked ticket to ensure proper traceability.

Test suggestions

  • Resolution of distinctId from authenticated user (req.user.id)
  • Resolution of distinctId from anonymous user session (req.sessionID)
  • Fallback to 'anonymous' when req is null or undefined
  • Coercion of numeric user.id to string for PostHog compatibility
  • Verification that options are correctly passed to the underlying analytics methods
Prompt proposal for missing tests
Consider implementing these tests if applicable:
1. Resolution of distinctId from authenticated user (req.user.id)
2. Resolution of distinctId from anonymous user session (req.sessionID)
3. Fallback to 'anonymous' when req is null or undefined
4. Coercion of numeric user.id to string for PostHog compatibility
5. Verification that options are correctly passed to the underlying analytics methods

🗒️ Improve review quality by adding custom instructions

@codacy-production

codacy-production Bot commented Apr 23, 2026

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 3 high

Alerts:
⚠ 3 issues (≤ 0 issues of at least minor severity)

Results:
3 new issues

Category Results
ErrorProne 3 high

View in Codacy

🟢 Metrics 29 complexity · 5 duplication

Metric Results
Complexity 29
Duplication 5

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes. Give us feedback

Routes today repeat the same distinctId plumbing for every flag call:

  const id = req.user?.id ?? req.sessionID ?? 'anonymous';
  await analytics.getFeatureFlag('checkout-v2', id);

This is verbose and the anonymous fallback is easy to forget (flag
evaluated without a user silently returns the default value).

Add two sugar helpers on AnalyticsService that extract the distinctId
from an Express request:

  analytics.getFeatureFlagForRequest(flag, req, options)
  analytics.isFeatureEnabledForRequest(flag, req, options)

Resolution chain: req.user?.id → req.sessionID → 'anonymous'.
Defensive edge: req == null also resolves to 'anonymous' so the
helper can be called without a pre-check.

Existing getFeatureFlag / isFeatureEnabled remain public — cron,
worker, and scheduled-job callers have no req and keep passing the
identifier explicitly. Non-breaking for downstream.

Tests: 11 unit tests cover the three canonical cases (authenticated
user, session-only, no req), plus defensive edges (null req, non-id
user object, numeric user.id coercion, no-PostHog passthrough).

MIGRATIONS.md: entry documents the new helpers and opt-in migration.

Closes #3487
CodeRabbit/Copilot feedback: truthy checks (`if (req.user?.id)`) would
coerce `0` or empty-string identifiers to `'anonymous'`. Switch to
`!= null` so any defined id/sessionID is preserved and stringified.

Added 2 tests (`id: 0`, empty sessionID) — suite now 13 tests.
@PierreBrisorgueil
PierreBrisorgueil force-pushed the feat/analytics-featureflag-request-helpers-3487 branch from ae288b4 to 753d142 Compare April 23, 2026 11:41
@PierreBrisorgueil
PierreBrisorgueil merged commit bcfe797 into master Apr 23, 2026
2 of 4 checks passed
@PierreBrisorgueil
PierreBrisorgueil deleted the feat/analytics-featureflag-request-helpers-3487 branch April 23, 2026 11:42
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.

feat(analytics): helpers getFeatureFlagForRequest/isFeatureEnabledForRequest avec distinctId auto

2 participants