SwimdraftPricingSign inStart free

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.

Software release: BPMN 2.0 diagram
Software release process modelled as a BPMN 2.0 diagram

Standard BPMN 2.0 interchange XML: opens in Camunda Modeler, Signavio, Bizagi, and any other compliant tool.

The process at a glance

Starts when
Code freeze declared
Ends with
Release completed · Release rolled back
Lanes
Development · QA · Release manager · Operations
Decisions
Tests passed? · Deployment healthy?

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

ElementCountIn this diagram
Start event1Code freeze declared
End event2Release completed, Release rolled back
Task4Run regression tests, Fix reported defects, Publish release notes, Roll back deployment
User task1Approve release
Service task1Deploy to production
Script task1Build release candidate
Exclusive gateway2Tests passed?, Deployment healthy?
Parallel gateway2
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

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 free

No credit card required