一个项目管理软件的诞生(六):工作项状态机设计,状态类型、流转步骤与运行治理

0 评论 365 浏览 0 收藏 43 分钟

本文分享项目管理软件中工作项状态机设计实践:将状态从普通字段中分离、自定义状态归入待处理进行中已完成三类类型、定义流转步骤与规则,并通过执行历史保证变更可追溯,为中性项目管理平台设计提供参考。

一个项目管理软件的诞生:工作项状态机设计

此前,我在内部项目管理平台里设计一款 Skill,需要把查询工作项、分配负责人、更新状态等能力开放给 Agent。

查询工作项很好理解,分配负责人也能落到一个明确字段。真正拆到“更新状态”时,事情突然复杂了。

Agent 查到一条处于“待验证”的缺陷,能不能直接把状态改成“已关闭”?当前操作者没有验收权限,Agent 能不能用系统身份替他完成验收?关闭时缺少验证结果,平台应该返回“字段修改失败”,还是明确告诉 Agent:你正在执行“验证通过”这个步骤,但步骤规则尚未满足?

如果把状态当成普通字段,这些问题都很难回答。因为普通字段只关心新值是否合法,状态变化却意味着一次业务决定已经发生:有人完成了某个动作,承担了判断责任,补充了当时产生的信息,系统还可能计算完成时间、通知相关角色,并触发后续自动化。

这也是我重新理解状态机的起点:

状态机不是用来“修改状态”的,它管理的是一次业务决定如何合法发生。

为了让后面的概念更容易理解,可以先记住五句话:

  • 状态 回答工作项现在停在哪里;
  • 状态类型 把不同流程里的自定义状态归入待处理、进行中、已完成;
  • 流转步骤 连接两个状态,定义工作项允许怎样变化;
  • 步骤规则 回答执行前检查什么、执行时填写什么、执行后还要做什么;
  • 执行历史 回答这次变化以后如何被证明。

如果平台只保存最后一个状态值,它记录的只是结果;当它开始管理状态类型、流转步骤、规则和历史,才真正拥有了工作项生命周期。

状态字段保存结果,状态机约束变化过程

01 先把“状态”从各种进展描述中分离出来

小团队管理待办时,“未开始、进行中、已完成”通常已经够用。进入研发协作后,需求、缺陷、风险和发布拥有不同生命周期,状态自然会增加:草稿、待评审、待开发、开发中、待联调、待验收、已完成、已取消……

状态多并不是问题。真正的问题是,每个状态到底在表达什么。

从第一性原理看,状态应该是一个工作项在某段时间内所处的稳定、互斥、可查询,并且会影响下一步行为的业务阶段

“待评审”意味着需求已经具备评审条件,正在等待某个角色形成结论;“开发中”意味着研发活动已经开始;“待验收”意味着研发已经提交交付物,判断责任正在从研发转向验收角色。它们都能回答同一个问题:这条工作项目前停在哪里。

很多混乱,恰恰来自把其他概念也做成状态。

“评审不通过”更像一次处理结果,它通常让需求从“待评审”退回“草稿”或“待完善”;“重复缺陷”“无法复现”是关闭原因,可以和“已关闭”同时成立;“延期”来自计划日期与当前时间的比较;“由测试负责人处理”描述的是当前责任。它们都重要,但不应该和生命周期阶段挤在同一组状态值里。

可以用三个问题判断一个词是否适合成为状态:

  1. 它是否描述对象能够持续停留的阶段,而不是刚刚发生的动作?
  2. 同一时刻,它是否应该与同一流程中的其他状态互斥?
  3. 进入这个值以后,允许执行的动作、承担责任的角色或统计口径是否会改变?

三个问题中只要有一个答不上来,它就不适合作为这一流程里的状态,大概率应该被设计成结果、原因、属性、角色或风险,而不是状态。

表格:概念、回答的问题、示例,共 6 行
状态、动作、结果、原因和角色不能混成一组状态值

02 自定义状态再多,也必须归入三个状态类型

当不同团队开始自定义流程,状态名称很快就会失去统一性。产品团队使用“待方案、设计中、待验收”,研发团队使用“待开发、开发中、待合并”,测试团队又使用“待验证、验证中、已关闭”。这些名称对各自团队都有意义,但平台如果直接拿它们做跨团队查询和报表,几乎不可能得到稳定口径。

