Skip to content

Commit 954dee7

Browse files
committed
docs(flags): Document local eval with Java SDK
1 parent c86e922 commit 954dee7

File tree

5 files changed

+197
-10
lines changed

5 files changed

+197
-10
lines changed

contents/docs/feature-flags/adding-feature-flag-code.mdx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import IOSFeatureFlagsCode from '../integrate/feature-flags-code/_snippets/featu
2626
import FlutterFeatureFlagsCode from '../integrate/feature-flags-code/_snippets/feature-flags-code-flutter.mdx'
2727
import ElixirFeatureFlagsCode from '../integrate/feature-flags-code/_snippets/feature-flags-code-elixir.mdx'
2828
import DotNetFeatureFlagsCode from '../integrate/feature-flags-code/_snippets/feature-flags-code-dotnet.mdx'
29+
import JavaFeatureFlagsCode from '../integrate/feature-flags-code/_snippets/feature-flags-code-java.mdx'
2930

3031
<!-- prettier-ignore -->
3132
<Tab.Group tabs={['Web', 'React', 'Node.js', 'Python', 'PHP', 'Ruby', 'Go', 'React Native', 'Android', 'iOS', 'Flutter', 'Java', 'Rust', 'Elixir', '.NET', 'api']}>
@@ -82,9 +83,7 @@ import DotNetFeatureFlagsCode from '../integrate/feature-flags-code/_snippets/fe
8283
<FlutterFeatureFlagsCode />
8384
</Tab.Panel>
8485
<Tab.Panel>
85-
<blockquote class='warning-note'>
86-
Feature flags are not supported yet in our <a href="/docs/libraries/java">Java SDK</a>. However, you can integrate them into your project by using the <a href="/docs/feature-flags/adding-feature-flag-code?tab=api">PostHog API</a>.
87-
</blockquote>
86+
<JavaFeatureFlagsCode />
8887
</Tab.Panel>
8988
<Tab.Panel>
9089
<blockquote class='warning-note'>

contents/docs/integrate/_snippets/install-java.mdx

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
The best way to install the PostHog Java SDK is with a build system like Gradle or Maven. This ensures you can easily upgrade to the latest versions.
22

