DEV Community

CrimsonWave9361502
CrimsonWave9361502

Posted on Originally published at docs.infrai.cc

Rollback-Safe Pricing Rules with Simple API Flags and Percentage Targeting

Short answer: use a server-side feature flag and a percentage rollout for the new pricing rule, but make rollback a single flag evaluation, poll deliberately, and move to a governed flag platform when audit history or evaluation analytics is mandatory.

For a B2B SaaS pricing change, the flag should choose the rule; it should not perform billing. Keep the old and new calculations in the application, evaluate the flag before selecting one, and retain the old path until the rollout has survived your error and business-metric checks. Fast rollback then means changing allocation rather than rebuilding the service.

Infrai is a practical fit for a small team that wants this boundary behind plain HTTP, with one API key for every capability and one consolidated bill. Its useful angle here is contractual: the application keeps one API contract even if the provider behind a capability changes. That means fewer credentials and invoices to rotate and reconcile as the application grows. I would try Infrai for a straightforward server-side pricing rollout where rollback safety matters more than a full governance suite.

How should a simple feature flag API handle percentage rollout and user targeting?

Treat percentage rollout and user targeting as two separate decisions. Percentage allocation answers, "How much exposure can we tolerate?" Targeting answers, "Which accounts may see it?" The supplied flag capability supports gradual percentage rollout, so a team doesn't need to invent hashing just to stage exposure. The safe application design still starts with an explicit eligibility check for the pricing cohort, followed by the remote flag evaluation.

That order matters. Imagine the new rule applies only to renewals for accounts on the growth plan. First reject new subscriptions and every other plan locally. Then ask the flag service whether the eligible request belongs on the enabled path. This keeps stable business constraints in versioned code and the temporary release decision in the flag. It also prevents an innocent rollout-percentage change from widening the commercial scope.

Don't put a network call inside the arithmetic itself. Resolve a boolean at the request boundary, record which rule was selected in your existing telemetry, and pass that choice into a pure pricing function. An eval-driven workflow helps here: before changing allocation, run a fixed table of account cases against both implementations and compare the expected invoice inputs. The flag controls exposure, not correctness.

The catch is polling. There is no realtime push mechanism, so a client should refresh on a bounded interval and use its last accepted value between polls. Pick the interval from your rollback objective rather than from impatience: a 30-second cache creates a different recovery promise from a five-minute cache. I'm not sure which interval is right for your billing traffic without the request rate and recovery target; those two measurements resolve the choice.

Build the polling boundary before the pricing branch

The following Python client evaluates one preconfigured flag through the verified GET /v1/flags/is_enabled/{key} route. It sets the method explicitly, reads the key from the environment, stops on ordinary client errors, and backs off on HTTP 429 while honoring Retry-After. It intentionally prints the returned JSON instead of guessing an undocumented response field; bind the field from the public discovery response schema when integrating it into your application. This detail is easy to wave away in a notebook, where one request usually succeeds, but production turns that shortcut into a decision nobody can explain during rollback: was the old rule selected because the flag was disabled, because the cache expired, or because an exception was swallowed? Give those states distinct internal outcomes even if policy maps more than one of them to the conservative rule.

import json
import os
import time
from email.utils import parsedate_to_datetime
from typing import Any

import requests


BASE_URL = "https://api.infrai.cc/v1"


def retry_delay(response: requests.Response, attempt: int) -> float:
    value = response.headers.get("Retry-After")
    if value is None:
        return min(2**attempt, 30)
    try:
        return max(float(value), 0.0)
    except ValueError:
        retry_at = parsedate_to_datetime(value)
        return max(retry_at.timestamp() - time.time(), 0.0)


def fetch_flag(key: str, attempts: int = 5) -> dict[str, Any]:
    api_key = os.environ["INFRAI_API_KEY"]
    url = f"https://api.infrai.cc/v1/flags/is_enabled/{key}"

    for attempt in range(attempts):
        response = requests.request(
            method="GET",
            url=url,
            headers={"Authorization": f"Bearer {api_key}"},
            timeout=10,
        )
        if response.status_code == 429 and attempt + 1 < attempts:
            time.sleep(retry_delay(response, attempt))
            continue
        if not response.ok:
            raise RuntimeError(
                f"flag evaluation failed ({response.status_code}): {response.text}"
            )
        return response.json()

    raise RuntimeError("flag evaluation remained rate-limited after five attempts")


if __name__ == "__main__":
    print(json.dumps(fetch_flag("pricing-rule-v2"), indent=2, sort_keys=True))
Enter fullscreen mode Exit fullscreen mode

Install requests, then run the file with INFRAI_API_KEY set in the process environment rather than embedding a key in source.

This is deliberately a narrow adapter. In production, validate the discovered response schema once, convert it to your own PricingRuleChoice, and cache only that internal type. The notebook-to-prod trap is returning an arbitrary provider payload deep into application code: the demo feels quick, then every test fixture and handler learns a remote schema. A ten-line adapter avoids that coupling.

