Feature Flag and Progressive Delivery Rules
1. Toggle Classification
Based on Martin Fowler's Feature Toggles taxonomy, classify every flag before creation.
| Category |
Lifetime |
Dynamism |
Decision Owner |
Example |
| Release Toggle |
Short (days–weeks) |
Per-deployment |
Engineering |
Hide incomplete payment flow |
| Experiment Toggle |
Medium (weeks–months) |
Per-request |
Product/Data |
A/B test checkout button color |
| Ops Toggle |
Long-lived |
Per-request |
Operations/SRE |
Circuit breaker for external API |
| Permission Toggle |
Long-lived |
Per-request |
Product/Sales |
Premium feature gating |
Classification Rules
- Every flag MUST have a category assigned at creation time
- Release toggles MUST have a planned removal date (default: 30 days after full rollout)
- Experiment toggles MUST define success metrics and evaluation criteria before activation
- Ops toggles MUST document the operational scenario they address
- Permission toggles MUST map to a defined entitlement or role
2. Flag Naming and Structure
Naming Convention
<category>.<domain>.<feature-name>
| Component |
Format |
Example |
| Category |
release, experiment, ops, permission |
release |
| Domain |
lowercase, kebab-case |
checkout |
| Feature name |
lowercase, kebab-case |
new-payment-flow |
Full example: release.checkout.new-payment-flow
Flag Definition Schema
{
"key": "release.checkout.new-payment-flow",
"category": "release",
"description": "Enables the redesigned payment flow with multi-step checkout",
"owner": "team-payments",
"created": "2026-03-01",
"expiry": "2026-04-15",
"default_value": false,
"tags": ["checkout", "payments", "q1-release"]
}
Rules
- Flag keys MUST be globally unique
- Descriptions MUST explain what the flag controls, not just name it
- Every flag MUST have an owner (team or individual)
- Release and experiment flags MUST have an expiry date
- Use boolean flags for on/off; use string/JSON variants only when multiple states are needed
3. Flag Lifecycle Management
Lifecycle Phases
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Created │───▶│ Testing │───▶│ Rollout │───▶│ Stable │───▶│ Removed │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
│ │ │
│ Flag off in prod │ Gradual % increase │ Code cleanup
│ On in dev/staging │ Monitor metrics │ Remove flag checks
│ │ Rollback if needed │ Delete flag config
Phase Rules
| Phase |
Required Actions |
| Created |
Define flag with metadata, assign owner, set expiry |
| Testing |
Enable in dev/staging environments, verify both paths |
| Rollout |
Gradual percentage increase with metric monitoring |
| Stable |
Flag at 100% for all users, begin cleanup planning |
| Removed |
Delete flag checks from code, remove flag configuration |
Cleanup Enforcement
- Track flag age in CI — warn at 80% of expiry, fail build at 120% of expiry
- Run static analysis to detect stale flag references
- Maintain a flag inventory dashboard showing age, status, and owner
- Include flag cleanup in sprint planning when flags approach expiry
4. Targeting Rules and Percentage Rollouts
Targeting Hierarchy
Evaluate targeting rules in this order:
- User-level override — specific user IDs (for testing or VIP access)
- Segment match — user belongs to a defined segment (beta-testers, internal-staff)
- Rule-based evaluation — attribute conditions (country == "US", plan == "enterprise")
- Percentage rollout — consistent hashing of user ID for gradual rollout
- Default value — fallback when no rules match
Percentage Rollout Rules
Recommended rollout schedule:
Day 1: 1% — smoke test, verify metrics
Day 2: 5% — expand, monitor error rates
Day 3: 10% — check performance impact
Day 5: 25% — broader exposure
Day 7: 50% — half of traffic
Day 10: 100% — full rollout, begin cleanup timer
- Use consistent hashing (e.g., murmur3 of user ID + flag key) so users get a stable experience
- Never use random assignment for percentage rollouts — it causes flickering
- Define rollback criteria before starting rollout (error rate threshold, latency p99, conversion drop)
- Automate rollback when metrics breach defined thresholds
Targeting Rule Example
{
"flag": "experiment.checkout.one-click-buy",
"rules": [
{
"priority": 1,
"condition": { "user_id": { "in": ["user-123", "user-456"] } },
"value": true
},
{
"priority": 2,
"condition": { "segment": "beta-testers" },
"value": true
},
{
"priority": 3,
"condition": { "country": "US", "plan": "premium" },
"value": true,
"percentage": 50
}
],
"default": false
}
5. Trunk-Based Development with Feature Flags
Core Pattern
Feature flags enable trunk-based development by decoupling deployment from release.
┌─────────────────────────────────────────────────────────┐
│ main branch │
│ ──●──●──●──●──●──●──●──●──●──●──●──●──●──●──●──●──▶ │
│ │ │ │ │ │ │
│ Add Impl Impl Wire Remove │
│ flag behind more 100% flag │
│ flag logic rollout + code │
└─────────────────────────────────────────────────────────┘
Trunk-Based Development Rules
- Merge to main frequently (at least daily) — feature flags protect incomplete work
- Never use long-lived feature branches when a release flag can achieve the same goal
- Wrap all incomplete or risky code paths behind release flags
- Deploy flag-guarded code to production even if the feature is not ready for users
- Use flag-driven deployment (deploy code) vs flag-driven release (enable feature) as separate steps
Anti-Patterns to Avoid
| Anti-Pattern |
Problem |
Solution |
| Flag in flag |
Nested flag checks create exponential test paths |
Refactor to single flag or combine conditions |
| Flag-driven architecture |
Business logic depends on flag topology |
Keep flag checks at boundaries, not deep in domain |
| Permanent release flag |
Release flags that never get removed |
Enforce expiry, track in CI |
| Flag-based branching |
Using flags instead of proper abstraction |
Use strategy pattern or polymorphism |
6. OpenFeature Standard
OpenFeature is the CNCF open standard for feature flag evaluation.
Architecture
┌─────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Application │────▶│ OpenFeature SDK │────▶│ Provider │
│ Code │ │ (Vendor-neutral)│ │ (LaunchDarkly, │
│ │ │ │ │ Unleash, Flipt, │
│ │ │ ┌────────────┐ │ │ Flagsmith, etc.) │
│ │ │ │ Hooks │ │ └──────────────────┘
│ │ │ └────────────┘ │
└─────────────┘ └──────────────────┘
Key Rules
- Use OpenFeature SDK as the abstraction layer — never call provider APIs directly
- Register a single provider per application instance
- Use evaluation context to pass user attributes for targeting
- Implement hooks for logging, metrics, and validation — not in application code
- For detailed API usage and provider implementation, see
references/openfeature-standard.md
Basic Usage Pattern
import { OpenFeature } from '@openfeature/server-sdk';
// Set provider once at startup
OpenFeature.setProvider(new YourProvider());
const client = OpenFeature.getClient();
// Evaluate with context
const showFeature = await client.getBooleanValue(
'release.checkout.new-payment-flow',
false, // default value
{ targetingKey: userId, country: userCountry }
);
7. Testing Strategy
Test Both Paths
Every flag evaluation point MUST have tests for both the flag-on and flag-off states.
Test Matrix for a Single Flag:
┌───────────────┬──────────┬──────────┐
│ Scenario │ Flag ON │ Flag OFF │
├───────────────┼──────────┼──────────┤
│ Unit test │ ✓ │ ✓ │
│ Integration │ ✓ │ ✓ │
│ E2E (staging) │ ✓ │ ✓ │
│ E2E (prod) │ Canary │ Default │
└───────────────┴──────────┴──────────┘
Testing Rules
- Use test-specific providers (in-memory) that let you set flag values per test
- Never depend on external flag service in unit tests
- Integration tests MAY connect to a test-environment flag service
- Test the default/fallback path — assume the flag service will be unavailable
- For experiment flags, test that metrics are emitted correctly in both variants
Test Helper Pattern
// Test helper: override flags for testing
function withFlags(overrides: Record<string, boolean>, fn: () => void) {
const testProvider = new InMemoryProvider(overrides);
OpenFeature.setProvider(testProvider);
try {
fn();
} finally {
OpenFeature.clearProvider();
}
}
// Usage in test
withFlags({ 'release.checkout.new-payment-flow': true }, () => {
const result = renderCheckoutPage();
expect(result).toContain('multi-step');
});
8. Security Considerations
Server-Side vs Client-Side Evaluation
| Aspect |
Server-Side |
Client-Side |
| Flag data exposure |
None — evaluation happens on server |
Flag rules may be visible to users |
| Targeting accuracy |
Full context available |
Limited to client-known attributes |
| Latency |
Network round-trip for each eval |
Instant after initial load |
| Recommended for |
Permission flags, sensitive logic |
UI toggles, non-sensitive features |
Security Rules
- NEVER expose permission flag rules or targeting logic to client-side code
- Use server-side evaluation for any flag that controls access to paid features or sensitive data
- Client-side SDKs should receive only the evaluated result, not the full rule set
- Rotate API keys for flag services on the same schedule as other service credentials
- Audit flag changes — every flag modification must be logged with who, what, when
- Restrict flag modification permissions by environment (dev: team-wide, prod: restricted)
Audit Requirements
| Event |
Required Fields |
| Flag created |
Key, owner, category, expiry, created_by, timestamp |
| Flag modified |
Key, old_value, new_value, modified_by, timestamp, reason |
| Flag evaluated |
Key, context_hash, result, provider, timestamp |
| Flag deleted |
Key, deleted_by, timestamp, final_state |
9. Progressive Delivery Integration
Feature flags integrate with progressive delivery to minimize blast radius.
Strategy Overview
| Strategy |
Flag Role |
Blast Radius |
| Percentage rollout |
Flag controls user % |
Per-user |
| Canary + flag |
Flag targets canary instances |
Per-instance then per-user |
| Blue-green + flag |
Flag switches traffic between environments |
Per-environment |
| Ring-based |
Flag targets deployment rings |
Per-ring |
Integration Rules
- Combine infrastructure-level delivery (canary, blue-green) with feature flags for maximum control
- Use feature flags for user-level targeting and infrastructure tools for instance-level routing
- Define automated rollback triggers for both layers
- Monitor both infrastructure metrics (CPU, memory, error rate) and business metrics (conversion, revenue)
- For detailed progressive delivery strategies, see
references/progressive-delivery.md
10. Flag Technical Debt Management
Debt Indicators
| Indicator |
Threshold |
Action |
| Flag count per service |
> 20 active flags |
Prioritize cleanup sprint |
| Average flag age |
> 45 days for release flags |
Enforce expiry policy |
| Orphaned flags |
Flag in config but not in code |
Remove from config |
| Dead code behind flags |
Flag always evaluates to same value |
Remove flag and dead path |
Cleanup Process
- Identify — Static analysis scan for flag references in code
- Verify — Confirm flag is at 100% or 0% and stable for > 7 days
- Remove code — Delete flag checks and the unused code path
- Remove config — Delete flag definition from the flag service
- Verify deployment — Deploy cleanup and confirm no regressions
CI Integration
# Example: Flag hygiene check in CI
flag-hygiene:
script:
- python scripts/check_flag_expiry.py --warn-days 7 --fail-days -14
- python scripts/find_orphaned_flags.py --source-dir src/ --flag-config flags.json
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
11. Platform Comparison
| Feature |
LaunchDarkly |
Unleash |
Flipt |
Flagsmith |
| Hosting |
SaaS / Relay Proxy |
Self-hosted / SaaS |
Self-hosted |
Self-hosted / SaaS |
| OpenFeature support |
Yes |
Yes |
Yes |
Yes |
| Targeting |
Advanced |
Strategy-based |
Segment + rule |
Segment + rule |
| A/B testing |
Built-in |
Via integration |
Via integration |
Built-in |
| Audit log |
Yes |
Yes (Enterprise) |
Yes |
Yes |
| Pricing |
Per-seat |
Open-source core |
Open-source (Apache 2.0) |
Open-source core |
| Best for |
Enterprise, complex targeting |
Self-hosted, privacy-first |
GitOps-native, lightweight |
Full-featured self-hosted |
Selection Guidance
- Need SaaS with advanced targeting → LaunchDarkly
- Need self-hosted with privacy → Unleash
- Need GitOps-native with declarative config → Flipt
- Need open-source with A/B testing → Flagsmith
- Need vendor-neutral code → Use OpenFeature SDK regardless of provider choice
1---2name: feature-flag3description: Feature flag and progressive delivery patterns including toggle classification (release, experiment, ops, permission), flag lifecycle management, targeting rules, percentage rollouts, A/B testing integration, trunk-based development with feature flags, and progressive delivery strategies (canary, blue-green). Covers OpenFeature standard, LaunchDarkly, Unleash, Flipt, and Flagsmith. Use when implementing feature flags, designing toggle strategies, planning progressive delivery, or integrating feature management with CI/CD pipelines and trunk-based development workflows.4license: MIT5---67# Feature Flag and Progressive Delivery Rules89## 1. Toggle Classification1011Based on Martin Fowler's Feature Toggles taxonomy, classify every flag before creation.1213| Category | Lifetime | Dynamism | Decision Owner | Example |14| --- | --- | --- | --- | --- |15| Release Toggle | Short (days–weeks) | Per-deployment | Engineering | Hide incomplete payment flow |16| Experiment Toggle | Medium (weeks–months) | Per-request | Product/Data | A/B test checkout button color |17| Ops Toggle | Long-lived | Per-request | Operations/SRE | Circuit breaker for external API |18| Permission Toggle | Long-lived | Per-request | Product/Sales | Premium feature gating |1920### Classification Rules2122- Every flag MUST have a category assigned at creation time23- Release toggles MUST have a planned removal date (default: 30 days after full rollout)24- Experiment toggles MUST define success metrics and evaluation criteria before activation25- Ops toggles MUST document the operational scenario they address26- Permission toggles MUST map to a defined entitlement or role2728---2930## 2. Flag Naming and Structure3132### Naming Convention3334```text35<category>.<domain>.<feature-name>36```3738| Component | Format | Example |39| --- | --- | --- |40| Category | `release`, `experiment`, `ops`, `permission` | `release` |41| Domain | lowercase, kebab-case | `checkout` |42| Feature name | lowercase, kebab-case | `new-payment-flow` |4344Full example: `release.checkout.new-payment-flow`4546### Flag Definition Schema4748```json49{50 "key": "release.checkout.new-payment-flow",51 "category": "release",52 "description": "Enables the redesigned payment flow with multi-step checkout",53 "owner": "team-payments",54 "created": "2026-03-01",55 "expiry": "2026-04-15",56 "default_value": false,57 "tags": ["checkout", "payments", "q1-release"]58}59```6061### Rules6263- Flag keys MUST be globally unique64- Descriptions MUST explain what the flag controls, not just name it65- Every flag MUST have an owner (team or individual)66- Release and experiment flags MUST have an expiry date67- Use boolean flags for on/off; use string/JSON variants only when multiple states are needed6869---7071## 3. Flag Lifecycle Management7273### Lifecycle Phases7475```text76┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐77│ Created │───▶│ Testing │───▶│ Rollout │───▶│ Stable │───▶│ Removed │78└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘79 │ │ │80 │ Flag off in prod │ Gradual % increase │ Code cleanup81 │ On in dev/staging │ Monitor metrics │ Remove flag checks82 │ │ Rollback if needed │ Delete flag config83```8485### Phase Rules8687| Phase | Required Actions |88| --- | --- |89| Created | Define flag with metadata, assign owner, set expiry |90| Testing | Enable in dev/staging environments, verify both paths |91| Rollout | Gradual percentage increase with metric monitoring |92| Stable | Flag at 100% for all users, begin cleanup planning |93| Removed | Delete flag checks from code, remove flag configuration |9495### Cleanup Enforcement9697- Track flag age in CI — warn at 80% of expiry, fail build at 120% of expiry98- Run static analysis to detect stale flag references99- Maintain a flag inventory dashboard showing age, status, and owner100- Include flag cleanup in sprint planning when flags approach expiry101102---103104## 4. Targeting Rules and Percentage Rollouts105106### Targeting Hierarchy107108Evaluate targeting rules in this order:1091101. **User-level override** — specific user IDs (for testing or VIP access)1112. **Segment match** — user belongs to a defined segment (beta-testers, internal-staff)1123. **Rule-based evaluation** — attribute conditions (country == "US", plan == "enterprise")1134. **Percentage rollout** — consistent hashing of user ID for gradual rollout1145. **Default value** — fallback when no rules match115116### Percentage Rollout Rules117118```text119Recommended rollout schedule:120121Day 1: 1% — smoke test, verify metrics122Day 2: 5% — expand, monitor error rates123Day 3: 10% — check performance impact124Day 5: 25% — broader exposure125Day 7: 50% — half of traffic126Day 10: 100% — full rollout, begin cleanup timer127```128129- Use consistent hashing (e.g., murmur3 of user ID + flag key) so users get a stable experience130- Never use random assignment for percentage rollouts — it causes flickering131- Define rollback criteria before starting rollout (error rate threshold, latency p99, conversion drop)132- Automate rollback when metrics breach defined thresholds133134### Targeting Rule Example135136```json137{138 "flag": "experiment.checkout.one-click-buy",139 "rules": [140 {141 "priority": 1,142 "condition": { "user_id": { "in": ["user-123", "user-456"] } },143 "value": true144 },145 {146 "priority": 2,147 "condition": { "segment": "beta-testers" },148 "value": true149 },150 {151 "priority": 3,152 "condition": { "country": "US", "plan": "premium" },153 "value": true,154 "percentage": 50155 }156 ],157 "default": false158}159```160161---162163## 5. Trunk-Based Development with Feature Flags164165### Core Pattern166167Feature flags enable trunk-based development by decoupling deployment from release.168169```text170┌─────────────────────────────────────────────────────────┐171│ main branch │172│ ──●──●──●──●──●──●──●──●──●──●──●──●──●──●──●──●──▶ │173│ │ │ │ │ │ │174│ Add Impl Impl Wire Remove │175│ flag behind more 100% flag │176│ flag logic rollout + code │177└─────────────────────────────────────────────────────────┘178```179180### Trunk-Based Development Rules181182- Merge to main frequently (at least daily) — feature flags protect incomplete work183- Never use long-lived feature branches when a release flag can achieve the same goal184- Wrap all incomplete or risky code paths behind release flags185- Deploy flag-guarded code to production even if the feature is not ready for users186- Use flag-driven deployment (deploy code) vs flag-driven release (enable feature) as separate steps187188### Anti-Patterns to Avoid189190| Anti-Pattern | Problem | Solution |191| --- | --- | --- |192| Flag in flag | Nested flag checks create exponential test paths | Refactor to single flag or combine conditions |193| Flag-driven architecture | Business logic depends on flag topology | Keep flag checks at boundaries, not deep in domain |194| Permanent release flag | Release flags that never get removed | Enforce expiry, track in CI |195| Flag-based branching | Using flags instead of proper abstraction | Use strategy pattern or polymorphism |196197---198199## 6. OpenFeature Standard200201OpenFeature is the CNCF open standard for feature flag evaluation.202203### Architecture204205```text206┌─────────────┐ ┌──────────────────┐ ┌──────────────────┐207│ Application │────▶│ OpenFeature SDK │────▶│ Provider │208│ Code │ │ (Vendor-neutral)│ │ (LaunchDarkly, │209│ │ │ │ │ Unleash, Flipt, │210│ │ │ ┌────────────┐ │ │ Flagsmith, etc.) │211│ │ │ │ Hooks │ │ └──────────────────┘212│ │ │ └────────────┘ │213└─────────────┘ └──────────────────┘214```215216### Key Rules217218- Use OpenFeature SDK as the abstraction layer — never call provider APIs directly219- Register a single provider per application instance220- Use evaluation context to pass user attributes for targeting221- Implement hooks for logging, metrics, and validation — not in application code222- For detailed API usage and provider implementation, see `references/openfeature-standard.md`223224### Basic Usage Pattern225226```typescript227import { OpenFeature } from '@openfeature/server-sdk';228229// Set provider once at startup230OpenFeature.setProvider(new YourProvider());231232const client = OpenFeature.getClient();233234// Evaluate with context235const showFeature = await client.getBooleanValue(236 'release.checkout.new-payment-flow',237 false, // default value238 { targetingKey: userId, country: userCountry }239);240```241242---243244## 7. Testing Strategy245246### Test Both Paths247248Every flag evaluation point MUST have tests for both the flag-on and flag-off states.249250```text251Test Matrix for a Single Flag:252253┌───────────────┬──────────┬──────────┐254│ Scenario │ Flag ON │ Flag OFF │255├───────────────┼──────────┼──────────┤256│ Unit test │ ✓ │ ✓ │257│ Integration │ ✓ │ ✓ │258│ E2E (staging) │ ✓ │ ✓ │259│ E2E (prod) │ Canary │ Default │260└───────────────┴──────────┴──────────┘261```262263### Testing Rules264265- Use test-specific providers (in-memory) that let you set flag values per test266- Never depend on external flag service in unit tests267- Integration tests MAY connect to a test-environment flag service268- Test the default/fallback path — assume the flag service will be unavailable269- For experiment flags, test that metrics are emitted correctly in both variants270271### Test Helper Pattern272273```typescript274// Test helper: override flags for testing275function withFlags(overrides: Record<string, boolean>, fn: () => void) {276 const testProvider = new InMemoryProvider(overrides);277 OpenFeature.setProvider(testProvider);278 try {279 fn();280 } finally {281 OpenFeature.clearProvider();282 }283}284285// Usage in test286withFlags({ 'release.checkout.new-payment-flow': true }, () => {287 const result = renderCheckoutPage();288 expect(result).toContain('multi-step');289});290```291292---293294## 8. Security Considerations295296### Server-Side vs Client-Side Evaluation297298| Aspect | Server-Side | Client-Side |299| --- | --- | --- |300| Flag data exposure | None — evaluation happens on server | Flag rules may be visible to users |301| Targeting accuracy | Full context available | Limited to client-known attributes |302| Latency | Network round-trip for each eval | Instant after initial load |303| Recommended for | Permission flags, sensitive logic | UI toggles, non-sensitive features |304305### Security Rules306307- NEVER expose permission flag rules or targeting logic to client-side code308- Use server-side evaluation for any flag that controls access to paid features or sensitive data309- Client-side SDKs should receive only the evaluated result, not the full rule set310- Rotate API keys for flag services on the same schedule as other service credentials311- Audit flag changes — every flag modification must be logged with who, what, when312- Restrict flag modification permissions by environment (dev: team-wide, prod: restricted)313314### Audit Requirements315316| Event | Required Fields |317| --- | --- |318| Flag created | Key, owner, category, expiry, created_by, timestamp |319| Flag modified | Key, old_value, new_value, modified_by, timestamp, reason |320| Flag evaluated | Key, context_hash, result, provider, timestamp |321| Flag deleted | Key, deleted_by, timestamp, final_state |322323---324325## 9. Progressive Delivery Integration326327Feature flags integrate with progressive delivery to minimize blast radius.328329### Strategy Overview330331| Strategy | Flag Role | Blast Radius |332| --- | --- | --- |333| Percentage rollout | Flag controls user % | Per-user |334| Canary + flag | Flag targets canary instances | Per-instance then per-user |335| Blue-green + flag | Flag switches traffic between environments | Per-environment |336| Ring-based | Flag targets deployment rings | Per-ring |337338### Integration Rules339340- Combine infrastructure-level delivery (canary, blue-green) with feature flags for maximum control341- Use feature flags for user-level targeting and infrastructure tools for instance-level routing342- Define automated rollback triggers for both layers343- Monitor both infrastructure metrics (CPU, memory, error rate) and business metrics (conversion, revenue)344- For detailed progressive delivery strategies, see `references/progressive-delivery.md`345346---347348## 10. Flag Technical Debt Management349350### Debt Indicators351352| Indicator | Threshold | Action |353| --- | --- | --- |354| Flag count per service | > 20 active flags | Prioritize cleanup sprint |355| Average flag age | > 45 days for release flags | Enforce expiry policy |356| Orphaned flags | Flag in config but not in code | Remove from config |357| Dead code behind flags | Flag always evaluates to same value | Remove flag and dead path |358359### Cleanup Process3603611. **Identify** — Static analysis scan for flag references in code3622. **Verify** — Confirm flag is at 100% or 0% and stable for > 7 days3633. **Remove code** — Delete flag checks and the unused code path3644. **Remove config** — Delete flag definition from the flag service3655. **Verify deployment** — Deploy cleanup and confirm no regressions366367### CI Integration368369```yaml370# Example: Flag hygiene check in CI371flag-hygiene:372 script:373 - python scripts/check_flag_expiry.py --warn-days 7 --fail-days -14374 - python scripts/find_orphaned_flags.py --source-dir src/ --flag-config flags.json375 rules:376 - if: $CI_PIPELINE_SOURCE == "merge_request_event"377```378379---380381## 11. Platform Comparison382383| Feature | LaunchDarkly | Unleash | Flipt | Flagsmith |384| --- | --- | --- | --- | --- |385| Hosting | SaaS / Relay Proxy | Self-hosted / SaaS | Self-hosted | Self-hosted / SaaS |386| OpenFeature support | Yes | Yes | Yes | Yes |387| Targeting | Advanced | Strategy-based | Segment + rule | Segment + rule |388| A/B testing | Built-in | Via integration | Via integration | Built-in |389| Audit log | Yes | Yes (Enterprise) | Yes | Yes |390| Pricing | Per-seat | Open-source core | Open-source (Apache 2.0) | Open-source core |391| Best for | Enterprise, complex targeting | Self-hosted, privacy-first | GitOps-native, lightweight | Full-featured self-hosted |392393### Selection Guidance394395- **Need SaaS with advanced targeting** → LaunchDarkly396- **Need self-hosted with privacy** → Unleash397- **Need GitOps-native with declarative config** → Flipt398- **Need open-source with A/B testing** → Flagsmith399- **Need vendor-neutral code** → Use OpenFeature SDK regardless of provider choice