3-
Look up the latest version in [com.posthog.posthog-server](https://central.sonatype.com/artifact/com.posthog/posthog-server).
3+
Look up the latest version of [`com.posthog.posthog-server`](https://central.sonatype.com/artifact/com.posthog/posthog-server).
44

55
#### Gradle
66

77
All you need to do is add the `posthog-server` module to your `build.gradle`:
88

99
```gradle_kotlin file=build.gradle
1010
dependencies {
11-
implementation 'com.posthog:posthog-server:1.+'
11+
implementation 'com.posthog:posthog-server:2.+'
1212
}
1313
```
1414

@@ -26,7 +26,7 @@ All you need to do is add the `posthog-server` module to your `pom.xml`:
2626

2727
#### Other
2828

29-
See [com.posthog.posthog-server](https://central.sonatype.com/artifact/com.posthog/posthog-server) in the Maven Central Repository. Clicking on the latest version shows you options for adding dependencies for other build systems.
29+
See [`com.posthog.posthog-server`](https://central.sonatype.com/artifact/com.posthog/posthog-server) in the Maven Central Repository. Clicking on the latest version shows you options for adding dependencies for other build systems.
3030

3131
### Setup
3232

@@ -42,13 +42,31 @@ class Sample {
4242
public static void main(String args[]) {
4343
PostHogConfig config = PostHogConfig
4444
.builder(POSTHOG_API_KEY)
45-
.host(POSTHOG_HOST)
45+
.host(POSTHOG_HOST) // TIP: host is optional if you use https://us.i.posthog.com
4646
.build();
4747

48-
PostHogInterface postHog = PostHog.with(config);
48+
PostHogInterface posthog = PostHog.with(config);
4949

50-
postHog.close(); // send the last events in queue
50+
posthog.flush(); // send any remaining events
51+
posthog.close(); // shut down the client
5152
}
5253
}
5354
```
5455

56+
## Integrating with Spring
57+
58+
To see how to integrate the PostHog SDK with Spring, check out this [sample project](https://github.com/PostHog/posthog-android/tree/main/posthog-samples/posthog-spring-sample).
59+
60+
## Debug mode
61+
62+
If you're not seeing the expected events being captured, the feature flags being evaluated, or the surveys being shown, you can enable debug mode to see what's happening.
63+
64+
To see detailed logging, set the debug configuration option to true.
65+
66+
```java
67+
PostHogConfig config = PostHogConfig
68+
.builder(POSTHOG_API_KEY)
69+
.host(POSTHOG_HOST)
70+
.debug(true)
71+
.build();
72+
```
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
### Boolean feature flags
2+
3+
```java
4+
5+
if (posthog.isFeatureEnabled("distinct_id_of_your_user", "flag-key")) {
6+
// Do something differently for this user
7+
8+
// Optional: fetch the payload
9+
Object matchedFlagPayload = PostHog.getFeatureFlagPayload("distinct_id_of_your_user", "flag-key")
10+
}
11+
```
12+
13+
### Multivariate feature flags
14+
15+
```java
16+
if ("variant-key".equals(posthog.getFeatureFlag("distinct_id_of_your_user", "flag-key"))) { // replace 'variant-key' with the key of your variant
17+
// Do something differently for this user
18+
19+
// Optional: fetch the payload
20+
Object matchedFlagPayload = PostHog.getFeatureFlagPayload("distinct_id_of_your_user", "flag-key")
21+
}
22+
```
23+
24+
import JavaOverrideServerProperties from './override-server-properties/java.mdx'
25+
26+
<JavaOverrideServerProperties />
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import OverrideServerPropertiesIntro from './override-server-properties-intro.mdx'
2+
3+
<OverrideServerPropertiesIntro />
4+
5+
```java
6+
import com.posthog.server.PostHogFeatureFlagOptions;
7+
8+
posthog.getFeatureFlag(
9+
'distinct_id_of_the_user',
10+
'flag-key',
11+
PostHogFeatureFlagOptions
12+
.builder()
13+
.defaultValue(false)
14+
.group("your_group_type", "your_group_id")
15+
.group("another_group_type", "your_group_id")
16+
.groupProperty("your_group_type", "group_property_name", "value")
17+
.groupProperty("another_group_type", "group_property_name", "value")
18+
.personProperty("property_name", "value")
19+
.build());
20+
```
21+
22+
import OverrideGeoIPPropertiesSDK from './override-geoip-properties-SDKs.mdx'
23+
24+
<OverrideGeoIPPropertiesSDK />

contents/docs/libraries/java/index.mdx

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,127 @@ PostHogConfig config = PostHogConfig
179179

180180
### Local evaluation
181181

182-
While planned, local evaluation of feature flags is not yet supported in the Java SDK. All feature flag evaluations occur remotely via the PostHog API.
182+
import LocalEvaluationIntro from "../feature-flags/snippets/local-evaluation-intro.mdx"
183+
184+
<LocalEvaluationIntro />
185+
186+
Local evaluation improves performance by fetching flag definitions once and evaluating them locally, rather than making API calls for each flag check.
187+
188+
#### Configuration
189+
190+
To enable local evaluation, you need:
191+
192+
1. A **personal API key** (not your project API key)
193+
- Generate one at: PostHog → Settings → Account → Personal API Keys
194+
2. Enable `localEvaluation` in your config
195+
196+
```java
197+
PostHogConfig config = PostHogConfig
198+
.builder(POSTHOG_API_KEY)
199+
.host(POSTHOG_HOST)
200+
.personalApiKey("phx_your_personal_api_key_here")
201+
.localEvaluation(true)
202+
.pollIntervalSeconds(30) // Optional: customize the rate which flag definitions are polled (default: 30s)
203+
.build();
204+
```
205+
206+
> **Note:** Setting the `personalApiKey` automatically enables `localEvaluation` unless explicitly set to `false`.
207+
208+
#### Usage with person and group properties
209+
210+
For local evaluation to work, you must provide person properties, groups, or group properties that are used in your flag's [release conditions](/docs/feature-flags/creating-feature-flags#release-conditions).
211+
212+
Use `PostHogFeatureFlagOptions` to provide these properties:
213+
214+
**Basic flag evaluation:**
215+
```java
216+
boolean isEnabled = postHog.isFeatureEnabled("distinct-id", "flag-key");
217+
```
218+
219+
**With person properties:**
220+
```java
221+
PostHogFeatureFlagOptions options = PostHogFeatureFlagOptions
222+
.builder()
223+
.defaultValue(false)
224+
.personProperty("plan", "premium")
225+
.personProperty("email", "[email protected]")
226+
.build();
227+
228+
boolean isEnabled = postHog.isFeatureEnabled("distinct-id", "flag-key", options);
229+
```
230+
231+
**With groups:**
232+
```java
233+
PostHogFeatureFlagOptions options = PostHogFeatureFlagOptions
234+
.builder()
235+
.defaultValue(false)
236+
.group("company", "company_id_in_your_db")
237+
.build();
238+
239+
boolean isEnabled = postHog.isFeatureEnabled("distinct-id", "flag-key", options);
240+
```
241+
242+
**With group properties:**
243+
```java
244+
PostHogFeatureFlagOptions options = PostHogFeatureFlagOptions
245+
.builder()
246+
.defaultValue(false)
247+
.group("company", "company_id_in_your_db")
248+
.groupProperty("company", "industry", "technology")
249+
.groupProperty("company", "employees", 500)
250+
.build();
251+
252+
boolean isEnabled = postHog.isFeatureEnabled("distinct-id", "flag-key", options);
253+
```
254+
255+
**Multivariate flags:**
256+
```java
257+
PostHogFeatureFlagOptions options = PostHogFeatureFlagOptions
258+
.builder()
259+
.defaultValue("control")
260+
.personProperty("plan", "premium")
261+
.group("company", "company_id_in_your_db")
262+
.groupProperty("company", "industry", "technology")
263+
.build();
264+
265+
Object flagValue = postHog.getFeatureFlag("distinct-id", "algorithm", options);
266+
String algorithm = flagValue instanceof String ? (String) flagValue : "control";
267+
switch (algorithm) {
268+
case "neural_network":
269+
useNeuralNetwork();
270+
break;
271+
case "collaborative_filtering":
272+
useCollaborativeFiltering();
273+
break;
274+
default:
275+
useControlAlgorithm();
276+
}
277+
```
278+
279+
#### How it works
280+
281+
1. **On initialization:** PostHog fetches all feature flag definitions using your personal API key
282+
2. **Periodic updates:** Flag definitions are refreshed every 30 seconds (configurable via `pollIntervalSeconds`)
283+
3. **Local evaluation:** When you call `getFeatureFlag` or `isFeatureEnabled`, PostHog evaluates the flag locally using the cached definitions
284+
4. **Automatic fallback:** If local evaluation fails (e.g., missing cohort data), PostHog automatically falls back to making an API call
285+
5. **Fallback caching:** In the event that a flag must be evaluated remotely with an API call, the result is cached according to the [Feature flag caching](#feature-flag-caching) section.
286+
#### Benefits
287+
288+
- **Reduced latency**: No API call needed for most flag evaluations
289+
- **Lower costs**: Fewer API requests (polling is billed as ~10 flag requests per poll)
290+
- **Offline support**: Flags continue to work with cached definitions
291+
292+
#### Limitations
293+
294+
Local evaluation is **not possible** for flags that:
295+
296+
1. Have **experience continuity** enabled (set when you check "persist flag across authentication steps")
297+
2. Are linked to an **early access feature**
298+
3. Depend on **static cohorts**
299+
300+
For these flags, PostHog automatically falls back to remote evaluation via the API.
301+
302+
For more details, see our [local evaluation guide](/docs/feature-flags/local-evaluation).
183303

184304
### Feature flag caching
185305

0 commit comments

Comments
 (0)