10.03 Common Workflow Patterns
Workflows recur across programs in recognizable patterns — escalation on overdue review, notification on critical events, multi-stage approval, fan-out to multiple integrations, conditional routing by team or risk level. Use these patterns as templates; adapt to your program's specifics.
Requires: Workflows Extra
The patterns described here are configurations of workflows defined in the Workflows Extra at
simplerisk/extras/workflows/. See What the Workflows Extra Does and Creating a Workflow for the underlying mechanics.
Why this matters
After building a few workflows, programs notice that the same shapes recur. A "send a notification when X happens" workflow looks almost identical whether X is a critical risk, an audit completion, or a document expiration. A "wait N days, then escalate if not resolved" workflow is the same shape regardless of what's being escalated. Recognizing these patterns means new workflows are quick to build (start from the pattern; adapt to specifics) rather than designed from scratch each time.
This article catalogs the patterns that show up most often. Use them as starting points; the specifics will differ from program to program.
Pattern 1: Notification on event
The simplest pattern: when X happens, notify someone.
Use cases:
- Slack notification when a Critical risk is filed.
- Email notification when an audit cycle starts.
- Teams notification when a document is approved.
Structure:
Trigger (event) → [optional condition] → Send Slack/Email/Teams → End
Example: see Creating a Workflow for the full walk-through of "Critical risk → Slack + email security lead."
Key design decisions:
- Which event(s) trigger the notification.
- What conditions filter (only Critical, only specific team, only first-time-Critical, etc.).
- Who receives the notification (a specific person, a Slack channel, a dynamic recipient list based on the event's data).
- What the notification's content looks like (with variable substitution from the event's context).
Pitfall: notifications without filters produce volume that recipients learn to ignore. Filter aggressively.
Pattern 2: Escalation on time-based threshold
When something has been in a state too long, escalate.
Use cases:
- Risk hasn't been reviewed in 30 days past its review SLA.
- Critical risk hasn't been mitigated in 60 days.
- Document is 14 days from expiration.
- Audit hasn't completed in 90 days.
Structure:
Trigger (event creating the watched record) →
Wait (N days) →
Branch (still in the not-good state?) →
true: Send escalation notification → End
false: End (resolved before timeout)
Example: when a Critical risk is created, wait 30 days; if still open and not yet reviewed, email the executive sponsor.
Key design decisions:
- The wait duration (24 hours? 7 days? 30 days?).
- The condition checked at the wait's end (still open? still in this state? assigned to anyone yet?).
- Who gets escalated to (the next-level manager, the executive sponsor, the program lead).
- Whether to escalate once or repeatedly (a single escalation versus a recurring reminder).
Pitfall: escalations that fire indefinitely without resolution become noise. End the chain with either a resolution check or a maximum escalation count.
Pattern 3: Multi-stage notification
When an event happens, notify multiple parties at the same time.
Use cases:
- Critical risk submitted: notify the owner (immediately), the team lead (immediately), the security operations team (immediately), and the executive sponsor (immediately).
- Document approved: notify the document owner, the approver, the affected team(s).
Structure:
Trigger → Send Email (recipient 1) → Send Email (recipient 2) → Send Slack (channel) → End
(Sequential nodes; or use parallel branches if your version supports them.)
Key design decisions:
- Channel per recipient: some get email, some get Slack, some get Teams.
- Order: typically arbitrary if all are concurrent; matters if a later notification depends on an earlier one's output.
- Personalization: use variable substitution to tailor each notification's content.
Pitfall: too many concurrent notifications produce alert fatigue. Differentiate immediate (action required) from informational (FYI).
Pattern 4: Conditional routing by attribute
When an event happens, route to different actions based on the event's attributes.
Use cases:
- Risk category determines which team's Slack channel gets notified.
- Risk severity determines whether to email the manager or the VP.
- Affected business unit determines which executive sponsor receives the notification.
Structure:
Trigger →
Branch (risk.category == X) →
true → action 1 → End
false → Branch (risk.category == Y) →
true → action 2 → End
false → action 3 (default) → End
(A series of branches forming a decision tree.)
Example: a risk-creation workflow that routes to the relevant team's Slack channel based on the risk's category — "Application Security" risks go to #appsec, "Vendor Risk" to #vrm, etc.
Key design decisions:
- The attributes that drive routing (category, team, severity, source, etc.).
- The default path for unmatched cases.
- Whether the routing reflects current org chart or a frozen mapping (org changes invalidate frozen mappings).
Pitfall: deeply-nested branches become unmaintainable. If the decision tree is more than 3-4 levels deep, consider splitting into multiple workflows or pre-computing the routing target as a custom field.
Pattern 5: Fan-out to multiple integrations
When an event happens, dispatch to multiple downstream systems.
Use cases:
- New Critical risk: post to Slack, file a Jira ticket, log to SIEM, notify ServiceNow.
- Audit completion: notify Slack, update the BI dashboard via webhook, file a record in the document management system.
Structure:
Trigger → Send Slack → Send Webhook (Jira) → Send Webhook (SIEM) → Send Webhook (ServiceNow) → End
Key design decisions:
- What downstream systems care about this event.
- The order (probably arbitrary unless there's a dependency).
- Failure handling (does a Jira failure block the SIEM call?).
Pitfall: a chain where each step blocks the next means slow downstream systems delay everything. Most workflow executors run nodes sequentially; high-latency targets pile up. Consider using async webhook patterns (the workflow fires; downstream systems handle their own queuing).
Pattern 6: Approval gate
A change requires explicit approval from a specific party before proceeding.
Use cases:
- Risk closure requires CISO approval.
- Document publication requires legal review.
- Mitigation acceptance requires risk owner sign-off.
Structure:
Trigger (record entering the awaiting-approval state) →
Send Email (to approver, with approve/reject link) →
Wait (for approval event) →
Branch (approved?) →
true → action (proceed with change) → End
false → action (reject and revert) → End
Key design decisions:
- How the approver indicates approval (clicking a link, updating a field, replying to an email — varies by infrastructure).
- The timeout (how long to wait before assuming non-response).
- The fallback (auto-reject, auto-approve, escalate, hold indefinitely).
Pitfall: the wait-for-approval pattern requires the approval event to be one the workflow can detect. If the approver indicates approval via something the workflow doesn't see, the workflow waits forever.
Pattern 7: Periodic reminder until resolved
Recurring reminder for an open item.
Use cases:
- Weekly reminder for unmitigated Critical risks.
- Monthly reminder for pending risk reviews.
Structure:
Trigger →
Wait (1 week) →
Branch (still open?) →
true → Send reminder email → loop back to Wait
false → End
(The loop-back is the key element; some Extras represent this as recursive workflow execution rather than literal looping.)
Alternative: rather than a single workflow with a loop, build the reminder as a recurring trigger (e.g., a daily cron-triggered workflow that scans for open Critical risks and reminds).
Key design decisions:
- The cadence (daily, weekly, monthly).
- The exit condition (resolved, closed, accepted, etc.).
- The reminder fatigue threshold (after N reminders, stop reminding and escalate instead).
Pitfall: reminders that fire forever produce ignored emails. Cap the loop or escalate after N iterations.
Pattern 8: Pre/post-condition workflow
A workflow that fires before and after an event, with logic that depends on the change.
Use cases:
- Risk score change: notify if the change crosses a threshold.
- Status transition: send different notifications depending on which transition occurred.
Structure:
Trigger (record updated) →
Branch (compare new vs old; threshold crossed?) →
true → Send alert → End
false → End
Key design decisions:
- Whether the trigger event provides both old and new values (depends on the trigger type).
- The threshold logic.
Pitfall: not all triggers provide before-state. Verify the event's context includes what you need.
Pattern 9: Cross-Extra coordination
A workflow in SimpleRisk that coordinates with another Extra.
Use cases:
- Risk creation triggers a Jira ticket via the Jira Extra (SimpleRisk-side may not be needed if the Jira Extra natively handles this; but variant routing might).
- Document approval triggers AI summarization via the AI Extra.
- Compliance test failure triggers an audit-trail update.
Structure: depends on the coordination point. Often a webhook from the workflow into the other Extra's surface.
Key design decisions:
- Whether the coordination is genuinely better as a workflow versus a native cross-Extra integration.
- Loop avoidance (an Extra-triggered event triggering a workflow that triggers the same Extra again).
Pitfall: workflows that coordinate Extras add coupling between Extras. If you remove or upgrade one Extra, the workflow breaks.
Pattern 10: Maintenance / cleanup workflow
A scheduled workflow that runs against the database state, not against an event.
Use cases:
- Weekly: send a digest of unmitigated Critical risks to the program lead.
- Monthly: archive risks closed more than a year ago.
- Quarterly: send a compliance summary to executive stakeholders.
Structure: depends on whether the Extra supports scheduled triggers (a schedule.weekly trigger, for example) or whether you build via cron-driven workflow invocation.
Key design decisions:
- The schedule.
- The query that defines the population to act on.
- The action to take per record.
Pitfall: maintenance workflows that query large populations can be slow. Test against representative data before scheduling in production.
Common pitfalls across patterns
A handful of patterns recur regardless of which pattern you're implementing.
-
Building patterns without testing. Each pattern has subtle edge cases; test in non-production before going live.
-
Over-engineering. A simple "send an email" doesn't need a six-stage workflow. Match the pattern complexity to the actual need.
-
Forgetting to plan for failure. What happens when a Slack webhook URL is wrong? When SMTP is down? When the cron worker is behind? Each pattern should have a failure mode you've thought through.
-
Replicating the same pattern with minor variations across many workflows. If you have ten workflows that are all "notify a specific Slack channel for a specific category," that's a configuration-driven workflow waiting to happen. Consider whether one workflow with a routing branch beats ten near-duplicate workflows.
-
Not maintaining workflows as the program evolves. A workflow that referenced "the Engineering team" three years ago may be wrong now; teams change, organization changes, processes change. Periodic review.
-
Not documenting the patterns in use. Future operators see workflow JSON; without context, "why does this exist?" is unanswerable. Use the workflow descriptions and supplemental documentation.