Migrating from LaunchDarkly
qfg migrate --from launchdarkly imports a LaunchDarkly project into a
Quonfig workspace. The current state of every flag, segment, and environment
is converted losslessly (modulo a small documented gap list, see below) and
either pushed to a hosted Quonfig workspace or written out as a standalone
git repo your own SDK can read.
This page is the quick-start. The companion artifact you actually need to
read after every run is the MIGRATION_REPORT.md the migrator writes
into the workspace — see Read the MIGRATION_REPORT.md
below.
Shipped in qfg 0.0.49.
Quick start (the 3-command flow)
qfg login
qfg workspace create my-team --org my-org
qfg migrate --from launchdarkly \
--source-api-key $LAUNCHDARKLY_API_KEY \
--push --workspace my-org/my-team \
--dir ./my-quonfig-workspace
That's it. The migrator clones the hosted workspace into ./my-quonfig-workspace,
pulls every flag and segment from LaunchDarkly, converts them, validates the
result client-side, commits, and pushes.
No cloud account? Drop --push and --workspace, keep --dir. You'll
get a standalone Quonfig workspace git repo on disk that your server-side SDK
can read directly via WithDatadir(...) (or the equivalent option). See
from-launch
for the local-only flow — it works the same way with LaunchDarkly.
Browser-SDK note. The local-only
--dirflow only works for server-side SDKs (Node, Go). Browser apps must use the--pushcloud flow because the browser SDK loads config over SSE fromapi-deliveryand has no datadir mode. See from-launch for details.
Get a LaunchDarkly API token
In the LaunchDarkly UI: Account Settings → Authorization → access tokens. Create an account-level token with read access — that's enough; the migrator never writes back to LaunchDarkly.
Export it under any of these env vars (the CLI checks in order):
export LAUNCHDARKLY_API_KEY="api-..." # provider-specific (preferred)
# or
export QUONFIG_MIGRATE_API_KEY="api-..." # generic, works for any source
Or pass --source-api-key <token> on the command line.
LaunchDarkly tokens use header auth — no
Bearerprefix. The migrator handles this for you. Rate limits are per-account, shared across all tokens, and unpublished; the migrator backs off onX-Ratelimit-Resetautomatically.
What happens during migration
The migrator runs in up to two phases:
- Phase 1 (always): config snapshot. Roughly ~60 API calls for a typical 500-flag project. Fast — seconds to a couple of minutes. This is the "imports current state" pass and is fully lossless within the documented gap list.
- Phase 2 (only with
--full-summary): history backfill. Walks the LaunchDarkly audit log and replays each change as a per-flag git commit with the original author, date, and message. Slow — hours for a real account. Resumable on its own checkpoint, so an interrupted run picks up where it left off.
History retention is plan-gated on the LaunchDarkly side: Developer plans retain only the last 30 days; unlimited history is Enterprise/select-plan only. Anything outside that window is permanently gone in LaunchDarkly and no API can recover it. The migrator probes your account's retention before the slow phase starts and tells you the real horizon up front — you never wait hours expecting two years and silently get thirty days.
If you don't need history yet, leave --full-summary off. You can re-run
later with the flag to backfill.
Read the MIGRATION_REPORT.md
This is the most important step. Every run writes a fresh
MIGRATION_REPORT.md at the root of the workspace directory. It is the
honest record of what came across cleanly and what needs human eyes.
Three things in particular need your attention before you flip an SDK over:
- Users will be re-bucketed. LaunchDarkly and Quonfig hash percentage rollouts differently. The rollout percentages are preserved exactly, but individual users will land in different buckets after cutover. The report lists every flag with a percentage rollout under a "users will be re-bucketed" heading — plan your comms accordingly.
- Dropped prerequisites. Quonfig has no cross-flag dependency operator in v1. Each dropped prerequisite is named in the report; the downstream flag's own targeting still works, but the gate doesn't.
- Skipped rules. Any clause whose operator couldn't be converted is listed by flag and clause so you can rebuild it by hand.
The report also documents informational drops (LaunchDarkly maintainer
metadata, mobile-key availability, customProperties) so nothing is
silently lost.
Point your coding agent at it
The fastest workflow:
claude "read MIGRATION_REPORT.md and tell me what I need to do before \
flipping the SDK to Quonfig"
The report is structured enough that an agent can summarize the must-fix items and propose concrete edits to your remaining LaunchDarkly call sites.
What doesn't migrate cleanly
The full gap list lives in your workspace's MIGRATION_REPORT.md. The short
version, drawn from the design plan §5.4:
- Prerequisites — dropped and reported. The downstream flag's targeting survives; the cross-flag gate does not. Re-implement as in-flag rules or wait for native prerequisite support.
- Individual / context targets — LaunchDarkly's
targetsandcontextTargetsare converted to a leadingPROP_IS_ONE_OFrule on the identifier attribute. Evaluation order is preserved; you lose the dedicated targets-pane UI affordance. privateAttributes— dropped and reported. No Quonfig equivalent in v1.- AI Configs — out of scope for v1. The fetcher may enumerate them for the report, but no Quonfig file is emitted.
- Big / synced segments — the segment shell migrates. Membership does not, because LaunchDarkly does not expose unbounded segment members via REST. "Membership unavailable" is recorded in the report.
- Negated comparison / date / semver operators — skipped and reported
per flag and clause. Quonfig has no negated form of
lessThan/greaterThan/before/after/semVerEqualetc., and an algebraic flip would need combinatorial rule expansion (the missing-attribute trap makes it subtle). Negatedin/contains/startsWith/endsWith/matches/segmentMatchare unaffected — they have direct negated operators and always convert. - Bucketing — as above, individual user-to-bucket assignments change post-cutover even though the rollout percentages are identical.
- Experiment rollouts — converted to plain
weighted_values; thekind: "experiment"framing andseedare dropped and reported. - Maintainer metadata, mobile-key availability,
customProperties— dropped and reported. Authorship lives in git history in Quonfig.
Re-running
Each qfg migrate run overwrites MIGRATION_REPORT.md and re-asserts the
workspace state from the source. To experiment with different conversions
or compare runs side-by-side, create distinct workspaces (e.g.
competitor-launchdarkly-1, -2) and migrate into each.
--dry-run summarizes what would happen without writing anything — handy
for sanity-checking a fresh token before committing to a real run.
Command reference
The full flag list is shared with the other sources — see
from-launch → Command reference.
LaunchDarkly-specific notes:
--from launchdarklyselects this source.--source-api-key <token>(env:LAUNCHDARKLY_API_KEYorQUONFIG_MIGRATE_API_KEY) — the LaunchDarkly account-level read token. The older--api-key/LAUNCH_API_KEYpair is kept as a deprecated alias for thelaunchsource only.--full-summary— opt into the slow Phase-2 audit-log backfill.
Run qfg migrate --help for live descriptions.
Next steps
- Troubleshooting common issues
- Migrate your call sites — point your coding agent at
.qf/identifier-map.json(the legacy → Quonfig key map the migrator writes) to rewrite LaunchDarkly SDK call sites. - Swap your SDK: Node, Go, JavaScript (browser)