Software release BPMN example
A software release process describes how tested code reaches production and what happens when it should not have. Teams map it because releases sit at the intersection of development, QA, and operations, and the handoffs are exactly where late defects, missing release notes, and improvised rollbacks breed. Mapping also settles the recurring argument about who actually authorises the push to production.
The model uses one pool with four lanes: Development, QA, Release manager, and Operations. It combines three patterns in one diagram: a rework loop from failed regression tests back to the build, a parallel gateway that deploys to production while release notes are published, and a health check gateway guarding rollback. The rework loop is the choice worth noticing, because it encodes "fix and rebuild" as the only legal response to a failed test run.
Standard BPMN 2.0 interchange XML: opens in Camunda Modeler, Signavio, Bizagi, and any other compliant tool.
The process at a glance
How to read this diagram
Everything begins at "Code freeze declared" in the Development lane, after which the release candidate is built and handed to QA for regression tests. The gateway "Tests passed?" routes failures down its default No branch to "Fix reported defects", which loops back to the build task: a candidate cannot skip retesting after a fix. On the Yes branch, the Release manager approves the release.
After approval a parallel gateway splits the flow: Operations deploys to production while the Release manager publishes release notes, and a joining parallel gateway waits for both. The final gateway "Deployment healthy?" ends the happy path at "Release completed" on Yes; its default No branch triggers "Roll back deployment", ending at "Release rolled back". Separate end events keep the two outcomes visible in reporting instead of burying rollbacks inside a generic finish.
BPMN elements used
| Element | Count | In this diagram |
|---|---|---|
| Start event | 1 | Code freeze declared |
| End event | 2 | Release completed, Release rolled back |
| Task | 4 | Run regression tests, Fix reported defects, Publish release notes, Roll back deployment |
| User task | 1 | Approve release |
| Service task | 1 | Deploy to production |
| Script task | 1 | Build release candidate |
| Exclusive gateway | 2 | Tests passed?, Deployment healthy? |
| Parallel gateway | 2 |
View the BPMN 2.0 XML for this diagram
<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="swimdraft" exporterVersion="1.0">
<bpmn2:collaboration id="Collaboration_1">
<bpmn2:participant id="pool_sr" name="Software release" processRef="Process_pool_sr" />
</bpmn2:collaboration>
<bpmn2:process id="Process_pool_sr" isExecutable="false">
<bpmn2:laneSet id="LaneSet_pool_sr">
<bpmn2:lane id="lane_sr_dev" name="Development">
<bpmn2:flowNodeRef>start_sr</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>t_sr_build</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>t_sr_fix</bpmn2:flowNodeRef>
</bpmn2:lane>
<bpmn2:lane id="lane_sr_qa" name="QA">
<bpmn2:flowNodeRef>t_sr_test</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>g_sr_pass</bpmn2:flowNodeRef>
</bpmn2:lane>
<bpmn2:lane id="lane_sr_rm" name="Release manager">
<bpmn2:flowNodeRef>t_sr_appr</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>g_sr_split</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>t_sr_notes</bpmn2:flowNodeRef>
</bpmn2:lane>
<bpmn2:lane id="lane_sr_ops" name="Operations">
<bpmn2:flowNodeRef>t_sr_deploy</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>g_sr_join</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>g_sr_health</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>t_sr_roll</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>end_sr_done</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>end_sr_roll</bpmn2:flowNodeRef>
</bpmn2:lane>
</bpmn2:laneSet>
<bpmn2:startEvent id="start_sr" name="Code freeze declared">
<bpmn2:outgoing>fsr1</bpmn2:outgoing>
</bpmn2:startEvent>
<bpmn2:scriptTask id="t_sr_build" name="Build release candidate">
<bpmn2:incoming>fsr1</bpmn2:incoming>
<bpmn2:incoming>fsr6</bpmn2:incoming>
<bpmn2:outgoing>fsr2</bpmn2:outgoing>
</bpmn2:scriptTask>
<bpmn2:task id="t_sr_test" name="Run regression tests">
<bpmn2:incoming>fsr2</bpmn2:incoming>
<bpmn2:outgoing>fsr3</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:exclusiveGateway id="g_sr_pass" name="Tests passed?" default="fsr5">
<bpmn2:incoming>fsr3</bpmn2:incoming>
<bpmn2:outgoing>fsr4</bpmn2:outgoing>
<bpmn2:outgoing>fsr5</bpmn2:outgoing>
</bpmn2:exclusiveGateway>
<bpmn2:task id="t_sr_fix" name="Fix reported defects">
<bpmn2:incoming>fsr5</bpmn2:incoming>
<bpmn2:outgoing>fsr6</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:userTask id="t_sr_appr" name="Approve release">
<bpmn2:incoming>fsr4</bpmn2:incoming>
<bpmn2:outgoing>fsr7</bpmn2:outgoing>
</bpmn2:userTask>
<bpmn2:parallelGateway id="g_sr_split">
<bpmn2:incoming>fsr7</bpmn2:incoming>
<bpmn2:outgoing>fsr8</bpmn2:outgoing>
<bpmn2:outgoing>fsr9</bpmn2:outgoing>
</bpmn2:parallelGateway>
<bpmn2:serviceTask id="t_sr_deploy" name="Deploy to production">
<bpmn2:incoming>fsr8</bpmn2:incoming>
<bpmn2:outgoing>fsr10</bpmn2:outgoing>
</bpmn2:serviceTask>
<bpmn2:task id="t_sr_notes" name="Publish release notes">
<bpmn2:incoming>fsr9</bpmn2:incoming>
<bpmn2:outgoing>fsr11</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:parallelGateway id="g_sr_join">
<bpmn2:incoming>fsr10</bpmn2:incoming>
<bpmn2:incoming>fsr11</bpmn2:incoming>
<bpmn2:outgoing>fsr12</bpmn2:outgoing>
</bpmn2:parallelGateway>
<bpmn2:exclusiveGateway id="g_sr_health" name="Deployment healthy?" default="fsr14">
<bpmn2:incoming>fsr12</bpmn2:incoming>
<bpmn2:outgoing>fsr13</bpmn2:outgoing>
<bpmn2:outgoing>fsr14</bpmn2:outgoing>
</bpmn2:exclusiveGateway>
<bpmn2:task id="t_sr_roll" name="Roll back deployment">
<bpmn2:incoming>fsr14</bpmn2:incoming>
<bpmn2:outgoing>fsr15</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:endEvent id="end_sr_done" name="Release completed">
<bpmn2:incoming>fsr13</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:endEvent id="end_sr_roll" name="Release rolled back">
<bpmn2:incoming>fsr15</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:sequenceFlow id="fsr1" sourceRef="start_sr" targetRef="t_sr_build" />
<bpmn2:sequenceFlow id="fsr2" sourceRef="t_sr_build" targetRef="t_sr_test" />
<bpmn2:sequenceFlow id="fsr3" sourceRef="t_sr_test" targetRef="g_sr_pass" />
<bpmn2:sequenceFlow id="fsr4" name="Yes" sourceRef="g_sr_pass" targetRef="t_sr_appr">
<bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">Yes</bpmn2:conditionExpression>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="fsr5" name="No" sourceRef="g_sr_pass" targetRef="t_sr_fix" />
<bpmn2:sequenceFlow id="fsr6" sourceRef="t_sr_fix" targetRef="t_sr_build" />
<bpmn2:sequenceFlow id="fsr7" sourceRef="t_sr_appr" targetRef="g_sr_split" />
<bpmn2:sequenceFlow id="fsr8" sourceRef="g_sr_split" targetRef="t_sr_deploy" />
<bpmn2:sequenceFlow id="fsr9" sourceRef="g_sr_split" targetRef="t_sr_notes" />
<bpmn2:sequenceFlow id="fsr10" sourceRef="t_sr_deploy" targetRef="g_sr_join" />
<bpmn2:sequenceFlow id="fsr11" sourceRef="t_sr_notes" targetRef="g_sr_join" />
<bpmn2:sequenceFlow id="fsr12" sourceRef="g_sr_join" targetRef="g_sr_health" />
<bpmn2:sequenceFlow id="fsr13" name="Yes" sourceRef="g_sr_health" targetRef="end_sr_done">
<bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">Yes</bpmn2:conditionExpression>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="fsr14" name="No" sourceRef="g_sr_health" targetRef="t_sr_roll" />
<bpmn2:sequenceFlow id="fsr15" sourceRef="t_sr_roll" targetRef="end_sr_roll" />
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Collaboration_1">
<bpmndi:BPMNShape id="pool_sr_di" bpmnElement="pool_sr" isHorizontal="true">
<dc:Bounds x="20" y="20" width="1720" height="630" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="lane_sr_dev_di" bpmnElement="lane_sr_dev" isHorizontal="true">
<dc:Bounds x="50" y="20" width="1690" height="130" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="lane_sr_qa_di" bpmnElement="lane_sr_qa" isHorizontal="true">
<dc:Bounds x="50" y="150" width="1690" height="130" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="lane_sr_rm_di" bpmnElement="lane_sr_rm" isHorizontal="true">
<dc:Bounds x="50" y="280" width="1690" height="130" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="lane_sr_ops_di" bpmnElement="lane_sr_ops" isHorizontal="true">
<dc:Bounds x="50" y="410" width="1690" height="240" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="start_sr_di" bpmnElement="start_sr">
<dc:Bounds x="112" y="59" width="36" height="36" />
<bpmndi:BPMNLabel><dc:Bounds x="76" y="101" width="108" height="14" /></bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="t_sr_build_di" bpmnElement="t_sr_build">
<dc:Bounds x="230" y="37" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="t_sr_test_di" bpmnElement="t_sr_test">
<dc:Bounds x="380" y="167" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="g_sr_pass_di" bpmnElement="g_sr_pass">
<dc:Bounds x="555" y="182" width="50" height="50" />
<bpmndi:BPMNLabel><dc:Bounds x="543" y="162" width="74" height="14" /></bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="t_sr_fix_di" bpmnElement="t_sr_fix">
<dc:Bounds x="680" y="37" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="t_sr_appr_di" bpmnElement="t_sr_appr">
<dc:Bounds x="680" y="297" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="g_sr_split_di" bpmnElement="g_sr_split">
<dc:Bounds x="855" y="312" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="t_sr_deploy_di" bpmnElement="t_sr_deploy">
<dc:Bounds x="980" y="427" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="t_sr_notes_di" bpmnElement="t_sr_notes">
<dc:Bounds x="980" y="297" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="g_sr_join_di" bpmnElement="g_sr_join">
<dc:Bounds x="1155" y="442" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="g_sr_health_di" bpmnElement="g_sr_health">
<dc:Bounds x="1305" y="442" width="50" height="50" />
<bpmndi:BPMNLabel><dc:Bounds x="1277" y="422" width="106" height="14" /></bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="t_sr_roll_di" bpmnElement="t_sr_roll">
<dc:Bounds x="1430" y="427" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="end_sr_done_di" bpmnElement="end_sr_done">
<dc:Bounds x="1462" y="559" width="36" height="36" />
<bpmndi:BPMNLabel><dc:Bounds x="1432" y="601" width="97" height="14" /></bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="end_sr_roll_di" bpmnElement="end_sr_roll">
<dc:Bounds x="1612" y="449" width="36" height="36" />
<bpmndi:BPMNLabel><dc:Bounds x="1581" y="491" width="99" height="14" /></bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="fsr1_di" bpmnElement="fsr1">
<di:waypoint x="148" y="77" />
<di:waypoint x="230" y="77" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fsr2_di" bpmnElement="fsr2">
<di:waypoint x="330" y="77" />
<di:waypoint x="360" y="77" />
<di:waypoint x="360" y="207" />
<di:waypoint x="380" y="207" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fsr3_di" bpmnElement="fsr3">
<di:waypoint x="480" y="207" />
<di:waypoint x="555" y="207" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fsr4_di" bpmnElement="fsr4">
<di:waypoint x="605" y="207" />
<di:waypoint x="660" y="207" />
<di:waypoint x="660" y="337" />
<di:waypoint x="680" y="337" />
<bpmndi:BPMNLabel><dc:Bounds x="611" y="187" width="21" height="14" /></bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fsr5_di" bpmnElement="fsr5">
<di:waypoint x="605" y="207" />
<di:waypoint x="660" y="207" />
<di:waypoint x="660" y="77" />
<di:waypoint x="680" y="77" />
<bpmndi:BPMNLabel><dc:Bounds x="611" y="213" width="17" height="14" /></bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fsr6_di" bpmnElement="fsr6">
<di:waypoint x="780" y="77" />
<di:waypoint x="810" y="77" />
<di:waypoint x="810" y="132" />
<di:waypoint x="210" y="132" />
<di:waypoint x="210" y="77" />
<di:waypoint x="230" y="77" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fsr7_di" bpmnElement="fsr7">
<di:waypoint x="780" y="337" />
<di:waypoint x="855" y="337" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fsr8_di" bpmnElement="fsr8">
<di:waypoint x="905" y="337" />
<di:waypoint x="960" y="337" />
<di:waypoint x="960" y="467" />
<di:waypoint x="980" y="467" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fsr9_di" bpmnElement="fsr9">
<di:waypoint x="905" y="337" />
<di:waypoint x="980" y="337" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fsr10_di" bpmnElement="fsr10">
<di:waypoint x="1080" y="467" />
<di:waypoint x="1155" y="467" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fsr11_di" bpmnElement="fsr11">
<di:waypoint x="1080" y="337" />
<di:waypoint x="1110" y="337" />
<di:waypoint x="1110" y="467" />
<di:waypoint x="1155" y="467" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fsr12_di" bpmnElement="fsr12">
<di:waypoint x="1205" y="467" />
<di:waypoint x="1305" y="467" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fsr13_di" bpmnElement="fsr13">
<di:waypoint x="1355" y="467" />
<di:waypoint x="1410" y="467" />
<di:waypoint x="1410" y="577" />
<di:waypoint x="1462" y="577" />
<bpmndi:BPMNLabel><dc:Bounds x="1361" y="447" width="21" height="14" /></bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fsr14_di" bpmnElement="fsr14">
<di:waypoint x="1355" y="467" />
<di:waypoint x="1430" y="467" />
<bpmndi:BPMNLabel><dc:Bounds x="1361" y="473" width="17" height="14" /></bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="fsr15_di" bpmnElement="fsr15">
<di:waypoint x="1530" y="467" />
<di:waypoint x="1612" y="467" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>Frequently asked questions
Why does the defect fix loop back to the build task and not to testing?
Fixed code must be rebuilt before it can be retested, otherwise QA would be testing a stale artefact. Routing the loop to "Build release candidate" enforces that ordering in the model itself. If your pipeline rebuilds automatically on commit, you could route to the test task instead and note the assumption.
Why deploy and publish release notes in parallel rather than in sequence?
Neither activity needs the other to finish first, and the joining gateway still forces both to complete before the health check. Modelling them in sequence would silently add waiting time and imply a dependency that does not exist. Parallel gateways are the standard BPMN way to state "both, in any order".
How would I model a staged rollout instead of a single deployment?
Replace "Deploy to production" with a short chain: deploy to a canary group, then an exclusive gateway asking "Canary healthy?" with the Yes branch continuing to full deployment and the default No branch joining the existing rollback task. The rest of the model, including the parallel release notes branch, stays unchanged.
Related BPMN examples
Incident management
An ITIL-style incident management process as a BPMN 2.0 diagram: triage, major incident handling, escalation, and review. Download the .bpmn file free.
Change management
An IT change management process as a BPMN 2.0 swimlane diagram: risk assessment, CAB approval, implementation, and rollback. Free .bpmn download included.
User access request
A user access request process as a BPMN 2.0 diagram: manager approval, parallel provisioning, and an access register update. Download the .bpmn file free.
Password reset
A password reset process as a BPMN 2.0 diagram: MFA self-service, manual verification fallback, and a message wait state. Free .bpmn file download included.
Generate your own version of this diagram
Describe how the process works in your organisation: plain English, meeting notes, a transcript, or a spreadsheet. Swimdraft turns it into a spec-validated BPMN 2.0 diagram you can edit in the browser and export as .bpmn, SVG, or PNG.
Start freeNo credit card required