chore(analytics): remove dead PostHog feature-flag module

Delete app/utils/posthog_features.py. It was unused by routes, and
is_posthog_enabled() always returned False, so flags never activated and
the API was misleading.

Document the real model: deployment behavior uses environment variables
and app/config.py; per-user UI preferences stay on the user record.
Refresh PostHog monitoring guides and README accordingly, update the
incomplete-implementations note, and soften posthog_segmentation wording
so it does not imply an in-app PostHog flag layer.
This commit is contained in:
Dries Peeters
2026-04-16 16:23:54 +02:00
parent 51035f6779
commit b746fa912e
7 changed files with 52 additions and 719 deletions
@@ -4,14 +4,15 @@ This guide explains how to leverage PostHog's advanced features in TimeTracker f
## 📊 What's Included
TimeTracker now uses these PostHog features:
TimeTracker uses these PostHog-related analytics capabilities where configured:
1. **Person Properties** - Track user and installation characteristics
2. **Group Analytics** - Segment by version, platform, etc.
3. **Feature Flags** - Gradual rollouts and A/B testing
4. **Identify Calls** - Rich user profiles in PostHog
5. **Enhanced Event Properties** - Contextual data for better analysis
6. **Group Identification** - Cohort analysis by installation type
3. **Identify Calls** - Rich user profiles in PostHog
4. **Enhanced Event Properties** - Contextual data for better analysis
5. **Group Identification** - Cohort analysis by installation type
**Server-side feature gates** (rollouts, kill switches, route guards) are **not** implemented via PostHog in this codebase. Use environment variables and [`app/config.py`](../../../app/config.py) instead.
## 🎯 Person Properties
@@ -102,109 +103,19 @@ Installations are automatically grouped by:
- "How many Linux installations are active?"
- "Which Python versions are most common on Windows?"
## 🚀 Feature Flags
## 🧪 Experiments and measurement
### Basic Usage
There is no in-app PostHog feature-flag or variant API. To compare behaviors, implement variants in your own code (for example driven by `app.config`) and **distinguish them in analytics** with explicit properties on `track_event` calls (see below).
Check if a feature is enabled:
### Track meaningful actions
```python
from app.utils.posthog_features import get_feature_flag
from app import track_event
if get_feature_flag(user.id, "new-dashboard"):
return render_template("dashboard_v2.html")
else:
return render_template("dashboard.html")
```
### Route Protection
Require a feature flag for entire routes:
```python
from app.utils.posthog_features import feature_flag_required
@app.route('/beta/advanced-analytics')
@feature_flag_required('beta-features')
def advanced_analytics():
return render_template("analytics_beta.html")
```
### Remote Configuration
Use feature flag payloads for configuration:
```python
from app.utils.posthog_features import get_feature_flag_payload
config = get_feature_flag_payload(user.id, "dashboard-config")
if config:
theme = config.get("theme", "light")
widgets = config.get("enabled_widgets", [])
```
### Frontend Feature Flags
Inject flags into JavaScript:
```python
# In your view function
from app.utils.posthog_features import inject_feature_flags_to_frontend
@app.route('/dashboard')
def dashboard():
feature_flags = inject_feature_flags_to_frontend(current_user.id)
return render_template("dashboard.html", feature_flags=feature_flags)
```
```html
<!-- In your template -->
<script>
window.featureFlags = {{ feature_flags|tojson }};
if (window.featureFlags['new-timer-ui']) {
// Load new timer UI
}
</script>
```
### Predefined Feature Flags
Use the `FeatureFlags` class to avoid typos:
```python
from app.utils.posthog_features import FeatureFlags
if get_feature_flag(user.id, FeatureFlags.ADVANCED_REPORTS):
# Enable advanced reports
pass
```
## 🧪 A/B Testing & Experiments
### Track Experiment Variants
```python
from app.utils.posthog_features import get_active_experiments
experiments = get_active_experiments(user.id)
# {"timer-ui-experiment": "variant-b"}
if experiments.get("timer-ui-experiment") == "variant-b":
# Show variant B
pass
```
### Track Feature Interactions
```python
from app.utils.posthog_features import track_feature_flag_interaction
track_feature_flag_interaction(
track_event(
user.id,
"new-dashboard",
"clicked_export_button",
{"export_type": "csv", "rows": 100}
"export.completed",
{"export_type": "csv", "rows": 100, "experiment_variant": "b"},
)
```
@@ -269,41 +180,6 @@ Event: timer.started
Breakdown: Hour of day
```
## 🎨 Setting Up Feature Flags in PostHog
### 1. Create a Feature Flag
1. Go to PostHog → Feature Flags
2. Click "New feature flag"
3. Set key (e.g., `new-dashboard`)
4. Configure rollout:
- **Boolean**: On/off for everyone
- **Percentage**: Gradual rollout (e.g., 10% of users)
- **Person properties**: Target specific users
- **Groups**: Target specific platforms/versions
### 2. Target Specific Users
**Example: Enable for admins only**
```
Match person properties:
is_admin = true
```
**Example: Enable for Docker installations**
```
Match group properties:
deployment_method = "docker"
```
### 3. Gradual Rollout
1. Start at 0% (disabled)
2. Roll out to 10% (testing)
3. Increase to 50% (beta)
4. Increase to 100% (full release)
5. Remove flag from code
## 🔐 Person Properties for Segmentation
### Available Person Properties
@@ -360,81 +236,40 @@ Person properties:
4. **Timer Usage** - `timer.started` events over time
5. **Export Activity** - `export.csv` events by user cohort
## 🚨 Kill Switches
## 🚨 Kill switches and rollouts (application)
Use feature flags as emergency kill switches:
To disable or limit behavior for **all users** of an installation, use **configuration**: environment variables and [`app/config.py`](../../../app/config.py). That requires a deploy or config change, which is the supported model for this codebase.
```python
from app.utils.posthog_features import get_feature_flag, FeatureFlags
## 🧑‍💻 Development best practices
@app.route('/api/export')
def api_export():
if not get_feature_flag(current_user.id, FeatureFlags.ENABLE_EXPORTS, default=True):
abort(503, "Exports temporarily disabled")
# Proceed with export
```
### 1. Centralize deployment toggles
**Benefits:**
- Instantly disable problematic features
- No deployment needed
- Can target specific user segments
- Helps during incidents
Add booleans or strings to `Config` in `app/config.py` and read them from the environment with safe defaults.
## 🧑‍💻 Development Best Practices
### 2. Default to safe values
### 1. Define Flags Centrally
Prefer secure or conservative defaults for production (for example registration off unless explicitly enabled).
```python
# In app/utils/posthog_features.py
class FeatureFlags:
MY_NEW_FEATURE = "my-new-feature"
```
### 3. Document env vars
### 2. Default to Safe Values
When you add a new toggle, document the variable in deployment or admin docs so operators know how to set it.
```python
# Default to False for new features
if get_feature_flag(user.id, "risky-feature", default=False):
# Enable risky feature
```
### 4. Test behavior
### 3. Clean Up Old Flags
Test both branches of a toggle in unit tests by patching `current_app.config` or the setting your view reads.
Once a feature is fully rolled out:
1. Remove the flag check from code
2. Delete the flag in PostHog
3. Document in release notes
### 4. Test Flag Behavior
```python
def test_feature_flag():
with mock.patch('app.utils.posthog_features.get_feature_flag') as mock_flag:
mock_flag.return_value = True
# Test with flag enabled
mock_flag.return_value = False
# Test with flag disabled
```
## 📚 Additional Resources
## 📚 Additional resources
- **PostHog Docs**: https://posthog.com/docs
- **Feature Flags**: https://posthog.com/docs/feature-flags
- **Group Analytics**: https://posthog.com/docs/data/group-analytics
- **Person Properties**: https://posthog.com/docs/data/persons
- **Experiments**: https://posthog.com/docs/experiments
## 🎉 Benefits Summary
## 🎉 Benefits summary
Using these PostHog features, you can now:
With the analytics integration, you can:
**Segment users** by role, auth method, platform, version
**Gradually roll out** features to test with small groups
**A/B test** different UI variations
**Kill switches** for emergency feature disabling
**Remote config** without deploying code changes
**Cohort analysis** to understand user behavior
**Track updates** and version adoption patterns
**Monitor health** of different installation types
@@ -111,48 +111,7 @@ posthog.group_identify(
- ✅ Understand deployment patterns
- ✅ Correlate issues with specific configurations
### 4. **Feature Flags System** 🚩
**What:** Complete feature flag utilities for gradual rollouts and A/B testing.
**New File:** `app/utils/posthog_features.py`
**Features:**
- `get_feature_flag()` - Check if feature is enabled
- `get_feature_flag_payload()` - Remote configuration
- `get_all_feature_flags()` - Get all flags for a user
- `feature_flag_required()` - Decorator for route protection
- `inject_feature_flags_to_frontend()` - Frontend integration
- `track_feature_flag_interaction()` - Track feature usage
- `FeatureFlags` class - Centralized flag definitions
**Example Usage:**
```python
from app.utils.posthog_features import get_feature_flag, feature_flag_required
# Simple check
if get_feature_flag(user.id, "new-dashboard"):
return render_template("dashboard_v2.html")
# Route protection
@app.route('/beta/feature')
@feature_flag_required('beta-features')
def beta_feature():
return "Beta!"
# Frontend injection
feature_flags = inject_feature_flags_to_frontend(user.id)
return render_template("app.html", feature_flags=feature_flags)
```
**Benefits:**
- ✅ Gradual feature rollouts (0% → 10% → 50% → 100%)
- ✅ A/B testing different UI variations
- ✅ Emergency kill switches
- ✅ Target features to specific user segments
- ✅ Remote configuration without deployment
### 5. **Automatic User Identification on Login** 🔐
### 4. **Automatic User Identification on Login** 🔐
**What:** Users are automatically identified when they log in (both local and OIDC).
@@ -191,23 +150,17 @@ return render_template("app.html", feature_flags=feature_flags)
- Added `identify_user()` calls on OIDC login
- Set person properties on every login
### New Files
4. **`app/utils/posthog_features.py`** (NEW)
- Complete feature flag system
- Predefined flag constants
- Helper functions and decorators
### Documentation
5. **`POSTHOG_ADVANCED_FEATURES.md`** (NEW)
4. **`POSTHOG_ADVANCED_FEATURES.md`** (NEW)
- Complete guide to all features
- Usage examples and best practices
- PostHog query examples
6. **`POSTHOG_ENHANCEMENTS_SUMMARY.md`** (THIS FILE)
5. **`POSTHOG_ENHANCEMENTS_SUMMARY.md`** (THIS FILE)
- Summary of all changes
### Tests
7. **`tests/test_telemetry.py`**
6. **`tests/test_telemetry.py`**
- Updated to match enhanced property names
## 🚀 What You Can Do Now
@@ -217,61 +170,26 @@ return render_template("app.html", feature_flags=feature_flags)
- Group installations by version, platform, deployment method
- Build cohorts for targeted analysis
### 2. **Gradual Rollouts**
```python
# In PostHog: Create flag "new-timer-ui" at 10%
if get_feature_flag(user.id, "new-timer-ui"):
# Show new UI to 10% of users
pass
```
### 2. **Deployment toggles (not PostHog)**
### 3. **A/B Testing**
```python
experiments = get_active_experiments(user.id)
if experiments.get("onboarding-flow") == "variant-b":
# Show variant B
pass
```
Rollouts, kill switches, and route-level gates use **environment variables** and [`app/config.py`](../../../app/config.py), not a PostHog feature-flag module.
### 4. **Emergency Kill Switches**
```python
if not get_feature_flag(user.id, "enable-exports", default=True):
abort(503, "Exports temporarily disabled")
```
### 5. **Remote Configuration**
```python
config = get_feature_flag_payload(user.id, "dashboard-config")
theme = config.get("theme", "light")
widgets = config.get("enabled_widgets", [])
```
### 6. **Frontend Feature Flags**
```html
<script>
window.featureFlags = {{ feature_flags|tojson }};
if (window.featureFlags['new-ui']) {
// Enable new UI
}
</script>
```
### 7. **Version Analytics**
### 3. **Version Analytics**
- Track how many installations are on each version
- Identify installations that need updates
- Measure update adoption speed
### 8. **Platform Analytics**
### 4. **Platform Analytics**
- Compare behavior across Linux, Windows, macOS
- Identify platform-specific issues
- Optimize for most common platforms
### 9. **User Behavior Analysis**
### 5. **User Behavior Analysis**
- Filter events by user role
- Analyze admin vs regular user behavior
- Track feature adoption by user segment
### 10. **Installation Health**
### 6. **Installation Health**
- Monitor active installations (telemetry.health events)
- Track deployment methods (Docker vs native)
- Geographic distribution via timezone
@@ -317,24 +235,7 @@ Compare to: All users
## 🎨 Setting Up in PostHog
### 1. **Create Feature Flags**
Go to PostHog → Feature Flags → New feature flag
**Example: Gradual Rollout**
- Key: `new-dashboard`
- Rollout: 10% of users
- Increase over time: 10% → 50% → 100%
**Example: Admin Only**
- Key: `admin-tools`
- Condition: Person property `is_admin` = `true`
**Example: Docker Users**
- Key: `docker-optimizations`
- Condition: Person property `deployment_method` = `docker`
### 2. **Create Cohorts**
### 1. **Create Cohorts**
**Docker Admins:**
```
@@ -351,7 +252,7 @@ Events:
telemetry.install within last 30 days
```
### 3. **Build Dashboards**
### 2. **Build Dashboards**
**Installation Health:**
- Active installations (last 24h)
@@ -388,7 +289,7 @@ pytest tests/test_telemetry.py -v
No linter errors:
```bash
pylint app/utils/telemetry.py app/utils/posthog_features.py
pylint app/utils/telemetry.py
# ✅ No errors
```
@@ -405,14 +306,10 @@ With these enhancements, you now have:
**World-class product analytics** with person properties
**Group analytics** for cohort analysis
**Feature flags** for gradual rollouts & A/B testing
**Kill switches** for emergency feature control
**Remote configuration** without deployments
**Rich context** on every event
**Installation tracking** with version/platform groups
**User segmentation** by role, auth, platform
**Automatic identification** on login
**Frontend integration** for client-side flags
**Comprehensive docs** and examples
**Production-ready** with tests passing
@@ -424,20 +321,11 @@ With these enhancements, you now have:
POSTHOG_HOST=https://app.posthog.com
```
2. **Create Feature Flags** in PostHog dashboard
2. **Build Dashboards** for your metrics
3. **Build Dashboards** for your metrics
3. **Analyze Data** in PostHog to make data-driven decisions
4. **Start Using Flags** in your code:
```python
from app.utils.posthog_features import FeatureFlags, get_feature_flag
if get_feature_flag(user.id, FeatureFlags.NEW_DASHBOARD):
# New feature!
pass
```
5. **Analyze Data** in PostHog to make data-driven decisions
4. **Gate features in the app** using `app/config.py` and environment variables when you need deploy-time toggles
---
@@ -38,49 +38,9 @@ identify_user(user.id, {
})
```
### Check Feature Flag
```python
from app.utils.posthog_features import get_feature_flag
### Application toggles (server-side)
if get_feature_flag(user.id, "new-feature"):
# Enable feature
pass
```
### Protect Route with Flag
```python
from app.utils.posthog_features import feature_flag_required
@app.route('/beta/feature')
@feature_flag_required('beta-access')
def beta_feature():
return "Beta!"
```
### Get Flag Payload (Remote Config)
```python
from app.utils.posthog_features import get_feature_flag_payload
config = get_feature_flag_payload(user.id, "app-config")
if config:
theme = config.get("theme", "light")
```
### Inject Flags to Frontend
```python
from app.utils.posthog_features import inject_feature_flags_to_frontend
@app.route('/dashboard')
def dashboard():
flags = inject_feature_flags_to_frontend(current_user.id)
return render_template("dashboard.html", feature_flags=flags)
```
```html
<script>
window.featureFlags = {{ feature_flags|tojson }};
</script>
```
TimeTracker does **not** ship a PostHog-backed feature-flag API. Enable or restrict behavior with **environment variables** and [`app/config.py`](../../../app/config.py) (for example `DEMO_MODE`, `ALLOW_SELF_REGISTER`, `ENABLE_TELEMETRY`). Per-user UI options live on the user model in the database.
## 📊 Person Properties
@@ -100,39 +60,6 @@ def dashboard():
- `timezone` - Installation timezone
- `first_seen_version` - Original version (set once)
## 🎯 Feature Flag Examples
### Gradual Rollout
```
Key: new-ui
Rollout: 10% → 25% → 50% → 100%
```
### Target Admins Only
```
Key: admin-tools
Condition: is_admin = true
```
### Platform Specific
```
Key: linux-optimizations
Condition: current_platform = "Linux"
```
### Version Specific
```
Key: v3-features
Condition: current_version >= "3.0.0"
```
### Kill Switch
```
Key: enable-exports
Default: true
Use in code: default=True
```
## 📈 Useful PostHog Queries
### Active Users by Role
@@ -187,16 +114,6 @@ Compare: All platforms
## 🧪 Testing
### Mock Feature Flags
```python
from unittest.mock import patch
def test_with_feature_enabled():
with patch('app.utils.posthog_features.get_feature_flag', return_value=True):
# Test with feature enabled
pass
```
### Mock Track Events
```python
@patch('app.track_event')
@@ -212,36 +129,7 @@ def test_event_tracking(mock_track):
- **Analytics Docs**: [docs/analytics.md](docs/analytics.md)
- **PostHog Docs**: https://posthog.com/docs
## 🎯 Predefined Feature Flags
```python
from app.utils.posthog_features import FeatureFlags
# Beta features
FeatureFlags.BETA_FEATURES
FeatureFlags.NEW_DASHBOARD
FeatureFlags.ADVANCED_REPORTS
# Experiments
FeatureFlags.TIMER_UI_EXPERIMENT
FeatureFlags.ONBOARDING_FLOW
# Rollouts
FeatureFlags.NEW_ANALYTICS_PAGE
FeatureFlags.BULK_OPERATIONS
# Kill switches
FeatureFlags.ENABLE_EXPORTS
FeatureFlags.ENABLE_API
FeatureFlags.ENABLE_WEBSOCKETS
# Premium
FeatureFlags.CUSTOM_REPORTS
FeatureFlags.API_ACCESS
FeatureFlags.INTEGRATIONS
```
---
**Quick Tip:** Start with small rollouts (10%) and gradually increase as you gain confidence!
**Quick Tip:** Use person properties and cohorts in PostHog for analysis; gate behavior in the app with config and env vars.