解决办法不是禁止自定义,而是在自定义状态之上增加一层稳定分类。每创建一个状态,管理员都必须把它归入三种状态类型之一:

表格:状态类型、判断标准、常见自定义状态、平台通常如何使用,共 3 行

这里最容易误解的是“待验收”。它的名字带有一个“待”字,但需求已经进入交付过程,也已经由验收角色接手,因此通常应归入“进行中”,而不是“待处理”。状态类型判断的不是汉字,而是这条工作是否已经启动、是否仍占用组织的在制过程。

Jira 把所有自定义状态强制归入 To do、In progress、Done 三种 Status category,用它们统一列表、看板和报表。ONES 创建工作项状态时同样要求选择状态类型,并在看板排序和度量中使用“未开始、进行中、已完成”。如果从零设计一个中性的项目管理平台,我会在产品界面里统一称为“待处理、进行中、已完成”:其中“待处理”比“未开始”更宽,既能覆盖尚未启动的工作,也能覆盖已经提交但仍在队列中等待处理的工作。

自定义状态必须映射到统一状态类型,再服务看板与度量

状态类型不是进度百分比,也不能替代具体状态。两条需求都属于“进行中”,一条可能正在开发,另一条可能正在验收;平台仍要依靠具体状态决定下一步动作和责任人。状态类型解决的是跨流程归一,具体状态解决的是单条工作项现在到底停在哪里。

“已完成”也不等于“成功交付”。一条已取消需求和一条已上线需求都结束了当前生命周期,可以共同归入“已完成”类型,但它们的业务结果完全不同。Jira 用 Resolution 表达工作项以什么方式结束,例如完成、不处理、重复或无法复现;自研平台同样应该保留结果或终止原因,不能看到绿色状态类型就把所有对象都计入交付成果。

这层分类一旦建立,就应该成为平台级稳定语义。具体状态可以新增、改名和重新编排,但每个状态都必须选择一个类型;看板颜色、WIP、周期时间和跨流程查询优先使用状态类型,不再依赖状态名称字符串。否则管理员只把“开发中”改成“研发中”,报表口径就可能跟着失效。

所以状态设计的第一条原则不是“少一些”或“多一些”,而是:

状态负责定位,结果负责解释,角色负责归责,风险由事实计算。

这四件事拆开以后,后面的权限、流程、统计与自动化才有稳定基础。

03 状态机的最小模型,是状态之间必须经过流转步骤

只配置“草稿、待评审、开发中、已完成”四个值,还不能称为状态机。它只是一张状态字典。

状态机真正增加的,是状态之间的合法路径。工作项不能从任意状态跳到任意状态,而要通过一个有业务语义的流转步骤完成变化。

以需求为例:

  • 从“草稿”进入“待评审”,执行的是“提交评审”;
  • 从“待评审”回到“草稿”,执行的是“退回修改”;
  • 从“待评审”进入“待开发”,执行的是“评审通过”;
  • 从“待验收”回到“开发中”,执行的是“验收不通过”;
  • 从“已完成”回到“开发中”,执行的是“重新打开”。

状态是节点,流转步骤是有方向的边。图上允许画分支、回退和循环,但一条工作项在这一套生命周期中,始终只有一个当前状态。

状态是节点,流转步骤是有业务语义的边

为什么一定要把状态和步骤拆成两个对象?因为同样的前后状态,不一定代表同一件事。

一条缺陷从“待验证”进入“已关闭”,测试人员可能执行的是“验证通过”,管理员也可能在异常情况下执行“强制关闭”。两次操作的来源状态和目标状态相同,但权限、必填信息、业务含义、审计责任和统计口径都不一样。

如果系统只保存“待验证 → 已关闭”,三个月后只能知道状态变了,却无法解释它是正常验收,还是治理旁路。流转步骤因此必须拥有独立、稳定的标识,不能只用“来源状态+目标状态”代替。

不同产品对这条“边”的命名并不一致。Jira 使用 Transition;ONES 直接称为“步骤”;TAPD 和飞书项目更多使用“流转”或“状态流转”。形式化的状态机理论通常把它叫作 transition,工程文档里也常译成“迁移”。但在面向管理员和普通用户的产品里,“流转步骤”和“动作”更容易理解:前者是后台配置对象,后者是详情页上用户真正点击的“提交评审”“验证通过”。本文后面统一使用这两个名称。

