top of page
Search

The Adapter Flag Problem: How One Boolean Can Break an Enterprise Service Flow

  • Writer: Mark Kendall
    Mark Kendall
  • 1 hour ago
  • 11 min read



The Adapter Flag Problem: How One Boolean Can Break an Enterprise Service Flow



In enterprise integration, adapters are supposed to make life easier.


They sit between systems. They normalize APIs. They translate payloads. They hide downstream complexity. They let one platform talk to another platform without every team having to understand every downstream contract.


That is the theory.


In practice, adapters can become one of the most dangerous parts of the architecture.


Especially when the adapter controls behavior through simple flags like:

emitEvent = true

emitEvent = false

That looks harmless.


It is not harmless.


In a TM Forum-style architecture, where a service order, trouble ticket, product order, resource update, inventory change, or activation request may move across multiple systems, a simple Boolean can become the difference between:


  • downstream work happening or not happening,

  • an event being published or silently suppressed,

  • ServiceNow receiving a notification or never knowing something changed,

  • an orchestration flow continuing or stopping,

  • a customer-impacting action happening twice,

  • a workflow becoming invisible to operations.



That is the adapter flag problem.


The problem is not that flags are bad. The problem is when flags are treated as simple technical switches instead of governed business behavior.



The TM Forum context



TM Forum Open APIs support both direct API operations and event-driven notification patterns.


In the classic TM Forum hub/listener pattern, an API implementation can expose a hub resource so consumers can register to receive events that happen as a result of API operations, such as entity creation, deletion, or state change. TM Forum community guidance describes this hub pattern as entrenched in many Open APIs and notes that it allows consumers to register for events caused by operation invocation.


TM Forum also defines TMF688 Event Management API, which provides a standardized interface for creating, managing, and receiving service-related events. TM Forum describes TMF688 as supporting automation workflows, service outage notifications, SLA violation notifications, trouble-ticket creation, and orchestration scenarios between management systems.


That distinction matters.


There are really two patterns in play:


  1. Operation-driven notification


    A service is called, and the API emits an event because something happened.

  2. General-purpose event management


    An enterprise event layer, such as Kafka, RabbitMQ, or another event broker, carries events across systems.



TM Forum community guidance explicitly recognizes both the older hub-based pattern and TMF688 as a more general event API that can wrap buses such as Kafka or RabbitMQ.


So when a team says, “Should this adapter emit an event or not?” that is not just an implementation detail.


That is an architectural decision.



Why the Boolean is dangerous



A Boolean hides too much meaning.

{

  "emitEvent": true

}

What does that actually mean?


Does it mean:


  • publish a TMF event?

  • publish to Kafka?

  • notify ServiceNow?

  • trigger downstream orchestration?

  • create a trouble ticket?

  • update inventory?

  • continue the service flow?

  • skip duplicate notification?

  • suppress external callbacks?

  • emit only after successful downstream completion?

  • emit before downstream completion?

  • emit both internal and external events?

  • emit only if state changed?

  • emit only for certain order types?

  • emit only for certain product families?

  • emit only when called from UAT or Prod?



That is too much semantic weight for one Boolean.


The Boolean becomes a hidden rules engine. Worse, it becomes a rules engine with no explainability.


A developer sees:

emitEvent = false

But the business consequence might be:

Do not notify ServiceNow that the customer-impacting service state changed.

That is a big difference.



The adapter is not just a pipe



Adapters are often described as plumbing.


That is dangerous language.


A pipe moves water. An adapter in an enterprise workflow makes decisions.


It may decide:


  • whether to transform a TMF payload,

  • whether to call a downstream system,

  • whether to suppress a duplicate event,

  • whether to emit an event,

  • whether to retry,

  • whether to map an error,

  • whether to enrich with inventory data,

  • whether to create a correlation ID,

  • whether to pass a behavioral flag downstream,

  • whether to stop the flow.



That is not plumbing.


That is control-plane behavior.


When adapters control flow, they need governance, testing, observability, and architectural intent.



ServiceNow makes the problem more real



ServiceNow has TM Forum-aligned APIs, including an Event Notification Management Open API implementation based on TMF688. ServiceNow documentation states that its Event Notification Management Open API is based on the TMF688 Event Management API User Guide v4.0.0.


That means this is not theoretical.


In a telecom or enterprise service-management landscape, a TMF-style event may be the thing that tells ServiceNow:


  • a service order changed state,

  • a service outage occurred,

  • a trouble ticket should be created,

  • an SLA condition has been triggered,

  • a downstream system completed work,

  • manual operations needs to intervene.



If the adapter suppresses that event accidentally, operations may lose visibility.


If the adapter emits it twice, operations may create duplicate work.


If the adapter emits it too early, ServiceNow may act on a state that is not actually complete.