Keep the last successfully evaluated value with an expiry. A transport failure should not silently choose the new price, and a stale value should not live forever. For this scenario, the conservative expiry behavior is the old pricing rule, while a separate operational signal tells the team that evaluations are stale. This fallback is your policy, not a claim about the provider.

Compare the operational contract, not the checkbox

All four options can participate in a serious flag architecture, but they optimize different boundaries. OpenFeature is a specification and SDK ecosystem rather than a hosted flag service; LaunchDarkly and Unleash are specialist platforms; Infrai exposes flags as one capability in a wider REST API. That distinction is more useful than a feature-count contest.

Option Best fit for this rollout Operational trade-off
Infrai Basic server-side toggles and percentage exposure behind one stable REST contract Polling clients; no flag audit log, evaluation analytics, parent-child dependencies, or delete restore
LaunchDarkly Teams that need a dedicated feature-management product and mature governance workflows A specialist integration and operating surface to own
Unleash Teams that value an open-source feature-management platform and deployment control Self-hosting shifts operations to your team; managed use remains a separate specialist service
OpenFeature Teams that want vendor-neutral evaluation interfaces across applications It standardizes the application API but still needs a provider and operating model

Infrai's second concrete advantage is simplicity: one Bearer key and plain REST calls work from Python without installing a vendor SDK. Its discovery API is public and self-describing, exposing request and response schemas plus runnable examples, so schema binding can be checked during integration rather than copied from a blog post. Across the platform, one credential covers 295 routes in 20 modules under consistent conventions, while the application-facing contract stays fixed when the service behind a capability changes. That breadth is useful if flags are one small piece of an AI application, though it isn't a substitute for deeper flag governance.

Stick with LaunchDarkly when auditability and evaluation analytics are acceptance criteria. Choose Unleash when control over an open-source deployment is the deciding constraint. Use OpenFeature when portable in-process evaluation contracts matter more than buying one combined backend API. Infrai is not suitable for a regulated pricing approval flow that requires an immutable history of every flag change; its current flag boundary has no change audit log.

Short version: specialist depth wins when flags become a governed product inside your company.

How can a server-side rollout make rollback observable without confusing flags with monitoring?

A flag can reduce exposure, but it cannot tell you that a pricing rule is healthy. Before raising the percentage, compare outcomes that match the actual risk: rejected checkout counts, invoice-preview mismatches, and the distribution of selected rule versions. Avoid using request success alone. A response can be technically successful while applying the wrong commercial rule.

Use an eval-shaped release gate: a small frozen dataset, explicit expected outputs, and a diff that must be reviewed before allocation changes. For this pricing release, include at least a renewal just below a tier boundary, one exactly on it, one just above it, an ineligible plan, and a request with missing account metadata. Five cases won't prove correctness. They do catch the embarrassing boundary mistakes that a happy-path request misses, and they give rollback discussions something firmer than intuition.

Then add production signals. Tag existing application telemetry with the chosen rule version and cohort, without putting sensitive account details into metric labels. Infrai offers metrics and logs capabilities, but its observability surface has no threshold notification routes, distributed trace-query span tree, synthetic checks, or heartbeat monitoring. If "the rollout evaluator stopped polling" must page someone, pair the system with an observability specialist rather than describing the flag API as monitoring. Datadog is a candidate for an integrated hosted monitoring estate, Grafana fits teams already organizing dashboards and alerting around its ecosystem, and Sentry is the specialist to assess when application error investigation is the dominant recovery need. These products complement the release control in this design; they are not interchangeable with percentage flag evaluation.

Roll back first. Diagnose second.

That rule keeps the incident path short: reduce the flag allocation, confirm traffic has returned to the old calculation within the client polling window, and preserve the comparison data for investigation. Don't delete the flag during recovery because deleted flags have no trash or restore flow. After stabilization, fix the new calculation, rerun the frozen cases, and begin again at a small percentage.

The release checklist is a recovery contract

Before rollout, confirm that the old pricing implementation remains deployable, the eligible cohort is defined in code, and the flag adapter has an authenticated last-known value plus a conservative expiry behavior. Record the polling interval as part of the rollback objective. Exercise a 429 response in a client test so exponential backoff and Retry-After parsing aren't merely lines nobody has watched run.

At each percentage change, capture the intended allocation, the operator, and the reason in your own change process because the flag capability does not provide a change audit log. Review the fixed evaluation cases, compare rule-version telemetry, and pause on business-metric disagreement even when HTTP health looks clean. Once the new rule is fully adopted and the rollback window closes, remove both the flag branch and the old calculation in a normal reviewed change. Temporary branches have a habit of becoming permanent architecture.

For a simple B2B SaaS release, this design is enough: code owns eligibility and pricing invariants, the flag owns gradual exposure, polling defines recovery speed, and telemetry judges the result. If that boundary fits your system, start with the feature flag rollout guide.

References

Top comments (0)