一套能够运行的工作项状态机,至少包含六类模型:

表格:模型、产品要定义什么、为什么不能省略,共 6 行

这张表也给产品经理提供了一条很实用的检查线索:当用户说“我要配置工作流”时,他可能不只是想画状态连线,还想定义谁能点按钮、点击时填什么、什么情况下不能通过,以及通过以后系统做什么。

Jira 在工作流中使用 Transition 连接状态,并把规则分成 Restrict transitions、Request input、Validate details 和 Perform action:先限制谁在什么条件下能看到或执行,再收集与校验输入,最后修改字段、分配负责人、发送通知或触发 Webhook。ONES 的“步骤”也沿着相近顺序配置步骤验证、步骤页布局和后置动作。TAPD 的高级流转把授权用户和附加字段放在某次流转上,飞书项目则把状态流向、流转表单与授权角色组合在状态流配置中。

叫法不同,底层判断是一致的:状态只描述工作项在哪里,真正承载规则的是连接状态的流转步骤。

飞书项目又提供了另一种思路。它可以把状态流向、流转表单与授权角色放在同一套状态流配置里,并根据当前状态能够执行流转的角色计算当前处理人。这样,负责人不再只是一个人工维护的人员字段,也可能是生命周期规则推导出来的当前责任。

这类设计更自动,但也更需要管理员理解规则的后果:修改授权角色,不只是改了一项权限,还可能改变工作项进入某个阶段以后由谁接手。

主流产品对状态之间连接及步骤规则采用不同命名

界面上,用户点击一次“提交验收”,似乎只是把状态从“开发中”改成“待验收”。从产品模型看,中间并不是一条无条件的箭头,而是一个拥有完整执行周期的步骤。

我会把步骤规则固定拆成三段:

  1. 执行前 决定动作能否开始。系统检查当前身份、角色、工作项属性、子工作项和关联关系。例如只有当前研发负责人可以提交验收,验收负责人不能为空,所有阻断级子工作项必须已经完成。对权限和确定不适用的条件,可以直接隐藏或禁用动作;对用户可以修复的条件,则应该保留入口并说明还缺什么。
  2. 执行时 收集这次动作刚刚产生的信息。用户点击“提交验收”后,平台展示流转表单,要求填写交付说明、代码版本、影响范围和必要附件;提交时再次校验必填项和最新业务事实。再次校验不能省,因为用户打开表单以后,负责人、子项或依赖状态可能已经被别人修改。
  3. 执行后 让这次决定产生完整结果。平台更新目标状态、完成时间和下一负责人,追加执行历史;随后可以发送通知、修改其他字段、调用 Webhook、触发 CI/CD 或启动自动化。哪些动作必须和状态一起成功,哪些可以异步重试,要在模型里明确分开。

把“提交验收”写成一份步骤契约,会比画一条箭头更容易发现遗漏:

表格:执行阶段、需要配置什么、“提交验收”示例、失败时怎么处理,共 3 行

这三段不能混成一个“规则列表”。执行前规则决定用户能不能开始;执行时规则帮助用户补齐本次决定所需的信息;执行后规则负责把决定落实到系统和外部工具。管理员如果看不到规则所属阶段,就很难判断执行顺序,也无法预期某条规则失败时状态到底有没有变化。

把三段规则放回同一个“提交验收”步骤,会更容易看清每一段各自承担什么责任。

流转步骤必须把规则明确拆成执行前、执行时和执行后

这张图里最重要的不是三种颜色,而是失败边界:执行前失败,动作不能开始;执行时失败,工作项停在原状态并保留可修正的输入;执行后只有核心数据写入失败才回滚,通知和外部系统失败则进入重试与补偿。

即使都发生在状态写入之前,权限检查和输入校验也要区分。权限不通过意味着用户不应承担这次决定,动作可以隐藏或禁用;输入校验不通过意味着他有权执行,只是当前信息不完整,系统应保留表单并指出怎样修正。API 与 Agent 还需要稳定错误码、失败规则标识和可执行的修复建议,不能都返回一句“操作失败”。

第四篇讨论过新建布局、详情布局和流转布局。流转布局真正发挥价值的地方,就是步骤执行时的表单。