If the adapter emits it too late, the customer-impacting workflow may stall.


So the flag is not technical. The flag is operational.



The bad pattern: Boolean-driven behavior



The bad pattern looks like this:

{

  "emitEvent": true,

  "continueFlow": true,

  "notifyExternal": false

}

At first this seems practical.


Then every downstream system gets a slightly different interpretation.


One adapter treats emitEvent=false as “do not publish Kafka.”


Another treats it as “do not notify ServiceNow.”


Another treats it as “do not continue orchestration.”


Another treats it as “skip callback.”


Another ignores it completely.


Eventually nobody knows what the flag means.


That leads to system behavior like this:

Order API called

Adapter receives request

Flag says emitEvent=false

Adapter suppresses event

Downstream service assumes notification already happened

ServiceNow never receives update

Orchestration waits

Team investigates Kafka

Kafka looks fine

Pods look fine

Payload looks fine

The flow is broken because one flag meant the wrong thing

That is the real danger.


The system does not fail loudly. It fails semantically.



The better pattern: decision contracts, not mystery flags



Instead of passing raw Booleans through the system, define explicit decision contracts.


Bad:

{

  "emitEvent": true

}

Better:

{

  "eventPolicy": {

    "mode": "EMIT_AFTER_SUCCESS",

    "scope": "INTERNAL_AND_EXTERNAL",

    "eventType": "ServiceOrderStateChangeEvent",

    "reason": "State changed from acknowledged to inProgress",

    "sourceRule": "SERVICE_ORDER_STATE_TRANSITION_V3"

  }

}

This is more verbose, but it is safer.


Now the system explains itself.


The adapter is no longer guessing what true means. It is executing a declared policy.



Recommended event policy model



A good adapter event policy should answer these questions:



1. Should an event be emitted?


EMIT

SUPPRESS

DEFER

EMIT_ONLY_ON_STATE_CHANGE

EMIT_ONLY_ON_SUCCESS

EMIT_ONLY_ON_FAILURE


2. When should the event be emitted?


BEFORE_DOWNSTREAM_CALL

AFTER_DOWNSTREAM_ACCEPTED

AFTER_DOWNSTREAM_SUCCESS

AFTER_STATE_PERSISTED

AFTER_ORCHESTRATION_COMPLETE


3. Who should receive it?


INTERNAL_ONLY

EXTERNAL_ONLY

SERVICENOW_ONLY

KAFKA_ONLY

TMF_HUB_ONLY

TMF688_EVENT_BUS

INTERNAL_AND_EXTERNAL


4. What business event is being emitted?


ServiceOrderCreateEvent

ServiceOrderStateChangeEvent

TroubleTicketCreateEvent

ResourceStateChangeEvent

ProductOrderStateChangeEvent


5. Why is the event being emitted or suppressed?


State changed

Duplicate detected

No material change

Manual operation requested

Downstream owns notification

Adapter owns notification

Replay mode

Migration mode

UAT suppression


6. Who made the decision?


adapter

orchestrator

rules engine

configuration

feature flag service

manual override

That gives the system a real behavioral contract.



Turn flags into policies



The move is simple but powerful:


From this:

{

  "emitEvent": false

}

To this:

{

  "eventPolicy": {

    "decision": "SUPPRESS",

    "reason": "DOWNSTREAM_SYSTEM_IS_EVENT_OWNER",

    "owner": "ServiceNowAdapter",

    "auditRequired": true

  }

}

Now when something breaks, you can inspect the payload, logs, traces, or audit event and understand why the event did not fire.


That is the difference between a mystery Boolean and an enterprise-safe decision.



Do not let every adapter invent its own rules



This is where enterprise teams get into trouble.


One adapter is built for ServiceNow.


Another adapter is built for inventory.


Another adapter is built for activation.


Another adapter is built for billing.


Another adapter is built for assurance.


Each one gets a local flag.


Each one interprets the flag differently.


That creates adapter drift.


Adapter drift is when each integration point slowly becomes its own rules engine with its own private behavior.


The solution is to create a shared adapter behavior standard.


Every adapter should use the same language for:


  • event emission,

  • event suppression,

  • callback behavior,

  • retry behavior,

  • idempotency behavior,

  • state transition behavior,

  • audit behavior,

  • correlation behavior,

  • failure behavior.



The implementation can differ. The contract should not.



The adapter behavior matrix



Every adapter should be documented in a matrix like this:

Adapter

Downstream System

Operation

Emits Event?

Event Owner

Timing

Suppression Rule

Audit Required

ServiceNow Adapter

ServiceNow

Create Ticket

Yes

Adapter

After accepted

Suppress duplicate external ID

Yes

Activation Adapter

Network Activation

Activate Service

Yes

Orchestrator

After success

Suppress if dry-run

Yes

