报价是销售中的关键关节。软件开发服务的报价,尤其复杂。很大一部分困难源于成本估计,它有很多依赖变量,而很多依赖变量在报价阶段,仍然存在很大的变数,而影响估计准确。所以在估算中,要考虑全面,还要尽量减少不确定性。这也是软件工程中的一个经典问题。本文想讨论的,也是和人的发挥强关联,是需求迭代和简化设计的重要性。
常见瀑布式做法,按照客户需求,依次设计架构,切分功能模块和依赖关系,估算每个模块的人月,再加一定的余量,以此为根据计算研发费用。销售以此为依据来决策报价。这样的“瀑布式”估计,没有充分考虑软件开发服务往往是多环节多次迭代的一个过程。当需求发生细化和变化,工程上需要调整和重构,或者新的运维和升级需求的时候,就会出现成本增加的压力。导致不可持续的局面。
因此在工程正式开始之前,首先对需求进行细致的梳理尤其重要,千万不能跳步骤,必须标准化。功能上的需求,通过低保真low fidelity的手段,让客户体验最终的结果,快速完成迭代,确定功能细节。细致到什么程度?客户端要细化到每个页面,服务端细化到每个服务和每个API,数据端细化到每个数据定义,等等。类比装修,对成本计算要求极高,只有在基于共识的图纸上,才可能计算工程时间,计算材料、最终报价。
功能部分外,软件的稳定性、数据备份和恢复、运维、扩容、升级等需求,也要纳入到需求设计中迭代。需要强调尽量找到行业标准的方案,甚至获得已有类似项目的相关成本数字。这样能够弥补自己思考上全面性的不足。
简化(设计,开发和系统)是另一个重要的帮助成本估计的手段,但往往被忽视。简单的系统,成本更容易估计,也更容易开发。做好简单题,不做复杂题。就好比,有时写程序,多重循环比递归更快速,更不容易出错。但是我们对于简单和复杂的直觉并不可靠,经常低估复杂度,高估自己的能力。
细分而言,首先是简少开发量。软件工程中,代码行数LoC是项目复杂性的一个重要指标。随着云原生的普遍,各种框架的崛起,软件配置也应该考虑作为代码行数。开发量越大对于成本抖动风险越大。在选择技术栈的时候,考虑对于开发量的影响。充分利用开源代码,甚至购买第三方的软件服务。尤其是项目早期,能不写代码就不写代码。
其次,简化是功能。在能在影响客户决策的阶段,在不牺牲可用性的前提下,尽量选择简单的设计。复杂的需求,尽可能安排到项目的后期,甚至后期的版本中。更不要为了复杂但是优先级低、非关键的需求,影响项目的复杂度。反复推敲minimum viable product的边界。
再次,避免研发没经验、没把握的功能。比如人工智能算法相关的,如果没有现成的模块可用,需要的研发投入将不可控,也难以进行成本统计。
最后,外部依赖模块的复杂性,也需要考虑和反复复盘。引入的开源和第三方软件的成熟度,匹配度。一个框架的版本重要更新,可能需要几个月时间才能完成迁移。选择错误的依赖,尤其是太复杂的依赖也会带来灾难性的结果。
软件开发是个复杂的过程。在需求梳理和简化基础上,还要给足余量。不断提醒自己,不要高估自己,没有一个什么都可以干的团队。