Policy authoring anti-patterns

These anti-patterns are common causes of policy drift and inconsistent behavior.

1) Ambiguous rule IDs

Bad:

  • RULE_1

Good:

  • WEB_APPLICATION_SERVICE_CONTINUITY_SCOPE

Why it matters:

  • Traceability and troubleshooting depend on clear IDs.

2) Mixing terminology

Bad:

  • some rules use scope terms, others use legacy concern terms for the same meaning.

Good:

  • use scope vocabulary consistently (inScope, outOfScope, notAssessed).

Why it matters:

  • Mixed language causes user confusion and implementation divergence.

3) Hidden wildcard scope

Bad:

if:
  - eq: { key: serviceContinuity, value: inScope }
then:
  - expect: { entity: component, control: trafficFiltering }

Good:

if:
  - eq: { key: type, value: webApplication }
  - eq: { key: serviceContinuity, value: inScope }
then:
  - expect: { entity: component, control: trafficFiltering }

Why it matters:

  • broad matches cause accidental controls on unrelated entities.

4) No negative-path tests

Bad:

  • only testing when conditions are true.

Good:

  • always test one condition flip and confirm no trigger.

Why it matters:

  • many regressions hide in false-condition behavior.

5) Stage misuse

Bad:

  • scoring runs before derived values are set.

Good:

  • derive in init/pre; score in default/post/final as needed.

Why it matters:

  • stage ordering directly affects determinism.

6) Target/action mismatch by accident

Bad:

target: connection
then:
  - expect: { entity: component, control: hasAuthentication }

(when component effect was not intended)

Good:

  • align target and action entity unless cross-entity behavior is intentional and documented.

Why it matters:

  • accidental cross-entity updates are hard to debug.

7) Copy-paste parity gaps

Bad:

  • update API rule, forget equivalent webApplication rule.

Good:

  • maintain explicit parity checklist for linked entity types.

Why it matters:

  • users see inconsistent outcomes for similar architectures.

8) Silent default/missing behavior

Bad:

  • score map without explicit default/missing behavior.

Good:

  • define deterministic fallback behavior.

Why it matters:

  • unknown values should not create undefined scoring.

9) Overloaded mega-rules

Bad:

  • one rule with many unrelated conditions/actions.

Good:

  • split into focused rules with single intent.

Why it matters:

  • smaller rules are easier to validate and maintain.

10) Unscoped specification text

Bad:

  • specification guidance added for failure states in noisy ways.

Good:

  • keep specification guidance concise and value-specific.

Why it matters:

  • generated guidance should be actionable, not cluttered.

11) Hidden inter-scope dependencies

Bad:

  • actionAccountability controls are triggered by identity scope without clear user-visible explanation.
  • users set actionAccountability=outOfScope but still see accountability-related controls because identity=inScope indirectly activates them.

Example of confusing rule coupling:

- id: IDENTITY_DRIVES_ACCOUNTABILITY
  target: component
  if:
    - eq: { key: identity, value: inScope }
  then:
    - expect: { entity: component, control: executionAuditLogging }

Good:

  • either keep scopes independent, or make dependency explicit in naming, descriptions, and user help text.
  • if dependency is intentional, encode it as a clear dual-scope rule and document why.

Example with explicit dependency signaling:

- id: ACCOUNTABILITY_REQUIRES_IDENTITY_SCOPE
  description: Accountability controls require identity assurances for actor attribution.
  target: component
  if:
    - eq: { key: actionAccountability, value: inScope }
    - eq: { key: identity, value: inScope }
  then:
    - expect: { entity: component, control: executionAuditLogging }

Why it matters:

  • hidden dependencies make controls look arbitrary.
  • users cannot predict outcomes from scope settings.
  • troubleshooting becomes harder because scope intent and control outcomes drift apart.

Authoring guardrails:

  • document scope dependencies next to the rule.
  • prefer explicit dual-condition checks over implicit cross-scope coupling.
  • add a negative-path test where one scope is in and the dependent scope is out.

12) Parent scope mutating child scope values

Bad:

  • a parent scope (for example identityAndAccess) silently rewrites child scopes (for example IdentityAssurance) from notAssessed to outScope.
  • stored config and effective rule behavior diverge, so users cannot trust what they see in the component config.

Bad pattern:

- id: IDENTITY_PARENT_SETS_CHILD_SCOPE
  target: component
  if:
    - eq: { key: identityAndAccess, value: outScope }
  then:
    - setValue: { entity: component, control: IdentityAssurance, value: outScope }

Good:

  • keep scope fields independent and evaluate controls against explicitly set values.
  • if a relationship is needed, express it in scoring/expectation rules, not by mutating another scope field.

Preferred pattern:

- id: IDENTITY_ASSURANCE_EXPECTATION
  target: component
  if:
    - eq: { key: identityAndAccess, value: inScope }
  then:
    - expect: { entity: component, control: hasAuthentication }

Why it matters:

  • avoids ambiguous state between stored config and derived runtime outcomes.
  • makes score/color decisions predictable.
  • prevents hard-to-debug scope cascades in UI and export artifacts.

Quick anti-pattern check before merge

  • ID is descriptive and unique.
  • Scope terms are consistent.
  • Entity type filters are explicit.
  • Negative path is tested.
  • Stage ordering is intentional.
  • Equivalent entity types are checked for parity.
  • Cross-scope dependencies are explicit and documented.