创建缺陷时,用户有条件提供复现步骤、影响版本和严重程度;研发完成修复时,才会产生解决方案、修复版本和影响范围;测试完成验证时,才会产生验证结果、验证环境和证据附件。若平台在新建时要求一次性填完所有字段,用户只能填写“待补充”“无”或一个临时值。

同样,“缺陷关闭时必须填写根因”也不是普遍正确的规则。关闭动作通常更适合要求处理结果、验证结果和必要说明。根因应该在哪一步填写,取决于组织如何治理缺陷:可以在问题确认时记录,也可以在修复完成或复盘阶段补充,还可以只对高严重度缺陷设置门禁。

字段在哪一步出现,不取决于它重不重要,而取决于这个信息在什么时候真实产生,并且是否影响当下的业务决定。

因此,流转表单只应该收集本次动作已经产生的信息。同一个字段可以在详情页可选,在“提交验收”时必填,进入“已完成”以后只读。字段值还要和步骤执行历史一起保存,否则后来只能看到状态从 A 变成 B,却看不到当时依据什么完成了判断。

关系模型也会在这里发挥作用。第五篇讲过,父子层级、依赖、阻塞与普通关联首先是在描述对象之间的事实。状态机不会因为存在一条“阻塞”关系就自动修改状态,但步骤执行前的规则可以读取这些事实:有未完成的阻塞项时禁止发布,所有必需子工作项完成后才允许提交验收。

这条边界很重要:

关系提供事实,规则解释事实,流转步骤完成合法的状态改变。

不要让关系一建立就偷偷写状态,也不要让状态机自己猜测对象之间的语义。三层拆开以后,规则才容易解释,异常也容易追踪。

04 详情页、看板、批量操作和 Agent,必须执行同一个流转步骤

状态机是否真正成立,不看后台有没有流程图,而看所有入口是否遵守同一份规则。

在详情页里,用户不应该先面对一个“请选择新状态”的下拉框,而应该看到当前可以执行的业务动作。一条处于“待验证”的缺陷,可以显示“验证通过”“返回修复”这类动作按钮。每个按钮都对应一个独立流转步骤,拥有自己的执行前规则、流转表单和执行后动作;而验证的结论——“通过”“无法复现”“重复”——作为本次步骤填写的处理结果保存,不再做成并列的状态或按钮。

这比直接编辑状态多了一层,但多出来的正是业务语义。状态告诉用户当前位置,动作告诉用户下一步能做什么。

看板拖拽也不应该成为状态机的后门。看板列服务于团队组织工作,具体状态服务于对象生命周期,两者可以映射,但不是同一层模型。

例如,团队可以把“待开发、开发中、待联调”映射到同一个“研发中”看板列。用户把卡片拖到“已完成”列时,平台仍要从当前状态解析可以执行的流转步骤。如果存在“验收通过”“取消”“强制关闭”等多条动作,或者步骤需要补充表单,就要让用户选择并填写信息,而不是静默覆盖状态。

飞书项目的公开帮助也体现了这层原则:卡片拖拽涉及字段修改或节点流转时,仍然遵守工作项状态变化的可见性和禁用逻辑。拖拽只是交互入口,不应该改变业务规则。

批量操作同样不是同时写入 N 个状态,而是为 N 条工作项寻找能够共同执行的步骤。ONES 对批量流转要求同一工作项类型、同一当前状态,本质上是在先获得一致的候选步骤。即使候选步骤相同,每条工作项的负责人、权限、字段和子项仍可能不同,系统依然要逐条校验,并分别返回成功或失败原因。

当平台开放 API、自动化和 Agent 工具以后,这个边界会变得更加重要。

一个容易实现的接口是“把状态更新为已关闭”,但它没有表达执行的是“验证通过”“取消”还是“强制关闭”,也无法自然承载步骤表单、权限、流程版本和幂等信息。更合理的能力应该分成两步:

1. 查询工作项当前可执行的步骤,返回步骤标识、动作名称、目标状态、所需输入和未满足条件;

2. 执行指定步骤,请求中携带步骤标识、工作项数据版本、本次输入与幂等键。

对高风险动作还可以先做预检查,但正式提交时必须重新校验。因为从“可以执行”到“确认执行”之间,负责人、子工作项或依赖状态都可能已经变化;涉及子项、阻塞等跨对象条件时,需要在步骤提交的事务内重读这些事实,避免校验与写入之间出现竞态。

