作者|周明耀
编辑|小智
本文是周明耀技术管理专栏的第四篇文章。 明天我们主要讲一下软件开发的流程。
写在上面
最近,我写了两篇关于技术管理工作的文章,分别是《》和《》,并通过《》一篇文章回答了网友提出的问题。
明天回到我们的主题,我们继续第三篇技术管理工作,主要讲软件开发流程。
说到软件开发流程,有些朋友可能会讨厌这些标准化的流程。 他们认为无论发生什么,立即编码才是王道。 需求可以稍后再明确,设计是完全不必要的步骤。 不然速度太慢了。 他们称之为互联网软件开发精神。 互联网软件开发的精神是什么? 开源共享,模块化编程,极客精神,不野蛮开发。
我在看《聊建筑》这本书的时候,写了一个事后感悟,感觉自己终于摸索出了一本可以读很久的框架书,而不是所谓的牛逼的框架书,只能读1、2个小时。 我们经常遇到这样的笔试人员,你让他画整体结构图,恐怕他都没听说过,你换个提问形式,问他用什么框架,他立刻告诉你SSH、Spark、Mesos流程图软件,很多,但是当你让他画结构图时,他会很沮丧。 其实,你甚至不需要指望他去思考,比如为什么Hadoop的MapReduce并行估算模型在Map和Reduce之间采用Pull(拉)模式,而不是Push(推)模式? 为什么会出现Spark等等。 我将在后续文章中讨论此类问题。 我只想说,虽然这种框架的形成是从开发过程中框架设计环节发现问题开始,逐渐积累解决方案的。
以基线产品开发流程为例
通常,企业在开发软件时,会以两种并行的形式执行项目开发工作:基线和定制。 无论什么样的企业,都需要遵循成熟的产品开发流程体系,才能够生产出更优质的产品。 为此,如果项目较多,应合理安排定制前的基线和里程碑,使基线产品能够尽可能多地收集用户的通用需求,为定制项目的进展提供技术支撑,减少定制项目中大量代码修改的发生和新模块的需要。 据悉,产品开发流程体系也需要根据业务的实际时间需求进行更改。 不要屈服于按照瀑布方法或敏捷方法进行管理的需要。 你需要找到一种凡事都适合你的方法。 鞋子合不合脚,只有脚知道。
这里我们使用基线产品开发流程作为流程解释的基础。 需要注意的是,对于下面描述的每个阶段,在项目执行之前,必须明确每个阶段的目标,明确计划,并及时沟通,以确保每个阶段的所有成员对项目有一致的理解。
启动会议
项目启动会议的目标是明确产品开发项目的目标。 目标不是孤立存在的,目标和计划相辅相成,目标引导计划,计划的有效性影响目标的实现。 因此,在执行目标的时候,要考虑清楚自己的行动计划,以及如何更有效地实现目标。 这是一个大家必须详细清楚的问题。 否则,目标越不明确或太低,项目的实际效果就会受到影响。
项目启动会议需要说明项目目标、阶段定义、组织架构、管理流程等关键事项,并将这些内容写入PPT(最好有固定格式和示例文本,以便团队或公司共同遵循规范),需要达成一致。 对于关键角色的任命,还需要提前听取项目相关领导和关键利益相关者的意见。
用户需求
在开始软件开发之前,需要确定价格与获得的价值之间的比较,即ROI(投资回报率)。 一旦确定需要构建,就需要部署一系列资源来支持软件的生存。 这是对需求最原始的描述。
为什么要同时有用户需求和产品需求? 因为两者是有区别的,用户需求是用户提出来的,通常不描述技术,只描述产品目标。 产品需求是根据用户需求转化的技术实现需求。 需要对用户提出的产品目标进行细分,总结每个具体的功能点,然后将每个功能点细分为各种不同的操作流程,并对每个操作流程进行技术定义。
用户需求和产品需求很可能是不同的。 这是因为虽然你们都在谈论需求,但出发点可能不同,导致双方的关注点和思维方式不同。 用户需求聚焦于系统如何支持业务流程,底层需求是“业务目标的实现”。 技术人员关注的是合理的技术方案,其背后的要求是“工作量”、“实施难度”和“系统性能”。
产品需求
我们需要弄清楚产品总监或者项目请求者为什么要做这个项目? 这是最基本的业务需求。 需求分析确定的业务需求都是从业务需求推导出来的,必须服务于业务需求。
产品需求通常包括产品需求维度规范和产品需求矩阵。 产品需求矩阵通常按照子系统、功能集、执行单元的结构列出所有功能需求,每一列对应每个功能的工作步骤以及每个步骤的工作量。
产品需求写好后,需要进行审查。 在需求评审会上,产品和技术详细评审要求是否完成? 产品功能的正常场景有哪些? 是否存在闭环? 异常场景有哪些? 是否深思熟虑?
需求评审后,开发和测试负责人分别编写技术方案和测试用例。 对于技术方案审查,开发负责人与涉及的其他系统负责人讨论。 技术计划必须有业务流程图和序列图。 业务流程图是为了梳理开发对业务的理解,是否与需求一致。 序列图就是为了梳理这个需求所涉及到的系统交互。 技术方案审核通过后,确认工作量和交付时间,并向产品反馈。
总体设计
设计阶段的目标主要是对待开发系统的架构进行分析和设计,建立系统架构的基线,为以后的实施提供稳定的基础。
设计阶段包括系统架构的输出。 良好的系统架构设计可以帮助人类梳理业务逻辑和把握核心需求,设计稳定、可扩展的业务系统,评估业务开发周期和开发成本,有效规避风险。 例如,建造房屋时,必须有建筑图纸。 有了图纸,就可以计算出工期。
总体设计是整个系统的框架设计,意义重大,一般情况下不能省略(只有维护项目可以省略总体设计,因为标杆项目已经设计好了),所有产品开发项目都必须首先进行总体设计。
总体设计分为三个阶段:
轮廓设计
概要设计的目的是描述系统各模块的内部设计,是连接总体设计和详细设计的桥梁。
总体设计按照结构化设计方法进行设计。 结构化设计方法的基本思想是:根据问题域,将软件逐步分解为不需要进一步分解的模块。 每个模块执行某一功能,为一个或多个父模块服务(即接受调用),同时也接受一个或多个子模块的服务(即调用子模块)。 模块的概念对应于编程语言中的子例程或函数。
在总体设计阶段,将软件按照一定的原则分解为模块级别,为各个模块分配一定的任务,并确定模块之间的调用关系和套接字。
在这个阶段,设计者一般会考虑并关心模块的内部实现,但不会过多关注。 主要关注定义模块、分配任务、定义调用关系。 这个阶段应该把模块之间的套接字和参数传递做得非常详细和清晰,并且需要编写严格的数据字典,以防止后续设计中的混乱或误解。 外形设计通常不是一次性完成的,而是反复进行结构调整。 典型的调整是合并功能重复的模块,或者进一步分解可以重用的模块。 在概要设计阶段,应最大限度地提取可复用模块,构建合理的结构体系,节省后续环节的工作量。
概要设计文档最重要的部分是分层的数据流图、结构布局、数据字典和相应的文字描述等。在概要设计文档的基础上,可以并行地进行各个模块的详细设计。
详细设计
详细设计阶段是根据概要设计阶段的分解,设计各个模块中的算法和流程,并对各个模块完成的功能进行详细描述,并将功能描述转化为准确的、结构化的流程描述。
在详细设计的这个阶段,每个模块可以分配给不同的人进行并行设计。 设计者的工作对象是一个模块。 根据概要设计分配的部分任务和外部套接字,对模块的算法、流程、状态转换等内容进行设计和表达。 这里需要注意的是,如果发现需要进行结构调整(如分解子模块等),必须回到概要设计阶段,并将调整反映在概要设计文件中,但不能当场解决,不打招呼。 详细设计文档最重要的部分是模块流程图、状态图、局部变量以及相应的文字描述等。一个模块对应一份详细设计文档。
总体设计阶段一般得到软件结构,详细设计阶段常用的描述方法有:流程图、NS图、PAD图、伪代码等。详细设计的目的是描述某个模块内部的处理流程、开发方法和编码方法。 一般来说,详细设计由项目介绍、模块描述(具体说明各模块的内部流程、功能、逻辑、消耗以及未解决的问题)、接口设计(包括内部套接字和外部套接字)、数据结构设计(包括数学结构和逻辑结构)、特殊处理等部分组成。 软件的详细设计最终是以文本形式描述软件系统各部分的具体设计方法、逻辑和功能。 这样,在实现过程中,编码人员就可以严格按照这个原则来实现代码。
写代码
代码的编写可以按照以下原则:
先做核心模块的压力测试:很多程序员习惯把事情做完,快上线的时候再做性能测试,所以如果上面的设计出现问题,那就是大问题了。 其实后期快上线的时候也会做性能测试,但是我觉得前期还是很重要的。 其实要做好这件事,你需要了解一些业务。 你需要知道业务压力在哪里,业务需求的重点在哪里。 很多时候产品总监不说,但是你要问清楚。
确保过程可控:代码执行时,必须维护中间输出。 例如,每处理10万条日志,就会写入一个状态日志,记录已处理的日志条目数和当前执行时间。
多做日志:很多时候,我对代码写的不是很满意。 比如某个处理效率没有优化,某个处理方法不够简单,或者可扩展性比较差。 代码写得很白痴,但我可能短时间内无法找出最合理的解决方案。 考虑到这不是上线初期的重点,所以我不会刻意优化。
简单易懂的逻辑:不要让自己卷入其中。 时间长了,没人能理解你的逻辑。 如果函数中的逻辑确实很难实现,请尝试拆分。
不要陷入框架的困境:框架最大的问题是什么? 嵌套过于麻烦。 为什么我仍然对框架感到恼火? 因为我们经常会遇到每秒上千个请求的处理场景,所以在做这样的调优时,我们必须从无数框架中寻找数据处理的逻辑,找到性能卡点,或者只改两行代码,花了七天时间才找到问题。 程序员记住,你的技术能力一定不能受到框架的限制。
使用熟悉且成熟的技术:很多人根本不了解他们的障碍和问题,也不知道相关技术产品的优点和缺点。 看了一堆第三方数据评估后,他们头脑发热,学习新技术。 之后,他们就跳进坑里,出不来了。 在使用一项新技术之前,建议充分了解该技术的特点、适用范围和不适用范围。
代码审查
众所周知,团队中的代码审查(CodeReview)可以提高代码质量、共享项目知识、明确职责,最终实现更好的软件和更好的团队。
第一次代码审查非常重要。 一般来说,每周都会进行第一次代码审查。 首先,最初的代码审查有助于你跟踪项目的进度。 我们可以真实地看到我们手下的人进步得怎么样,更早发现他们是否误入歧途。 有时,人们会说“快完成了!”,而当你看代码时,你发现什么都没有或者只是一堆垃圾等等流程图软件,但距离完成还很远。 在管理中,这些情况是最令人反感的,所以我觉得代码审查是防止此类麻烦的最好方法。
单元测试
要理解单元测试,首先要理解什么是“单元(Unit)”。 所谓“单元”是指代码调用的最小单元,实际上是指功能块(Function)或方法(Method)。 所以单元测试是指对那些代码调用单元的测试。
单元测试是白盒测试的一种,是必须清楚单元的代码细节的测试。 因此,单元测试的编译和执行都是由软件工程师来完成的。 与单元测试相反,还有集成测试。 集成测试基本上是黑盒测试,主要由测试人员根据软件的功能指南进行,需要特殊测试环境的配合。 集成测试分为功能测试、回归测试等。
需要进行单元测试的代码实际上是开发人员自己编写的逻辑。 测试逻辑所依赖的环境是否正常并不是单元测试的目的。 将逻辑引入环境访问代码只会使逻辑更难测试,并使逻辑代码更难单元测试。 因此,只有可单元测试的代码才能进行单元测试。 判断可测试代码的另一个方法是看这个方法是否可以直接与主函数一起运行,如果可能的话,它是单元可测试代码。 可测试代码的另一个特点是,模式单元的参数可以由开发人员自由模拟,而不依赖于外部环境。
集成测试
集成测试,也称为组装测试或联合测试。 在单元测试的基础上,将所有模块按照设计要求组装成子系统或系统,并进行集成测试。 实践证明,有些模块实际上可以独立工作,但并不能保证它们连接时也能正常工作。 一些不能在本地反映出来的问题,可能会在全球范围内暴露出来。
集成测试是在软件系统集成过程中进行的测试,其主要目的是检测软件单元之间的欺骗是否正确。 根据集成测试计划,在将模块或其他模块组合成越来越大的系统的同时,运行系统来分析形成的系统是否正确以及各个组件是否同步。 集成测试有两种主要策略:自上而下和自下而下。 也可以理解为当软件设计单元和功能模块组装集成到一个系统中时,对应用系统的各个组成部分(软件单元、功能模块插座、链路等)进行联合测试,以确定它们能否协同工作。 这些组件可以是代码块、独立应用程序、网络上的客户端或服务器端程序。
系统测试
系统测试阶段包括系统测试计划和用例编写、功能测试、性能测试和稳定性测试。
为了验证需求分析确定的功能是否完整、正确实现,还对安装、部署、适应性、安全性、接口等非功能性需求进行测试。 系统测试也是由测试人员负责。 需求分析完成后进行设计,集成测试完成后实施。
功能测试通常由独立的测试团队以黑盒的形式进行,主要是测试系统是否符合“所需的尺寸规格”。 经过以上阶段的测试确认后,系统完全模拟客户环境进行测试。 系统测试是将已经确认的软件、计算机硬件、外设、网络等要素组合在一起,进行信息系统的各种组装测试和确认测试。 目的是对系统的需求进行比较,以发现开发的系统与用户需求之间的差异或矛盾,从而提出更多、更成熟的解决方案。
性能测试验证系统的稳定性和效率,检查系统是否满足规定的性能要求。 性能测试一般会选择一些典型的功能来检验系统在大量用户同时使用系统时是否稳定。 性能测试由测试人员负责。 可以在系统测试完成后进行,也可以先对重要模块进行性能测试,这样可以贯穿整个测试周期。 目的是尽快发现系统的性能难点并提前解决。
稳定性测试和性能测试必须等到系统基本没有问题、趋于稳定之后才能发挥作用。 否则很难顺利测试,出现异常也无法判断是系统架构问题还是功能缺陷。
稳定性测试(也称为可靠性测试)给系统负载一定的业务压力,让系统连续运行一段时间(通常是7x24小时),衡量系统是否仍能稳定运行。
产品发布
产品发布是系统测试后的最后一步。 一般情况下,软件产品开发过程中,不需要进行产品试产,可以直接上线。 系统测试人员只需输出系统测试报告并批准产品发布(在线)。
在产品发布之前,需要回顾一下从立项到产品发布简报的整个产品开发流程,强调整个过程中的不足,总结经验,为下一步的项目提供经验案例。 本次会议可以以即将召开的会议的形式举行。 需要召集产品总监、主要开发人员、测试人员、上级领导参与。 是经过精心策划的,在这款产品上市后,尽最大努力去解释它的疗效和好处。 规划上线后的价值评估。 这个环节是不可或缺的,即使是迭代速度很快的互联网公司,这个环节也需要得到满足。
开发流程回顾
虽然开发流程体系中没有这样的流程,而且我个人认为是极其重要的。
所有的总结,只要带着问题去思考,就会有所收获。 这就是评论。 无论我说多少,如果没有类似的经历,都很难产生强烈的共鸣。 我认为认识问题的最好方法是你在问题中扮演两个不同的角色。
总结项目的经验教训的目的是总结问题,分析原因,防止今后再犯同样的错误,而不是追究任何人的责任。
假设需求理解有缺陷,如果在需求阶段就发现了,改起来可能只需要一个小时,如果在设计完成时发现缺陷,由于涉及的人员和文件增加,可能需要一天的时间,如果直到代码编译才发现缺陷,可能需要七八天。 如果缺陷未被发现并直接进入生产系统怎么办? 这不是工作量的问题,也不可能害怕损失。 在质量管理理论中,每晚发现一个缺陷,修复的成本就会成倍增加。
写在最后
敏捷开发、极限开发等模式是为了解决需求不明确、时间紧迫时的快速迭代,而不是从根本上否定开发过程。 设计还是要设计的,但是生命周期纵向分为几个周期。 软件开发是一门具有严格工程要求的学科。 让我们坚持以严谨的态度和高效的工作方法打造高可用、高品质的软件产品。
关于作者
周明耀,2004年毕业于四川大学,获工学硕士学位。 拥有12年美国投资建设银行工作经验,4年分布式系统和物联网工作经验,10年技术团队管理经验。 IBM开发者峰会专家作者、Infoq专栏作家。 出版书籍《大话Java性能优化》、《深入理解JVM和G1GC》,正式出版《技术领导力——如何领导一支软件开发团队》一书。 已提交分布式计算领域发明专利超过15项。 微信号michael_tec。
广告
2017年,运维技术有哪些值得关注的热点? 智能运维、Serverless、DevOps……CNUTCon全球运维技术大会将于9月在北京召开,12位专家联合出品,解密最前沿运维技术,推荐学习! 点击“阅读原文”抢先看!
明天的建议
点击下图阅读
Instagram 的 Python 体验