
The Adapter Flag Problem: How One Boolean Can Break an Enterprise Service Flow
- 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:
Operation-driven notification
A service is called, and the API emits an event because something happened.
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.”

Comments