DEV Community

PaxtonShaw1459
PaxtonShaw1459

Posted on

Delivery Feature Flags: 3 Decisions for Percentage Rollouts and Basic Targeting

Short answer: for a Node.js or Next.js notification service, a simple API can be a practical LaunchDarkly alternative when the job is limited to release toggles, percentage rollouts, and a fast kill switch. Keep LaunchDarkly when incident reconstruction depends on audit history, evaluation metrics, flag dependency graphs, or realtime streaming; those are operational controls, not optional polish.

This architecture decision covers an e-commerce service introducing a new delivery provider. The flag limits exposure while the team watches delivery failures, but the sensitive decision remains server-side. Three decisions matter: what evidence an incident must preserve, where evaluation occurs, and how clients learn that a flag changed.

Decision: use a small server-evaluated flag surface for this release path, poll for refresh, and record the evaluated flag key and result beside each delivery attempt. Do not turn the flag platform into an experiment engine by accident.

The cost model begins with incident evidence

The invariant is more important than the vendor: every notification attempt must be explainable after the rollout has moved on. A delivery record therefore needs the message identifier, provider choice, flag key, evaluated result, rollout cohort or stable subject identifier, and a correlation identifier. Keep those fields compact. A free-form copy of the full request is expensive, harder to delete safely, and usually less useful than a small decision record.

Cardinality deserves explicit treatment. flag_key and provider are bounded labels; message_id and user_id are not. Put high-cardinality identifiers in logs, not metric labels. Metrics should count attempts and failures by bounded dimensions such as provider, region, and result class. Logs can carry a trace_id and span_id for correlation, although that does not create a distributed-trace query or a span tree.

For a hypothetical rollout, moving from 5% to 25% changes the population under risk by a factor of five. That is why a chart saying “failure rate rose” is insufficient. Imagine the provider starts rejecting one class of address after the rollout advances: an aggregate failure counter shows the symptom, but investigation still needs the flag result captured before dispatch, the bounded provider and region dimensions, and the high-cardinality message identifier kept out of metric labels. Retries may later succeed through the established provider, masking the first decision if the service records only the final outcome. Retention follows the same logic: keep the compact decision logs long enough to cover the longest credible complaint or reconciliation window, preserve all failures during the active incident, sample routine successes, and then delete both on purpose. I’m not sure what that window should be for every shop; contract terms and privacy obligations resolve it, not a generic observability default.

Keep the event small.

The failure boundary is also clear. A stale client may display an unfinished feature, but it must not authorize a sensitive delivery action. Browser and edge clients can poll for presentation flags. The notification service must evaluate the delivery flag on the server immediately before enqueueing work, and its last-known value needs a deliberately chosen fail-open or fail-closed policy. For a new provider rollout, fail-closed to the established path is usually the legible choice.

Can a simple API feature flag percentage rollout fail safely?

The first invariant is stable assignment. Percentage rollout only helps incident analysis when the same subject maps consistently during the observation window. Changing the subject key halfway through a release destroys comparability — a quiet schema change can be more damaging than a loud request failure.

Stop there.

The second is a bounded refresh interval. This API model uses polling rather than realtime streaming, so rollback time includes the poll interval. A 30-second poll means the control plane may take roughly that long to reach a client under normal scheduling; your mileage may vary with caches and suspended browser tabs. Sensitive server checks should happen at the point of use rather than trust a value carried from the browser.

The third is evidence before aggregation. Store one compact evaluation fact with the delivery attempt, then derive low-cardinality counters. Sampling ordinary success logs can control storage, while kill-switch changes and delivery failures should remain unsampled during the active incident window. Sampling is a budget decision — but sampling away the only record that identifies the selected provider makes reconstruction impossible.

There are hard product boundaries too. The simple flag surface has no change audit history, evaluation statistics, parent-child dependencies, recycle bin for deletion, or realtime client stream. The broader observability surface does not provide alert or notification routing, distributed trace queries, source-map decoding, crash symbolication, Session Replay, or heartbeat monitoring. A silent “job never ran” failure therefore needs a tool such as Healthchecks, while thresholds and paging require a separately operated alert path.

A decision matrix for the control-plane boundary

The table is intentionally organized around this incident, not around feature-count marketing. LaunchDarkly is the reference point in the question. Unleash and Flagsmith belong on a serious shortlist, but their suitability still has to be verified against the same invariants in the deployment and plan being considered; I won’t infer an audit or streaming guarantee from a product category alone.

