# Flags in your code (/docs/feature-flags/in-code)

Every flag gives your code one of these values:

| Value | Meaning |
| --- | --- |
| `true` / `false` | Boolean flag: on or off for this player. |
| `"control"`, `"test"`, … | Multivariate flag or experiment: the variant. |
| `nil` (`undefined` in JavaScript) | Unknown flag, the flag is off, or RoChart couldn't be reached. Take the default path. |

## In your game (Luau) [#in-your-game-luau]

Flags are read on the server, for one player:

```luau
local variant = RoChart.getFlag(player, "short-tutorial")

if variant == "short" then
  runTutorial(player, SHORT_STEPS)
else
  runTutorial(player, FULL_STEPS) -- "control", false or nil
end
```

| Function | Returns | Counts as exposure |
| --- | --- | --- |
| `getFlag(player, key)` | The value | Yes |
| `isFeatureEnabled(player, key)` | `true` for on or any variant | Yes |

Targeting uses what RoChart knows about the player: the [properties](/docs/getting-started/identify) the module sets on join (platform, Premium, country, account age…), your own from `setUserProperties`, and cohorts.

Because the value is decided on the server, a player can't switch their own variant from the client. If the server needs to tell the client which variant to show, send it with a RemoteEvent or an attribute on the player.

## Exposures and experiments [#exposures-and-experiments]

Reading a flag with `getFlag` or `isFeatureEnabled` records that the player **saw** it (a `$feature_flag_called` event). [Experiments](/docs/experiments) only count players who were exposed. So:

- **Read the flag where the experience starts**, such as when the tutorial begins or the shop opens, not when the player joins. Otherwise players who never reached the change dilute the result.
- **Handle every variant and `nil`.** Show `control` when in doubt.
- **The same player keeps their variant** on every server, so rejoining or switching devices doesn't reshuffle them.

## Websites and backends [#websites-and-backends]

The browser and Node SDKs read the same flags, for example on your game's website or in a Discord bot.

<Tabs items={['Browser', 'Node / Bun']}>
<Tab value="Browser">

```ts
const variant = rochart.getFeatureFlag('new-landing-page')
if (variant === 'test') renderNewLanding()
```

Flags load when the page starts, and again after `identify`, `group` and `reset`. On a first visit they're `undefined` for a moment; `rochart.onFeatureFlags(callback)` runs once they're in. Pass `{ sendEvent: false }` to read a flag without recording an exposure, and use `rochart.overrideFeatureFlags({ 'new-landing-page': 'test' })` to force values in your own browser while testing (`false` clears them).

</Tab>
<Tab value="Node / Bun">

```ts
const variant = await rochart.getFeatureFlag({
  distinctId: String(robloxUserId),
  key: 'bot-daily-reward',
  personProperties: { premium: true }, // values you know but haven't sent yet
})
```

If RoChart can't be reached within a few seconds, you get `undefined`, so always handle the default.

</Tab>
</Tabs>

## Other languages [#other-languages]

`POST /i/flags` returns every flag for a player. See the [SDK reference](/docs/reference/sdk#http-endpoints). If you use a value in an experiment, also send a `$feature_flag_called` event so the exposure counts.