Inventory Adapter

Inventory

Update Resource

Conditional

Inventory

After persisted

Emit only on material state change

Yes

Billing Adapter

Billing

Start Billing

No

Billing

N/A

Downstream owns event

Yes

This gives teams something better than tribal knowledge.


It lets QA test behavior.


It lets DevOps observe behavior.


It lets architects govern behavior.


It lets production support debug behavior.



Rules engine versus feature flags



Feature flags are useful.


But feature flags are not a full rules engine.


A feature flag is good for controlled enablement:

Enable TMF688 event emission for ServiceNow in UAT.

A rules engine is better for business behavior:

Emit ServiceOrderStateChangeEvent only when state changes from acknowledged to inProgress, downstream call succeeds, and ServiceNow is not already the event owner.

The mistake is using feature flags to carry complex business logic.


That creates flags like:

emitEventForSNButNotForActivationUnlessRetryAndNotUAT

That is not configuration. That is a bug waiting to happen.


Use flags for rollout.


Use rules for behavior.


Use policy contracts for propagation.



The right way to pass behavior downstream



Passing behavior downstream is not wrong. In fact, sometimes it is necessary.


But the downstream service should not receive vague switches.


Bad:

{

  "skipEvent": true

}

Better:

{

  "behaviorContext": {

    "eventOwnership": "DOWNSTREAM_OWNS_EXTERNAL_EVENT",

    "notificationMode": "NO_EXTERNAL_NOTIFICATION_FROM_ADAPTER",

    "auditMode": "RECORD_SUPPRESSION",

    "correlationId": "abc-123",

    "causationId": "order-789",

    "sourcePolicy": "SERVICENOW_EVENT_OWNERSHIP_V2"

  }

}

The goal is not to make payloads bigger for no reason.


The goal is to make behavior inspectable.



Correlation and causation are mandatory



In event-driven systems, correlation IDs are not optional.


Every adapter call and every event should carry:


  • correlationId: ties the whole business flow together.

  • causationId: identifies the specific command, event, or request that caused this action.

  • sourceSystem: who initiated the action.

  • eventOwner: who owns notification responsibility.

  • policyId: which rule made the decision.

  • decision: emitted, suppressed, deferred, failed.

  • reason: why.



Without that, teams end up debugging with guesses.


With that, they can reconstruct the flow.



Idempotency is the safety net



When adapters emit events or call downstream systems, they must be idempotent.


That means the same request can be retried without creating duplicate business effects.


For ServiceNow or similar downstream platforms, this usually means having stable external references:

externalId

correlationId

sourceOrderId

sourceTicketId

idempotencyKey

If the adapter emits an event twice, the receiver should be able to detect that it already processed it.


If the adapter retries a downstream call, it should not create duplicate tickets, duplicate orders, or duplicate service actions.


Without idempotency, event emission flags become terrifying because one wrong retry can multiply downstream impact.



The observability requirement



Every adapter decision should be logged and traced.


Not just errors.


Decisions.


A good adapter log line should say:

{

  "adapter": "ServiceNowAdapter",

  "operation": "CreateTroubleTicket",

  "correlationId": "abc-123",

  "eventDecision": "SUPPRESS",

  "eventType": "TroubleTicketCreateEvent",

  "reason": "DOWNSTREAM_SYSTEM_IS_EVENT_OWNER",

  "policyId": "SN_TICKET_EVENT_POLICY_V2",

  "environment": "UAT"

}

A good dashboard should show:


  • events emitted by adapter,

  • events suppressed by adapter,

  • events deferred,

  • events failed,

  • downstream calls made,

  • downstream calls skipped,

  • policy decisions by type,

  • adapter decisions by environment,

  • event volume by event type,

  • ServiceNow notification volume,

  • replay volume,

  • duplicate suppression count.



This is how you prevent “the flow just stopped” from becoming a two-day investigation.



The testing problem



Most teams test the API call.


They do not test the behavior matrix.


They test:

POST /serviceOrder returns 201

But they do not test:

When order state changes and ServiceNow is event owner, adapter suppresses external event, records audit decision, and does not stop internal orchestration.

That is the gap.


Adapter tests need to include:


  • event emitted,

  • event suppressed,

  • event deferred,

  • event emitted after downstream success,

  • event emitted after persistence,

  • downstream failure,

  • retry,

  • duplicate request,

  • replay,

  • environment-specific behavior,

  • ServiceNow-specific behavior,

  • TMF hub notification behavior,

  • TMF688 event bus behavior,

  • no accidental stop of orchestration.



The Boolean is only safe if every branch is tested.



The promotion checklist



Before moving adapter changes from Dev to UAT or Prod, ask:



Contract



  • Is the event behavior documented?

  • Is the event owner clear?

  • Is the downstream system responsible for notification or is the adapter?

  • Is the policy explicit?

  • Are we using Booleans or named decisions?