同样需要写清楚的还有 Agent 的执行身份。Agent 查到一条“待验证”的缺陷时,可以做的是把“验证通过”这个动作带进对话,按当前用户的权限校验、必要时请求用户确认;它不能以系统身份绕过一个没有验收权限的用户去完成验收。系统身份只应出现在显式配置的系统步骤或自动化里,而且必须留下审计记录。

这样设计以后,页面、看板、批量操作、API 和 Agent 只是不同交互入口,底层执行的仍然是同一份步骤契约。Agent 也不再靠猜测状态名称做决定,而是先读取平台明确给出的可执行动作,再选择或请求用户确认。

把五类入口放在一起看,产品边界会更清楚:入口只负责表达用户意图,查询和执行步骤才是统一的业务接口。

详情页、看板、批量操作、API 和 Agent 共用同一个流转引擎

如果看板拖拽、批量操作或 Agent 各自复制一份判断规则,规则迟早会漂移。同一条缺陷就可能在详情页无法关闭,却能被 API 直接写成“已关闭”。因此,多入口设计要复用的是同一个流转步骤,而不只是同一个目标状态。

05 一次可靠的步骤执行,是一条受控的运行链

当用户点击“提交验收”时,系统并不是依次修改几个字段那么简单。一个可靠的流转步骤通常要完成以下过程:

1. 读取工作项当前状态、数据版本与正在使用的流程定义;

2. 根据当前状态解析候选步骤,不接受调用方任意指定目标状态;

3. 判断动作是否适用,并校验当前操作者的执行权限;

4. 展示流转表单,收集并校验本次步骤需要的字段、说明和附件;

5. 检查子工作项、关联关系与其他业务条件;

6. 在一次原子写入中更新状态和关键字段,并追加步骤执行历史;

7. 提交成功后触发通知、字段更新、自动化、CI/CD、外部回调或系统同步。

把这七段处理按发生顺序展开,才能看清不同失败应该在哪一层被截住。

一次流转需要依次完成读取、解析、校验、核心写入与外部触发

前五段失败时,业务决定尚未成立,工作项必须停在原状态;第六段的状态、关键字段和历史要共同成功或失败;第七段发生在核心结果提交以后,可以独立重试,但不能把已经成立的状态变化重新解释成失败。

为什么要把执行过程拆得这么清楚?因为不同步骤失败时,产品需要给出不同结果。

权限不通过,说明这个人不能承担这次决定;业务校验不通过,说明事实还不支持决定成立;数据版本冲突,说明工作项已经被其他人修改,需要重新读取;外部通知失败,则不应该反过来否定已经成功的状态改变。

执行后不能只设计成一个没有顺序的动作列表。状态、关键字段、步骤输入与执行历史属于步骤的核心结果,应通过一次原子写入共同成功或失败。讲得直白一点:要么全部保存,要么全部不保存。否则可能出现状态已经变成“已完成”,验证结果却没保存,或者字段写入成功但历史里没有执行人。特别地,“转交验收负责人”这类决定下一步由谁接手的字段写,也必须与状态在同一事务内提交。

通知、CI/CD、外部回调和跨系统同步可以在步骤核心结果提交后异步执行,但要有重试、幂等和失败记录。ONES 的做法是允许后置动作执行失败而不回滚步骤结果——那是可接受的产品选择,但前提是“转交负责人”这类关键字段不能放进异步侧。

步骤核心结果已经成功、外部同步失败时,平台应该明确告诉用户:“状态已更新,外部同步待重试。”不能用一个模糊的“操作失败”,让人不知道这次业务决定到底有没有生效。

并发和重试也不是纯技术细节,而是产品语义的一部分。两个人可能同时从“待评审”执行“评审通过”和“退回修改”,Agent 也可能因为网络超时重复提交同一请求。工作项需要数据版本或乐观锁——发现别人已经改过,就拒绝旧请求;步骤请求需要幂等键——同一次请求即使重试,也只允许生效一次。否则后一次写入会覆盖前一次业务决定,通知还可能重复发送。

自动化可以走不同规则,但不能成为无语义的旁路

真实产品为了提升自动化成功率,常常不会要求机器完整模拟人工操作。

ONES 的公开帮助明确说明,流程自动化可以把工作项从 A 直接流转到工作流中的其他状态,不必逐步经过中间步骤,被跳过步骤上的后置动作自然也不会执行;代码事件(代码提交、新建分支、新建合并请求)触发状态变化时,则会忽略步骤验证与步骤属性这类人工校验,但后置动作仍会正常执行。

