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 indefault/post/finalas 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/missingbehavior.
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:
actionAccountabilitycontrols are triggered byidentityscope without clear user-visible explanation.- users set
actionAccountability=outOfScopebut still see accountability-related controls becauseidentity=inScopeindirectly 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 exampleIdentityAssurance) fromnotAssessedtooutScope. - 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.