API versioning vs feature flags for evolving decision management APIs

We’re evolving our decision management API in Mendix 10.3 and debating between traditional API versioning (v1, v2) versus feature flags for gradual rollout. Our decision rules engine serves multiple client applications, and we need to add new decision criteria and modify scoring algorithms without breaking existing clients. API versioning strategies seem safer but create maintenance burden with multiple versions running simultaneously. Feature flag rollout would let us enable new logic selectively, but I’m concerned about client compatibility when different clients see different behavior. What approaches have worked for evolving decision management APIs while maintaining backward compatibility?

For decision management specifically, we implemented a ‘decision version’ parameter in the request payload. Clients explicitly specify which decision logic version they want to use. This is separate from API versioning - the API contract stays v1, but the decision rules can be v1, v2, v3. This gives clients time to validate new decision logic in their systems before switching. We maintain the last 3 decision versions simultaneously.

I’ll provide a comprehensive perspective on API versioning strategies, feature flag rollout, and client compatibility for decision management APIs, drawing from implementations across multiple Mendix platforms.

API Versioning Strategies:

Three viable approaches for decision management APIs:

  1. URL Versioning (/v1/decisions, /v2/decisions):

    • Pros: Clear separation, easy routing, explicit contracts
    • Cons: Maintenance burden, client migration required, duplicate code
    • Best for: Major breaking changes, complete API redesigns
  2. Header Versioning (Accept: application/vnd.company.v1+json):

    • Pros: Same URL, version in headers, clean URLs
    • Cons: Less visible, harder to test in browsers, requires header inspection
    • Best for: Multiple concurrent versions with same endpoints
  3. Content Versioning (decision-version parameter in payload):

    • Pros: Granular control, API contract stable, decision logic evolves independently
    • Cons: Complexity in routing logic, requires careful documentation
    • Best for: Decision management where business rules evolve frequently

Feature Flag Rollout for Decision APIs:

Implement a structured feature flag framework:

  1. Flag Types:

    • Release flags: Enable new features (short-lived, removed after rollout)
    • Experiment flags: A/B testing different decision algorithms (time-boxed)
    • Ops flags: Circuit breakers for problematic features (permanent)
    • Permission flags: Role-based feature access (permanent)
  2. Flag Implementation:

    • Store flags in Mendix Constants or external config service
    • Support per-client flag overrides (client X gets feature Y early)
    • Include flag evaluation result in API response for transparency
    • Log flag states with each request for debugging
  3. Flag Lifecycle:

    • Creation: New feature starts disabled, enabled for internal testing
    • Rollout: Gradual enablement (10% clients, then 50%, then 100%)
    • Default: After stabilization, becomes default behavior
    • Cleanup: Remove flag code after 6 months, one version behind

Client Compatibility Strategies:

Maintaining compatibility while evolving:

  1. Additive Changes Only (within major version):

    • New optional fields in requests (old clients omit them)
    • New fields in responses (old clients ignore them)
    • New endpoints alongside existing ones
    • Never remove fields or change field types
  2. Deprecation Process:

    • Announce deprecation 6 months before removal
    • Include deprecation warnings in API responses
    • Provide migration guide with examples
    • Monitor usage of deprecated features
    • Only remove in new major version
  3. Decision Logic Versioning:

    • Clients specify desired decision version in request
    • Default to latest stable if not specified
    • Maintain N-2 versions (current + 2 previous)
    • Each version is fully independent decision rule set

Recommended Hybrid Approach for Mendix 10.3:

Combine strategies for optimal flexibility:

  1. Use URL versioning for major API contract changes (v1, v2)
  2. Within each major version, use feature flags for new capabilities
  3. Use decision-version parameter for business rule evolution
  4. Implement client-specific flag overrides for gradual rollout

Example request:


POST /v1/decisions/evaluate
X-Feature-Flags: advanced-scoring,ml-predictions
{
  "decisionVersion": "2024.11",
  "applicationId": "APP-123",
  "applicantData": {...},
  "scoringFactors": {...}
}

Response includes active features:


{
  "decision": "approved",
  "score": 750,
  "activeFeatures": ["advanced-scoring", "ml-predictions"],
  "decisionVersion": "2024.11"
}

Critical Success Factors:

  1. Monitoring: Track feature flag adoption rates and API version usage
  2. Testing: Automated tests for each feature flag combination (limit to 3-5 active flags)
  3. Documentation: OpenAPI spec with conditional schemas based on flags
  4. Communication: Proactive client notification about new features and deprecations
  5. Metrics: Measure impact of new decision logic on approval rates, processing time

For decision management APIs specifically, the decision-version parameter approach provides the best balance. It allows rapid iteration on business rules (which change frequently) without forcing API version migrations (which are disruptive). Combine this with feature flags for truly experimental features that you want to test with subset of clients before full rollout.

The key is treating decision logic evolution separately from API contract evolution - they change at different rates and for different reasons.

Exactly right. New input fields are always optional in the API contract. Each decision version knows which fields it requires. If a client sends v2 decision version but omits new required fields, we return a 400 error with details about missing fields. If they send v1 decision version with extra fields, we ignore them. This keeps the API contract stable while allowing decision logic to evolve independently.

The decision version parameter approach is interesting. How do you handle the case where new decision logic requires additional input fields that older versions don’t need? Do you make those fields optional and ignore them in older versions?