TMF alignment



  • Is this a hub/listener notification pattern?

  • Is this TMF688 event management?

  • Is this an internal Kafka event?

  • Is this a ServiceNow event notification?

  • Are we mixing patterns unintentionally?




Runtime behavior



  • Does the adapter emit before or after downstream success?

  • What happens if the downstream call fails?

  • What happens if the event publish fails?

  • What happens if the event succeeds but downstream fails?

  • What happens if downstream succeeds but event publish fails?

  • Is there a replay strategy?




Observability



  • Can we see emitted events?

  • Can we see suppressed events?

  • Can we see why the event was suppressed?

  • Can we trace one order across all adapters?

  • Can operations see where the flow stopped?




Safety



  • Is the operation idempotent?

  • Are duplicate events safe?

  • Are missing events detectable?

  • Is event suppression audited?

  • Are feature flags environment-specific and governed?

  • Can a bad flag be rolled back quickly?




The architecture I would recommend



For a serious enterprise platform, I would move toward this model:

Command / API Request

        |

        v

Adapter receives request

        |

        v

Normalize TMF payload

        |

        v

Evaluate Adapter Policy

        |

        +--> Downstream call policy

        +--> Event emission policy

        +--> Retry policy

        +--> Audit policy

        |

        v

Execute downstream behavior

        |

        v

Persist decision record

        |

        v

Emit, suppress, or defer event with reason

        |

        v

Trace everything with correlation ID

The important part is the policy decision layer.


Do not let event emission be a random if statement buried in adapter code.


Make it a visible decision.



The rule of thumb



Use this rule:


If a flag can change business flow, it is not just a flag. It is a policy.


That means it needs:


  • a name,

  • an owner,

  • documentation,

  • tests,

  • audit,

  • observability,

  • rollout control,

  • rollback control,

  • environment governance.



A Boolean is acceptable only when the consequence is small and local.


But if the Boolean decides whether ServiceNow gets notified, whether a TMF event is emitted, whether orchestration continues, or whether a customer service flow advances, then the Boolean is too small for the job.



Good things about flags



Flags are not evil.


They are useful for:


  • staged rollout,

  • UAT testing,

  • turning on TMF688 eventing gradually,

  • isolating one downstream system,

  • canary release,

  • emergency shutoff,

  • migration from hub/listener to event bus,

  • suppressing duplicate notifications during transition,

  • controlling ServiceNow integration by environment.



Used properly, flags let the team move safely.



Bad things about flags



Flags become dangerous when:


  • nobody owns them,

  • names are vague,

  • behavior differs by adapter,

  • they are passed downstream without context,

  • they are not audited,

  • they are not tested,

  • they are manually changed in production,

  • they control too much,

  • they suppress events silently,

  • they mix rollout control with business rules.



That is how flags become hidden production logic.



The better enterprise standard



The enterprise standard should be:

No silent event suppression.

No unexplained adapter behavior.

No Boolean-only flow control for business-significant events.

No environment-specific adapter behavior without documented policy.

No event ownership ambiguity.

No production flag without audit and rollback.

And the design principle should be:

Adapters translate payloads.

Policies control behavior.

Events describe facts.

Flags control rollout.

Rules decide business conditions.

Observability proves what happened.

That separation keeps the architecture sane.



The intent-driven engineering angle



This is exactly where intent-driven engineering helps.


A vague ticket says:

Add emitEvent flag to ServiceNow adapter.

That is dangerous.


A good intent file says:

Implement governed adapter event behavior for ServiceNow integration.


The adapter must evaluate event emission using an explicit policy model, not a raw Boolean. It must support emit, suppress, and defer decisions. Every decision must include event type, reason, policy ID, event owner, correlation ID, and audit behavior. Event suppression must never stop internal orchestration unless explicitly configured by policy. The implementation must support TMF hub/listener notification behavior and TMF688-style enterprise event management without mixing ownership semantics. Tests must prove emitted, suppressed, deferred, duplicate, retry, and downstream failure scenarios.

That is the difference.


One creates a flag.


The other protects the system.



Final punchline



The adapter problem is not really an adapter problem.


It is an ownership problem.


Who owns the event?


Who owns the notification?


Who owns the downstream action?


Who owns the retry?


Who owns the audit trail?


Who owns the decision to suppress?


Once those answers are clear, the adapter becomes safe.


Until then, one Boolean can take down the flow.

:::


The clean slogan for this one is:


“If a flag can change business flow, it is not a flag. It is a policy.”

 
 
 

Recent Posts

See All

Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating
Post: Blog2_Post

Subscribe Form

Thanks for submitting!

©2020 by LearnTeachMaster DevOps. Proudly created with Wix.com

bottom of page