这是可用性优先的一种产品选择。代码提交是系统证据,人工点击“开始开发”是用户声明,两类事件本来就可能适用不同规则。问题不在于它们不同,而在于差异最终由历史语义承担:历史必须能区分人工、自动化与代码事件,一致性成本才看得见。

如果同一条工作项最终都到达“开发中”,管理员需要知道:

  • 哪些校验被跳过;
  • 哪些后置动作仍会执行;
  • 历史里如何区分人工、自动化、代码事件和管理员操作;
  • 报表是否把这些入口视为同一种步骤执行;
  • 自动化失败后由谁发现和补偿。

更稳妥的做法,是为机器建立独立的系统步骤或明确的自动化策略,让它仍然拥有稳定标识、权限边界和历史语义,而不是开放一个可以任意写状态的接口。

06 工作流一旦发布,就从后台配置变成了生产规则

状态机最难的部分,不是画出第一版流程,而是几万条工作项已经在这套流程里运行,管理员还要修改它。这里所说的运行实例,讲得简单一些,就是已经按照旧流程走到不同位置的那些工作项。

增加一个流转步骤、修改动作名称通常风险较低;删除状态、改变初始状态、调整权限或更换整套工作流,会直接影响正在运行的实例。产品必须回答:

  • 仍停在旧状态的工作项去哪里;
  • 已经发生的步骤执行历史怎样显示;
  • 报表、自动化和开放接口对旧标识的引用是否仍然有效;
  • 修改失败以后如何恢复;
  • 新建工作项与存量工作项从什么时候开始使用新规则。

工作流定义发布后,运行实例必须绑定可解释的版本

这里至少有三种常见策略。

  1. 原地更新 让所有实例立即使用新规则。它最简单,但一次错误可能同时影响全部工作项。
  2. 新旧版本并存 让存量实例沿旧流程运行,新建工作项使用新版本。历史最容易解释,但多版本会增加长期管理成本。
  3. 显式迁移 先定义旧状态到新状态的映射,再预演、执行并记录失败。它适合组织级流程统一,但需要完整的影响分析、迁移工具和恢复机制。

主流产品的具体实现并不完全相同。Jira 对正在使用的 active workflow 限制部分编辑;更新工作流或切换 workflow scheme 时,需要保证已有工作项在新流程中拥有合法状态,必要时进行状态映射,并通过临时工作流避免实例停在已删除的状态。TAPD 的公共工作流则保留变更历史,帮助管理员追踪配置如何变化。

这些实现背后有同一条产品原则:

流程配置一旦承载运行数据,就不能再按普通后台表单处理,而要按生产规则发布、迁移和审计。

我更倾向于把流程定义与运行版本显式分开。状态、流转步骤和规则使用稳定标识,历史保存执行当时的流程版本与关键输入;管理员发布破坏性变更前,先看到受影响的工作项、状态映射、自动化引用和不可迁移实例。

这套设计成本更高,却能避免三个月以后没人解释得清:一条旧需求为什么走过一条已经不存在的路径,一次验收当时到底遵守了哪版规则。

07 步骤执行历史不只是操作日志,而是审计与度量的事实来源

当前状态只能回答工作项现在在哪里,不能回答它经历过什么。

两条同样处于“开发中”的需求,一条可能昨天刚开始,另一条已经被评审退回三次;两条同样“已完成”的缺陷,一条一次验证通过,另一条关闭后又被重开。若平台只保存当前状态和最后更新时间,就无法可靠计算等待时间、处理时间、退回次数、重开次数和阶段瓶颈。

步骤执行历史至少应记录:

  • 步骤标识与前后状态;
  • 执行人或系统身份;
  • 发生时间与触发来源;
  • 本次步骤输入的关键快照;
  • 使用中的流程版本;
  • 步骤核心结果与关键后续动作结果。

同一份步骤事件会被两个完全不同的场景消费:审计要还原当时发生的事实,度量要把不同流程映射到可以比较的口径。

步骤执行历史通过稳定事件同时支撑审计与度量

图中两条分支不能反过来污染原始事件。审计需要保留当时的名称、身份、输入与流程版本;度量可以重新映射状态类型和统计口径,但不能为了报表方便改写历史。

