Skip to content

Commit 6c26181

Browse files
docs: Improved docs for provider integrations (#185)
1 parent c2955eb commit 6c26181

File tree

6 files changed

+534
-128
lines changed

6 files changed

+534
-128
lines changed
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# Confidence OpenFeature Provider Integration Guide
2+
3+
This guide contains common integration steps that apply to all Confidence OpenFeature providers in this repository.
4+
5+
For language-specific installation and quick start instructions, see your provider's README:
6+
- [Go Provider](go/README.md)
7+
- [Java Provider](java/README.md)
8+
- [JavaScript Provider](js/README.md)
9+
- [Ruby Provider](ruby/README.md)
10+
11+
---
12+
13+
## Table of Contents
14+
15+
1. [Getting Your Credentials](#getting-your-credentials)
16+
2. [Error Handling](#error-handling)
17+
3. [Sticky Assignments](#sticky-assignments)
18+
19+
---
20+
21+
## Getting Your Credentials
22+
23+
Before integrating any Confidence provider, you'll need a **client secret** from your Confidence account:
24+
25+
1. Log into the Confidence dashboard
26+
2. In the **Clients** section, create a new client secret for the client you intend to use (or start by creating a new client)
27+
3. Make sure to select **Backend** as integration type. Never expose your Backend client secret outside your organization
28+
29+
---
30+
31+
## Error Handling
32+
33+
All Confidence providers use a **default value fallback** pattern to ensure your application continues to function even when flag evaluation fails.
34+
35+
### How Default Values Work
36+
37+
When you request a flag value, you always provide a default:
38+
39+
```
40+
// Pseudocode
41+
value = client.getFlagValue("my-flag", DEFAULT_VALUE, context)
42+
```
43+
44+
If anything goes wrong, the provider returns `DEFAULT_VALUE` instead of throwing an error.
45+
46+
### Common Failure Scenarios
47+
48+
| Scenario | What Happens | Common Causes |
49+
|----------|--------------|---------------|
50+
| **Flag doesn't exist** | Returns default | Flag not created, wrong name, not enabled for the client |
51+
| **Type mismatch** | Returns default | Requesting boolean for string or object property. Or requesting boolean for the _flag_. Flags are objects in Confidence |
52+
| **Network failure** | Returns default | Confidence API unreachable (Ruby only) |
53+
| **Initialization failure** | Returns default | CDN unreachable, invalid credentials not backend type |
54+
| **Invalid context** | Returns default | Malformed attributes, missing targeting key |
55+
| **Provider not ready** | Returns default | Called before initialization complete |
56+
57+
### Error Details
58+
59+
For debugging, use the `details` methods to get error information:
60+
61+
**Error codes:**
62+
- `FLAG_NOT_FOUND`: The flag doesn't exist in Confidence
63+
- `TYPE_MISMATCH`: Wrong value type requested (e.g., boolean for string)
64+
- `PROVIDER_NOT_READY`: Provider still initializing
65+
- `PARSE_ERROR`: Response couldn't be parsed
66+
- `GENERAL_ERROR`: Other errors (network, timeout, etc.)
67+
68+
**Reasons** (standard OpenFeature reasons):
69+
- `TARGETING_MATCH`: Flag evaluated successfully and matched targeting rules
70+
- `DEFAULT`: Default value returned (no segment/variant matched)
71+
- `DISABLED`: Flag is disabled or archived
72+
- `STALE`: Stale cached value
73+
- `ERROR`: Evaluation failed (see error code)
74+
- `UNKNOWN`: Reason could not be determined
75+
76+
### Production Best Practices
77+
78+
1. **Choose safe defaults**
79+
Example:
80+
```
81+
✅ GOOD: Default to "off" for risky features
82+
❌ BAD: Default to "on" for untested code
83+
```
84+
85+
2. **Log errors for debugging**
86+
- Track evaluation failures in your monitoring system. You can use OpenFeature [hooks](https://openfeature.dev/docs/reference/concepts/hooks/) for this.
87+
- Include flag key, error code, and context in logs
88+
- Set up alerts for elevated error rates
89+
90+
3. **Monitor error rates**
91+
- Track `errorCode != null` metrics
92+
- Alert if error rate exceeds threshold (e.g., >5%)
93+
- Investigate spikes (may indicate misconfigured flag setup or SDK integration)
94+
95+
4. **Test error scenarios**
96+
- Verify app works when Confidence is unreachable
97+
- Test with invalid credentials
98+
- Test with non-existent flags
99+
- Verify graceful handling of type mismatches
100+
101+
5. **Document your defaults**
102+
```
103+
// Default: false - feature is opt-in for safety
104+
const enabled = getFlag("new-payment-flow", false)
105+
106+
// Default: 1000ms - conservative timeout
107+
const timeout = getFlag("api-timeout", 1000)
108+
```
109+
110+
---
111+
112+
## Sticky Assignments
113+
114+
Confidence provides **sticky** flag assignments to ensure users receive consistent variant assignments across evaluations. It can be used for two things:
115+
- Pause intake of new entities to an experiment
116+
- Ensure that entities are assigned the same variant throughout an experiment even if some of their targeting attributes change during the experiment.
117+
118+
### What are Sticky Assignments?
119+
120+
When a flag is evaluated for a user, Confidence creates a **materialization** — a snapshot of which variant that user was assigned. On subsequent evaluations, the same variant is returned even if:
121+
122+
- The user's context attributes change (e.g., different country, device type)
123+
- The flag's targeting rules are modified
124+
- New assignments are paused (controlled rollouts)
125+
126+
### How It Works
127+
128+
By default, **sticky assignments are managed by Confidence servers**:
129+
130+
1. First, the local WASM resolver attempts to resolve the flag
131+
2. If sticky assignment data is needed, the provider makes a network call to Confidence's cloud resolvers
132+
3. Materializations are stored on Confidence servers with a **90-day TTL** (automatically renewed on access)
133+
4. No local storage or database setup required
134+
135+
### Benefits
136+
137+
- **Zero configuration**: Works out of the box with no additional setup
138+
- **Managed storage**: Confidence handles all storage and consistency
139+
- **Global availability**: Materializations are available across all your services that are using this flag
140+
141+
### Latency Considerations
142+
143+
When a sticky assignment is needed, the provider makes a network call to Confidence's cloud resolvers. This introduces additional latency (the network latency between your location and Confidence servers) compared to local WASM evaluation.
144+
145+
### Custom Materialization Storage
146+
147+
Some providers support custom storage backends to eliminate network calls for sticky assignments. Check your provider's README for availability and implementation details:
148+
149+
- [Java Provider](java/README.md#sticky-assignments) - Supports custom `MaterializationRepository`
150+
- [JavaScript Provider](js/README.md#sticky-assignments) - Coming soon
151+
- [Go Provider](go/README.md) - Coming soon
152+
153+
### Deep Dive
154+
155+
For technical details on how sticky assignments work at the protocol level, including flowcharts, behavior matrices, and configuration patterns, see the [Sticky Assignments Technical Guide](../STICKY_ASSIGNMENTS.md).
156+
157+
---
158+
159+
## Additional Resources
160+
161+
- [Confidence Documentation](https://confidence.spotify.com/docs)
162+
- [OpenFeature Specification](https://openfeature.dev/specification)
163+
- [Provider-Specific READMEs](.)
164+
- [Go Provider](go/README.md)
165+
- [Java Provider](java/README.md)
166+
- [JavaScript Provider](js/README.md)
167+
- [Ruby Provider](ruby/README.md)
168+
- [Root Repository README](../README.md)
169+
- [Sticky Assignments Technical Guide](../STICKY_ASSIGNMENTS.md)
170+

openfeature-provider/go/README.md

Lines changed: 91 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ go mod tidy
2424
- Go 1.24+
2525
- OpenFeature Go SDK 1.16.0+
2626

27+
## Getting Your Credentials
28+
29+
You'll need a **client secret** from Confidence to use this provider.
30+
31+
**📖 See the [Integration Guide: Getting Your Credentials](../INTEGRATION_GUIDE.md#getting-your-credentials)** for step-by-step instructions on:
32+
- How to navigate the Confidence dashboard
33+
- Creating a Backend integration
34+
- Creating a test flag for verification
35+
- Best practices for credential storage
36+
2737
## Quick Start
2838

2939
```go
@@ -40,35 +50,89 @@ import (
4050
func main() {
4151
ctx := context.Background()
4252

43-
// Create provider with required credentials
53+
// Create provider with your client secret
4454
provider, err := confidence.NewProvider(ctx, confidence.ProviderConfig{
45-
ClientSecret: "your-client-secret",
55+
ClientSecret: "your-client-secret", // Get from Confidence dashboard
4656
})
4757
if err != nil {
4858
log.Fatalf("Failed to create provider: %v", err)
4959
}
5060

51-
// Set the provider
61+
// Set the provider and wait for initialization
5262
openfeature.SetProviderAndWait(provider)
5363

5464
// Get a client
5565
client := openfeature.NewClient("my-app")
5666

57-
// Evaluate a flag
58-
evalCtx = openfeature.NewEvaluationContext("user-123", map[string]interface{}{
67+
// Create evaluation context with user attributes for targeting
68+
evalCtx := openfeature.NewEvaluationContext("user-123", map[string]interface{}{
5969
"country": "US",
6070
"plan": "premium",
6171
})
6272

63-
value, err := client.BooleanValue(ctx, "my-flag.enabled", false, evalCtx)
73+
// Evaluate a flag
74+
value, err := client.BooleanValue(ctx, "test-flag.enabled", false, evalCtx)
6475
if err != nil {
65-
log.Printf("Flag evaluation failed: %v", err)
76+
log.Printf("Flag evaluation failed, using default: %v", err)
6677
}
6778

6879
log.Printf("Flag value: %v", value)
6980
}
7081
```
7182

83+
## Evaluation Context
84+
85+
The evaluation context contains information about the user/session being evaluated for targeting and A/B testing.
86+
87+
### Go-Specific Examples
88+
89+
```go
90+
// Simple attributes
91+
evalCtx := openfeature.NewEvaluationContext("user-123", map[string]interface{}{
92+
"country": "US",
93+
"plan": "premium",
94+
"age": 25,
95+
})
96+
```
97+
98+
## Error Handling
99+
100+
The provider uses a **default value fallback** pattern - when evaluation fails, it returns your specified default value instead of throwing an error.
101+
102+
**📖 See the [Integration Guide: Error Handling](../INTEGRATION_GUIDE.md#error-handling)** for:
103+
- Common failure scenarios
104+
- Error codes and meanings
105+
- Production best practices
106+
- Monitoring recommendations
107+
108+
### Go-Specific Examples
109+
110+
```go
111+
// The provider returns the default value on errors
112+
value, err := client.BooleanValue(ctx, "my-flag.enabled", false, evalCtx)
113+
if err != nil {
114+
// Log the error for debugging
115+
log.Printf("Flag evaluation failed, using default: %v", err)
116+
}
117+
// value will be 'false' if evaluation failed
118+
119+
// For critical flags, you might want to check the error
120+
if err != nil && strings.Contains(err.Error(), "FLAG_NOT_FOUND") {
121+
log.Warn("Flag 'my-flag' not found in Confidence - check flag name")
122+
}
123+
124+
// During initialization with timeout
125+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
126+
defer cancel()
127+
128+
provider, err := confidence.NewProvider(ctx, confidence.ProviderConfig{
129+
ClientSecret: "your-client-secret",
130+
})
131+
if err != nil {
132+
log.Fatalf("Provider initialization failed: %v", err)
133+
}
134+
```
135+
72136
## Configuration
73137

74138
### Environment Variables
@@ -88,36 +152,25 @@ The `ProviderConfig` struct contains all configuration options for the provider:
88152
#### Optional Fields
89153

90154
- `Logger` (*slog.Logger): Custom logger for provider operations. If not provided, a default text logger is created. See [Logging](#logging) for details.
91-
- `ConnFactory` (func): Custom gRPC connection factory for advanced use cases (e.g., custom interceptors, TLS configuration)
155+
- `TransportHooks` (TransportHooks): Custom transport hooks for advanced use cases (e.g., custom gRPC interceptors, HTTP transport wrapping, TLS configuration)
92156

93157
#### Advanced: Testing with Custom State Provider
94158

95-
For testing purposes only, you can provide a custom `StateProvider` to supply resolver state from local sources (e.g., a file cache):
159+
For testing purposes only, you can provide a custom `StateProvider` and `FlagLogger` to supply resolver state and control logging behavior:
96160

97161
```go
98162
// WARNING: This is for testing only. Do not use in production.
99-
provider, err := confidence.NewProviderWithStateProvider(ctx,
100-
confidence.ProviderConfigWithStateProvider{
101-
ClientSecret: "your-client-secret",
163+
provider, err := confidence.NewProviderForTest(ctx,
164+
confidence.ProviderTestConfig{
102165
StateProvider: myCustomStateProvider,
103-
AccountId: "your-account-id",
104-
// WasmBytes: customWasmBytes, // Optional: custom WASM module
166+
FlagLogger: myCustomFlagLogger,
167+
ClientSecret: "your-client-secret",
168+
Logger: myCustomLogger, // Optional: custom logger
105169
},
106170
)
107171
```
108172

109-
**Important**: This configuration disables automatic state fetching and exposure logging. For production deployments, always use `NewProvider()` with `ProviderConfig`.
110-
111-
## Credentials
112-
113-
Get your client secret from your [Confidence dashboard](https://confidence.spotify.com/):
114-
115-
- `ClientSecret`: The client secret used for authentication and flag evaluation
116-
117-
118-
## WebAssembly Module
119-
120-
The WASM module (`confidence_resolver.wasm`) is embedded in the Go binary using Go 1.16+ embed directives. No external WASM file is required at runtime.
173+
**Important**: This configuration requires you to provide both a `StateProvider` and `FlagLogger`. For production deployments, always use `NewProvider()` with `ProviderConfig`.
121174

122175
## Flag Evaluation
123176

@@ -162,26 +215,23 @@ provider, err := confidence.NewProvider(ctx, confidence.ProviderConfig{
162215

163216
The provider logs at different levels: `Debug` (flag resolution details), `Info` (state updates), `Warn` (non-critical issues), and `Error` (failures).
164217

165-
## Troubleshooting
166-
167-
### Provider Creation Fails
218+
## Shutdown
168219

169-
If provider creation fails, verify:
170-
- `ClientSecret` is correct
171-
- Your application has network access to the Confidence CDN
172-
- Credentials have the necessary permissions in your Confidence dashboard
220+
**Important**: Always shut down the provider when your application exits to ensure proper cleanup and log flushing.
173221

174-
### No Flag Evaluations Work
222+
```go
223+
// Shutdown the provider on application exit
224+
openfeature.Shutdown()
225+
```
175226

176-
Common issues:
177-
- Ensure you've called `openfeature.SetProviderAndWait(provider)` before creating clients
178-
- Check that your flags are published and active in Confidence
227+
### What Happens During Shutdown?
179228

180-
### Performance Issues
229+
1. **Flushes pending logs** to Confidence (exposure events, resolve analytics)
230+
2. **Closes gRPC connections** and releases network resources
231+
3. **Stops background tasks** (state polling, log batching)
232+
4. **Releases WASM instance** and memory
181233

182-
For optimal performance:
183-
- Reuse the same `provider` instance across your application
184-
- Create OpenFeature clients once and reuse them
234+
The shutdown respects the context timeout you provide.
185235

186236
## License
187237

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Contributing
2+
3+
## Development
4+
5+
### Code Formatting
6+
7+
This project uses the [Spotify fmt-maven-plugin](https://github.com/spotify/fmt-maven-plugin) for consistent code formatting.
8+
9+
**Check formatting:**
10+
```bash
11+
mvn fmt:check
12+
```
13+
14+
**Auto-format code:**
15+
```bash
16+
mvn fmt:format
17+
```
18+
19+
The `fmt:check` goal runs automatically during the build to ensure all code is properly formatted.
20+

0 commit comments

Comments
 (0)