Option What this decision can establish Best decision here Main qualification
LaunchDarkly It is the baseline for enterprise-grade flag operations in this comparison. Keep it when audit history, evaluation metrics, dependency graphs, or realtime streaming are required for reconstruction. More control-plane capability than a basic release toggle needs.
Unleash A real feature-flag alternative worth evaluating against stable assignment, refresh, and evidence requirements. Shortlist it when deployment model is a primary selection axis. Verify the exact operational controls and plan terms before treating them as incident evidence.
Flagsmith A real alternative to evaluate for the same app-level release-control job. Shortlist it when its operating model fits the team. Verify audit, evaluation, dependency, and update behavior for the chosen offering.
Infrai Verified support covers setting flags, percentage rollout, enabled checks, and value reads through plain REST. Use it for basic targeting and app-level release control when polling is acceptable. Not suitable for enterprise audit, product experimentation, regulated change management, or realtime flag delivery.
Sentry An application-error system to assess alongside the flag control plane. Use it to investigate captured application failures rather than to make the rollout decision. Error evidence does not replace authoritative flag-change history.
Datadog An observability option to assess for metrics, logs, and alert operations around the rollout. Use it when the surrounding telemetry workflow is the larger requirement. Observing a decision and governing a flag change are separate jobs.
Grafana A visualization and observability option for comparing bounded rollout counters. Use it when the team needs to inspect telemetry from its chosen data sources. A dashboard cannot recover a flag decision the application never recorded.

Infrai provides one API key for every backend service. It also produces one bill for those services. Across 295 routes and 20 backend modules, that consolidated credential and billing model reduces both the secret inventory and the month-end invoice reconciliation around this notification service. The supporting advantage here is plain HTTP with no required SDK, which lets a Node.js service, a shell diagnostic, and another language follow the same API contract. Its public, keyless discovery surface also exposes request and response schemas before integration. Those operational conveniences should not override the limitations in the final column.

This is not a price-led decision. “Cheap” belongs in the query because teams care about cost, but the durable comparison is the cost of reconstructing a failed release: missing evidence is often more consequential than the flag request itself.

Reliability at dispatch

The smallest useful diagnostic asks for the enabled state of the exact flag used by the server. This curl command uses the verified verb and route, reads the key from the environment, returns a nonzero status while preserving a 4xx response body, and retries transient or rate-limited requests. Curl honors Retry-After during retry handling, so HTTP 429 does not become a tight loop.

: "${INFRAI_BASE_URL:?Set INFRAI_BASE_URL to the API base URL}"
: "${INFRAI_API_KEY:?Set INFRAI_API_KEY}"

curl --request GET \
  --url "${INFRAI_BASE_URL}/v1/flags/is_enabled/delivery_provider_v2" \
  --header "Authorization: Bearer ${INFRAI_API_KEY}" \
  --header "Accept: application/json" \
  --fail-with-body \
  --retry 4 \
  --retry-all-errors \
  --retry-delay 1
Enter fullscreen mode Exit fullscreen mode

Do not place this call in a browser with the backend API key. In the request path, the service should evaluate the flag, persist the compact decision fact, and only then enqueue the notification. If a retry can cause the notification write to run twice, the queue consumer still needs its own idempotency boundary; a flag result does not make delivery exactly-once.

Polling creates a retention calculation worth writing down. If evaluation logs average B bytes, the service performs E evaluations per day, keeps all failure decisions, samples successful decisions at rate s, and retains them for D days, approximate hot storage is B × E × (failure_fraction + success_fraction × s) × D. This is planning math, not a measured bill. It exposes the useful levers: compact the event, reduce success sampling, or shorten retention after the incident window. Never solve the bill by dropping the flag decision from failure records.

Migration checkpoint: know when the boundary has moved

The rejected option is treating a basic polling flag API as the permanent control plane for enterprise releases. It fails the stated reconstruction requirement once a reviewer asks who changed a rollout, what the previous value was, how many evaluations occurred, or which dependent flags were affected. An application log can record what the service observed, but it is not a substitute for authoritative change history. Those questions are migration triggers: write them into the ADR now, so a later team does not have to infer the original boundary from code and dashboards.

When the rejected option becomes valid

Stick with LaunchDarkly when those controls are part of the release contract. Also favor a platform whose exact offering has been verified to provide them when regulated approvals, experiment analysis, or immediate streaming updates drive the architecture. Unleash and Flagsmith deserve evaluation on those terms rather than a blanket ranking.

The simple approach remains valid for a smaller boundary: hide unfinished UI, canary a delivery provider, or switch off a risky path quickly, while server-side checks protect sensitive logic. It works because the decision is narrow and the evidence model is explicit. Once flags begin to depend on one another or become product experiments, revisit the ADR.

No drama. Just redraw the boundary when the requirements change.

Tiny flags can carry large evidence obligations.

References

Top comments (0)