> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useshipd.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Roll out a flag to a percentage of users

> Serve a flag to a sticky percentage of users with a Percentage condition, then raise it gradually.

## Overview

A percentage rollout serves a flag to a fixed share of users, for example 10% get `new-checkout`. Each user always lands in the same bucket, so raising the percentage adds users without removing anyone already in.

## Add the rollout

**1. Open the flag**

In the [dashboard](https://app.useshipd.com), open the project, click `new-checkout`, and select the environment.

**2. Add a percentage rule**

Click **Add rule**, then **Percentage**. Set **Rollout** to `10` and the rule's **Serve** to `true`. Leave **Fallthrough** at `false`.

**3. Save**

Click **Save environment**.

Your code doesn't change, but it must send a `userId`:

```ts TypeScript theme={null}
// src/checkout.ts
shipd.isEnabled("new-checkout", { userId: "usr_8f2k19" });
```

```go Go theme={null}
// checkout.go
client.IsEnabled("new-checkout", shipd.EvaluationContext{UserID: "usr_8f2k19"})
```

## How bucketing works

The SDK hashes the flag key and the `userId` into a bucket from 0 to 9999. The rule matches when the bucket is below **Rollout** × 100, so `10` matches buckets 0–999.

* **Sticky.** The same user and flag always get the same bucket. No assignment is stored.
* **Monotonic.** Raising **Rollout** from 10 to 25 keeps buckets 0–999 in and adds 1000–2499.
* **Independent per flag.** A user in the first 10% of `new-checkout` isn't necessarily in the first 10% of another flag. To align flags, give them the same **Seed**.
* **Needs an ID.** Without a `userId` (or the chosen bucketing attribute) the condition never matches and the user gets the next rule or **Fallthrough**.

## Bucket by something other than the user

Set **Of attribute** to bucket on another context attribute, for example `organizationId`, so everyone in an organization gets the same result:

```ts TypeScript theme={null}
shipd.isEnabled("new-checkout", {
  userId: "usr_8f2k19",
  attributes: { organizationId: "org_fieldkit" },
});
```

```go Go theme={null}
client.IsEnabled("new-checkout", shipd.EvaluationContext{
    UserID: "usr_8f2k19",
    Attributes: map[string]any{"organizationId": "org_fieldkit"},
})
```

Leave **Of attribute** empty to bucket on `userId`.

## Combine with targeting

Conditions in one rule must all match. Add an **Attribute** condition to the same rule to roll out to 10% of Pro users only (`plan is pro` and **Percentage** `10`). Rules run top to bottom, so put an internal-staff rule above the rollout to keep staff always on. See [Target users by attribute](/flags/targeting).

## Finish the rollout

At 100%, the rule matches every user with an ID. To finish, set **Fallthrough** to `true`, remove the rule, and set `default: true` in TypeScript definitions or `Default: true` in Go definitions so the next `shipd push` doesn't set **Fallthrough** back to `false`. Later, remove the flag from code and [archive it](/flags/naming-and-lifecycle).

## Per environment

Rollouts are environment state. A 10% rollout in `staging` doesn't affect `production`. To copy the enabled state, rules, and **Fallthrough** to other environments, click **Sync to…** on the flag page.

## Approvals

If an [approval policy](/operations/approvals) protects the environment, saving can create a pending request instead of changing the live flag. Policies can target rollout increases specifically. SDKs keep serving the current rollout until the request is approved.

## Related

* [Run an A/B test](/flags/experiments)
* [Turn a feature off immediately](/flags/kill-switch)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.