ShipEasy
Flags & ExperimentsConfigs

Quickstart

From zero to a flag-gated feature in production — install the SDK, create a gate, roll it out, kill-switch it, and watch it in the dashboard. Five minutes, end to end.

TutorialOn this page · 8 min readUpdated · May 3, 2026Works with · Server SDK · Browser SDK · CLI

This walks through the full flow end to end: install the SDK, create a gate, gate code, roll it out gradually, then kill-switch it. By the end you'll have a feature behind a flag in production, evaluated locally with no per-request network call, that you can flip from your phone.

If you'd rather hand this to an AI agent, the LLM guide describes the same flow for Claude Code, Cursor, and Windsurf.

The 5-minute path

install · create · gate · roll out
01 · INSTALL

Install the SDK & log in

$npm install @shipeasy/sdk && shipeasy login
02 · CREATE

Create a gate at 0%

$shipeasy flags create new-checkout-flow --rollout 0
03 · ROLL OUT

Bump it to 5%, then 100%

$shipeasy flags rollout new-checkout-flow --percent 5

Prerequisites

  • A ShipEasy project. Sign up at shipeasy.ai — the free tier is enough for this walkthrough.
  • A server SDK key scoped to your environment. Find it in Project → SDK keys → Create → server, or generate one from the CLI:
shipeasy keys create --kind server
  • The SDK installed in your project:
$npm install @shipeasy/sdk

Initialise the SDK

Initialise once at boot. The server SDK polls flag updates in the background — no per-request cost, no fetch on the hot path.

src/lib/shipeasy.ts
import { configureShipeasy, flags } from "@shipeasy/sdk/server";

configureShipeasy({
  apiKey: process.env.SHIPEASY_SERVER_KEY!,
  
});

export const ready = flags.init();

Import this once from your entrypoint and await ready before serving the first request. After that, every flags.gate() call is synchronous and reads from in-process memory.

One configure call, one key

Flags, dynamic values, experiments, and i18n all share the same apiKey. Don't create per-feature wrappers in your codebase — the SDK owns its own initialisation.

Create a gate

From the CLI:

shipeasy flags create new-checkout-flow --rollout 0

Or in the dashboard: Configs → Gates → New gate, name new-checkout-flow, leave the rollout at 0%. The default value is false — nobody sees the new path yet.

Naming conventions to save your future self pain:

  • kebab-case: new-checkout-flow, not newCheckoutFlow.
  • Describe the change, not the abstract feature: enable-redis-pool ages better than redis-pool.
  • Prefix by area: checkout-, nav-, infra-. Makes deletion sweeps easy.

Gate your code

import { flags } from "@shipeasy/sdk/server";

export async function getCheckoutComponent(user) {
  if (flags.gate("new-checkout-flow", user)) {
    return import("./checkout/new");
  }
  return import("./checkout/legacy");
}

gate() is synchronous and reads from the in-process cache. Pass any user attributes you want available to targeting rules — at minimum { user_id }. The shape of user is just a plain object:

{
  user_id: "u_4f2a",      // required for deterministic bucketing
  plan: "pro",            // any attribute your rules want
  country: "US",
  tenure_days: 142,
}

Test locally with overrides

Add yourself to the overrides list in the dashboard (Gate → Overrides → Always on, paste your user_id). The flag returns true for you and false for everyone else — no rollout math involved.

If you have the browser DevTools overlay loaded, you can also force a value with a query param:

https://app.local/?shipeasy_gate_new-checkout-flow=true

That's persisted in localStorage and survives reloads until you toggle it off.

Roll it out gradually

Bump the rollout from 0%5%25%100% over a few hours, watching your error rate and p95.

shipeasy flags rollout new-checkout-flow --percent 5
shipeasy flags rollout new-checkout-flow --percent 25
shipeasy flags rollout new-checkout-flow --percent 100

Bucketing is deterministic by user_id, so the same 5% always sees the new path. You won't see flicker as you bump the percentage — you only ever add users to the bucket, never reshuffle.

A safe schedule for an unknown-risk change:

0%   → enable rule + dogfood with overrides
1%   → 1 hour, watch error rate + p95
5%   → 4 hours
25%  → overnight
50%  → 1 day
100% → keep an eye on it for a week, then delete the flag

Killswitch (when something breaks)

If something goes wrong, flip the killswitch. It overrides every targeting rule and serves false everywhere within the SDK's next poll (default 30s, faster on Pro).

shipeasy flags disable new-checkout-flow --killswitch

Dashboard: toggle the red Killswitch chip on the gate row. The killswitch is a separate field from enabled, so flipping it preserves your targeting rules and rollout — re-enabling restores the full prior state.

Wire it into CI

Add a check that fails the build if you reference a flag that doesn't exist on the project:

.github/workflows/ci.yml
- name: Validate flags
  run: shipeasy flags validate ./src

This catches typos (new-checkut-flow) and stale references after a flag has been deleted.

What just happened

When you flipped that rollout from 25 to 100, ShipEasy propagated the change worldwide. Within the SDK's poll interval (default 30s, 10s on Pro, push on Enterprise), every server saw the new value.

No deploy. No restart. No backend round-trip on the read.

What you just got for free

An audit log of every change, side-by-side dashboards for the affected gate, deterministic bucketing per user, and a kill-switch you can hit from your phone. You didn't have to build any of it.

Where to next

NEXT

Hand it to an AI agent.

The same flow, but driven by Claude Code or Cursor through the MCP server. You answer one auth prompt, the agent creates the gate, wires the SDK, and opens the PR.

Install the MCP
$shipeasy mcp install
Then ask your agent
$ship a flag for new checkout, default off
Was this page helpful?✎ Edit on GitHub

On this page