如果要利用第一步的成功,来扩展一个事业,就必须要想办法满足更多的需求,从而占领更大的市场份额,因此需要在“产品”和“团队”两方面都做准备。
特种兵小队在踏出项目成功的第一步之后,老板往往都希望他们能达到更多的目标,比如更多的功能需求,更好的性能表现等等, 因此,他们也会获得更多的资源。当然,在这个过程中,特种兵小队必须要改变的之前的工作方法,否则就会陷入“再而衰”的危机之中。
作为项目经理或者制作人,他们有可能会需要“兑现”之前因为付出努力所对应的回报,否则就会觉得工作没有得到认可。而对于主程序员等主要开发人,除了他们希望有所“兑现”以外,还可能觉得这个项目对于自己的专业发展已经没有价值了,没有更多的学习空间和应用知识的空间,因此就会想换一个项目去做。对于支援人员来说,他们往往认为既然项目已经“上了轨道”,就没理由让他继续兼着“打苦工”了,对于分派的任务可能会有所懈怠。
因此在项目转型的过程,是问题最集中爆发的时期。所以我们必须要在小型项目转型之前,做好几个事情:
论功行赏 :小型项目的艰苦加班和多种不同工作的压力,是需要给予一定回报的,不管是加工资、发奖金、升职位,还是一些别的晋升和嘉奖手段,都必不可少。这对安抚团队的“急躁”心理具备重要作用。
沟通转型之后的需求 :人对于改变总是害怕的。因此这个团队和这个项目,为什么要变化,为什么要加入更多的人和别的资源。目标是想做成什么样,都需要有充分的沟通。否则一方面容易给成员过高的期望,另外一方面也让原本大家都很清楚的项目目标变得模糊,从而增加猜忌。
总结经验教训 :项目有变化,总会有人离开。而项目的经验和教训却不应该随着人的离开而消失,特别是人数本来就少的情况下。争取时间记录整理项目经验,是为下一个阶段做好工作所必须的准备。
优化程序代码,提高重用性 :小型项目代码结构都不会特别的完美,有很多修补的痕迹。但是如果要进入一个新的阶段,让很多人去维护这样的代码,那么开发的效率就会降低。并不是说一定要整体重构,而是应该检视所有的代码,把那些明显的业务功能模块,特别是详细的、复杂的商业逻辑或者业务细节的实现代码,切分的更细,都包装好。因为项目发展,可能对于技术需求,比如性能、稳定性、扩展性会有更多要求,但是对于业务逻辑,修改并不会很多,所以这些业务逻辑代码,实际上是更常被“重用”的部分。根据业务逻辑封装代码本身就是一门技术,所以在做这个事之前,程序员除了要学习重构的种种知识,还要对业务需求本身进行思考和总结。只有完成了这些工作,团队在需要增加一些功能时,也能更加轻松简单的去完成。曾经有一个电子商务网站,在第一版面世的时候,为了提高开发速度,代码的结构并没有得到特别的关注;但是面世后随之而来的各种性能需求、特殊的商家的要求、甚至来自政府和法规的需求,都让这个系统急速的变得更加复杂。面对这个局面,负责开发的技术团队毅然做了一次全面的重构,把以前开发的功能都封装了一次,形成了自己的“业务 API ”库,不但重用了大部分以前开发的代码,还为之后的开发提供了规则和范本,从而很好的度过了需求激增的危险期。
完成了准备工作之后,正式进入新的项目体制时,在管理上,准备长期作战的最重要工作,就是改变“只看结果不管过程”的观念,对于项目开发的过程,开始要足够的关注。可以让原来的“特种兵”利用之前开发的经验,制定一些开发的流程和规范。然后关注这些规范的执行情况。而不是去挑战规范和流程是否完备。
一般我们需要准备的流程和规范包括:
制定项目开发计划的流程 :一个功能需求如何进入计划,需要有一个确定的流程,不管是民主讨论,还是独裁的决定方式,都必须要有一个明确的流程,用来记录下要做什么,谁去做,什么时候做完。
产品开发规范和接口流程 :这个包括了《需求文档规范》、《程序代码规范》、《美术资源规范》、《外包资源规范》等各种产出的规范。同时也应该为每个工种或者岗位,都制定“入口”(接收任务)和“出口”(完成任务)的处理流程。产出规范可以利用前阶段的工作内容作为模板,而流程是那些实践中的经验和教训。
产品发布流程 :资源充分之后,测试和发布是首先应该被重视起来的部分。一般来说开发流程是经过“设计 - 编码 - 测试”这种顺序的,但是这种方法在投入运营期的项目显得过于“笨重”,而应该采用“发现需求 - 编写实现 - 发布 - 测试验证”的模型。在项目初创阶段,发布往往并不是核心的工作,很多时候测试是直接在开发环境里面做的,但是到了运营阶段,测试必须在专门的测试环境里面进行,因此“发布”工作必须成为一个必要的环节。这种运营阶段的开发流程,以通过测试作为项目开发的目标,而不是完成设计方案作为目标,节省了设计阶段的繁文缛节,更加重视对客户的质量保证。因此应该让测试、发布、客服三个流程统一结合,成为反过来输入到开发流程中的一条工作流程。发布流程是在小型项目中最容易被忽视的,也是现在最迫切需要建立的。
从项目上,准备长期作战则需要去完善“三板斧”:首先是自动运维部署工具,其次是统计监控管理,最后是代码层次划分。
因为互联网项目通常都是一边运营一边开发,如果开发人员变多,运维部署和版本管理的复杂程度一定会上升,而这一类工作原本是比较容易从代码开发中划分出来。做好这一步,还能进一步提高 “测试”工作的质量。对于运营中的事故“救火”,也是有很大好处。特别是能让开发团队从特种兵时代的高强度 7x24 超高工作强度,转换成日常工作的强度,这对于长期性运作有基础性作用。
另外一方面,监控和统计是两个非常容易被忽视的工作,因为这两个并非是客户需求,也和产品功能无关,因此常常在设计的时候压根就没有考虑。往往是在进入运营准备阶段之后,才在市场部同事多方央求之下,或者在老板的强烈要求之下,开发部门才会认真的做起来。
然而,一个不断运营的项目是否正常,完全需要有一个自我诊断的工具。如果不去做这个事情,反而会令正常的开发过程中被很多的“查问题”“看日志”“查数据”所打断。有很多“大牛”程序员,在项目上线,出于责任心和项目稳定的需要,被迫每天上班都去花很多时间看日志,以确保已经上线的产品正常。这就是对人力资源的极大浪费。还不如一开始就多花一点时间做好监控的功能和统计的功能。
我曾经参加过一个公司的重点项目,经过大概半年紧张的开发,产品最终上线了,但是这个产品的特点是用户需求很单一,但是用户网络环境对于这个产品的使用有很大影响,因此在随后的一年多时间里,我就被迫每天的根据用户投诉的情况,去搜集网络环境的数据,并且试图从海量的用户使用日志中搜寻问题的线索,找出可能有问题的网络区域报告给专门处理网络问题的部门。因为缺乏前期的准备,也没有安排足够的开发时间来做这方面的开发,我一直只能使用低效的工具做着重复的工作。
一般的小型系统需要增加的系统和工具:
构建工具 :需要准备一个专门的、干净的环境作为构建环境。构建环境可以是一个空白的虚拟机,也可以是一个很久的 PC ,但是最重要的是只用于构建,不得再有别的无关进程和服务。否则在构建环境中引入了问题,将会非常难查,这就是所谓“兼容性问题”的来源。另外还需要一套脚本工具,可以只运行一个简单命令,就可以直接从最新的代码库和配置库中,构建出最新的版本来,同时也不需要有保留备份旧版本的功能。自动构建脚本并不容易开发,但是若开发出来了日后工作中许多复杂的情况会变得比较好解决。实践中,开发团队有用 shell script 配合 make 开发自动构建工具的,也有使用 scons 脚本的,或者用 ANT 等其他工具开发的。另外 Hudson 也是一款不错的自动构建 CI 工具。
部署工具 :现代互联网应用都会部署在多台服务器上,而且每台服务器都有可能在运行着不同的进程。这种非常复杂的运行配置,以及代码的版本兼容问题,困扰着很多互联网服务提供商。因此专门抽调人员,针对多台服务器,专门编写部署程序,管理各种运行配置,是非常有必要的。而且还有一点,就是程序本身,就应该想办法降低部署的复杂性,比如 IP 地址自己读网卡而不是依赖配置;多个不同功能的进程使用同一份可执行程序,通过参数不同来区别启动等等。业界的自动部署工具有 chef 和 puppet 等软件可供选择。
监控工具: 项目变大通常意味着用户变多,更多的用户意味着需要更多的服务器,而这些更多用户和更多的服务器通常会导致运营事故的多发,维护工作也就变得更加困难。所以这就必须要有一套自动的监控工具,取代程序员每天主动上线去看日志,看负载。而这套自动监控工具,必须要和业务功能结合起来,才有意义。比如说对于业务数据的平均处理速度的监控,还有服务进程的运行状态。关于操作系统的监控项目就更多了,但是我始终觉得网卡流量和 CPU 负载的图形监控是最容易找到问题的,因为业务如果有问题,使用人数通常都会下降,流量就会随之下降。对于操作系统的监控工具现在网上很多可以找到下载,而业务数据的监控,则是更直接,也更有意义的,这种监控工具,也是应该安排开发团队着手编写。 JAVA 系统则可以通过 JMX 规范来制作监控插件,具有良好的规范性。
统计工具 :项目的日志初期都是比较散乱的,带有 DEBUG 信息的比较多。而正式转入运营后,需要的是更多关于业务数据的日志,以及这些业务数据的统计。因此必须要让系统能够灵活的加入更多的业务数据日志,并且一定要安排专门的服务器和程序来做日志的过滤、合并、统计、查询。一般来说这些工作都需要安排专人负责,对接市场和产品人员接受统计需求,然后在开发中不断完善统计工具。
代码层次的划分非常有利于多人协作,因此也是项目走向多人共同开发的重要事项,没有良好的代码层次,就不会有良好的程序人力运用基础。如果有一个比较好的代码层次结构,那么在代码中自然就会定义出很多“接口”,这些接口就是项目自身的 API 。这样水平较低的人员可以开发重用率较差的代码。而高水平程序员则专心于改进重用率更高的代码,这样在质量和效率两方面都能得到平衡。
很多人认为多人合作开发,注释和文档是第一重要的。实则不然,如果代码层次过于混乱,纵有再多文档和注释,也难以让新进项目的程序员加入进来。而且就算勉强开始开发,也会碰到很多“以前犯过的错”。
需要说明的是,对于软件架构或者代码结构的“重构”或者修正,实际上是跟随着整个项目一直进行的。
在软件行业发展到今天,软件系统架构已经积累了很多经验,并且形成了多个知识体系。学习和应用这些知识体系,具有非常重要的意义。其中一个重要的知识,就是代码设计原则:
开闭原则 :代码应该对修改代码关闭,对增加代码开放。这个是代码结构的最高目标,符合这种原则的代码重用性最高。
里氏代换原则 :父类可行的调用,子类一定可行。这个原则保证了代码在重用情况下的稳定性。
依赖倒转原则 :代码应该针对抽象接口编程,而不应该依赖实现的细节。这一点在 C++ 语言开发中尤为重要。
接口隔离原则: 针对每个功能组合定义一个角色,不要做那种大而全,但是功能多到人脑无法理解的接口。
最小知识原则 :尽量少的产生不必要的耦合代码
对于以上的开发原则,虽然看起来繁文琐节,却是代码重用的重要原则,因此有必要对既有代码在每一个可能的机会里进行重构。
另外如果项目的复杂程度已经需要进行整体性构造,那么可能还需要参考学习更多架构模式:
分层模式 :构架大型复杂代码,有效管理耦合性的方法
微核模式 :对于需要单独开发调试模块,所有效的代码架构
管道和过滤器模式 :对于构造高灵活性互相协作模块群,所需要考虑的架构
MVC 及其衍生模式 :对于 GUI 系统需要使用的架构
编写代码的知识、设计模式、架构模式这些软件开发知识,对于项目从小型转向更大规模,是至关重要的。因为这些技术会直接影响整个团队的开发效率。作为项目经理,如果不精通这些知识,很容易陷入无法推动程序开发进度的困境之中。推动和支持代码在结构上的进化、优化是项目经理必须要去做的功课,这些工作正如磨刀不误砍柴工一样,对于项目进度有极大的好处。软件工程类知识和项目管理知识,看起来是不相干的两个方面,但是实际上都是为了提高开发效率所创造的。他们的关系是互相促进互相关联的。
对于软件结构来说,抽象是一个最主要的划分工具,而抽象是否有效,并不是由程序员或者架构师的天马行空的想象力决定,而是由具体的项目,在不断的实践中,经过无数次用户沟通反馈,无数次需求变更的情况下,才能准确的做出来的。抽象本身就是开发团队对于现实项目的一种理解,这种理解会随着实践而不断逼近现实,没有人的洞察力高到完全不需要实践就理解一个事物,所以代码结构和软件架构的调整,就是结合了既有的模式知识和现实的需求变更经验,所做出最符合开发效率的一种结果。如果说项目经理在软件开发上的知识上升空间在哪里,我觉得这个地方就是一个无穷大的空间。好的项目经理往往是他做过的项目领域的专家或者是优秀架构师,而不仅仅是个婆婆妈妈的高级秘书。
往期精彩内容回看:
回复 服务器架构 ——经典游戏服务器架构概述
回复 程序员 ——如何提高程序员的生产率
回复 RPC ——如何设计一个RPC系统
回复 架构模式 ——经典软件架构模式
回复 游戏服务器 ——可复用的游戏服务器端开发框架
回复 技术总监 ——如何做一个小型公司的技术总监
感谢大家的阅读,如觉得此文对你有那么一丁点的作用,麻烦动动手指转发或分享至朋友圈。如有不同意见,欢迎后台留言探讨。