这些事件既可以回答“谁在什么时候依据什么规则做了什么”,也可以继续生产周期时间、状态停留时间、首次完成时间、最终完成时间和流程瓶颈。

但状态机不会自动给出一套唯一正确的指标。

“完成时间”可以取第一次进入“已完成”类型,也可以取最后一次进入;周期时间可以从首次进入“进行中”算到最终完成,也可以扣除暂停与等待;被取消的工作项是否计入吞吐量,则取决于报表要回答什么问题。产品必须公开指标口径,不能让不同报表在状态名称上各自猜测。

审计与度量也要保持语义分离,而且都以稳定 ID 参与计算。审计关注当时真正发生了什么,因此旧历史不能随着状态重命名而被改写;度量关注不同流程之间的可比性,需要把具体状态映射到统一的状态类型——映射同样以稳定 ID 为准,状态改名只影响展示层,不改变度量归属。底层可以共享步骤执行事件,面向用户的解释不能混成一套。

08 状态机必须守住一个边界:一条工作项只有一个当前状态

一条普通工作项在一套生命周期中通常只有一个当前状态。这个约束让查询、权限和下一步动作都保持确定,也构成了状态机最重要的边界。

假设一条需求同时进入客户端开发、服务端开发、美术制作和测试准备。需求可以显示一个粗粒度的“开发中”,却无法用一个状态值说明四条分支各自进展到哪里。

如果新增“客户端开发中”“服务端开发中”“美术制作中”等状态,它们实际上可以同时成立,不再互斥——这本身就通不过前面三问测试里的“互斥”一问;如果继续创造“客户端完成、服务端开发中、美术待确认”这样的组合状态,状态数量会随着分支指数增长。

单状态机无法直接保存多条并行分支的运行态

项目管理平台通常有三种选择。

第一种,让主工作项只保留“开发中”这样的总体状态,具体并行过程不进入平台。它适合协作简单、管理者只需要看到总体进度的团队。

第二种,拆出多个子工作项。客户端、服务端、美术和测试分别拥有负责人、状态机和交付结果,父需求通过层级关系汇总。它适合需要独立负责、独立排期和独立统计的工作,也是第五篇关系模型与本篇状态机的组合方式。

第三种,在同一个业务对象上运行节点流程。多个活动节点分别保存进行中、完成或跳过,再通过条件分支和汇聚规则推进。它适合不希望制造大量工作项,但又需要精确表达跨角色并行过程的场景。

怎么选择,可以回到一个简单问题:这段工作是否值得成为一个独立的责任对象?

如果它有独立负责人、独立交付物、独立排期和独立统计价值,就应该拆成子工作项;如果它只是同一业务对象内部的一段稳定协作过程,则更适合交给流程编排;如果团队并不需要追踪到这个粒度,就保留总体状态。

状态机的边界不是“图上不能画两条线”,而是一个当前状态值不能诚实保存多条同时活动的事实

09 状态机管理的,是业务决定如何成立

从状态字段走到状态机,产品模型发生了一次根本变化:平台不再只记录结果,而是开始管理结果如何被合法地产生。

状态定义对象当前所处的阶段,状态类型提供跨流程统一分类,流转步骤表达一次业务动作;执行前规则检查权限与业务条件,执行时表单收集当时才产生的信息,执行后动作连接责任转交、字段更新、自动化和外部系统,历史与版本则让每次变化可以解释。

页面、看板、批量操作、API 和 Agent,都应该在同一份步骤契约上工作,而不是各自发明一套修改状态的方法。

这是产品经理设计状态机时最需要守住的判断:后台那张连线图只是配置界面,真正的产品模型是“一次业务决定由谁发起、依据什么事实成立、产生哪些结果,并且以后如何被证明”。

但状态机仍然只擅长管理单个对象的生命周期。当多个角色、多个活动需要同时推进、条件分支并再次汇聚时,继续增加状态只会让模型失真。

下一篇,我们会进入另一个同样经常被叫作 Workflow、却解决完全不同问题的模型:流程编排。

作者:AI产品零度,公众号:AI产品零度

本文由 @AI产品零度 原创发布于人人都是产品经理。未经作者许可,禁止转载

题图来自作者提供

更多精彩内容,请关注人人都是产品经理微信公众号或下载App
评论
评论请登录
  1. 目前还没评论,等你发挥!