;

项目管理五部曲之计划

发布时间: 作者:系统管理员 访问量:166

软技能分享:权利源于争取。软件开发项目经理大多是从技术岗位晋升而来,很多人对权利的认知处于模糊状态,在这里提醒大家,争权夺利,权利是要主动争取的,而不是被动等待,争权的关键看方法,要避免造成急功近利等不良表现。在软件项目开发管理中,大多数管理者只是在已有的资源中寻求项目实现,而相对成熟或灵活的管理者,会将权利其进行深挖,从而在公司中脱颖而出。

在计划过程中,一般的软件公司的项目管理人员重点针对系统/版本制定开发计划,其他的内容基本上是忽略的,在软件项目管理中看上去问题不大,但是,对于管理者自身的管理能力提升,其实是有限制的,使其无法得到更大能力的发挥。

下图列出了计划管理的完整内容,在本章将详细说明各部分的可能会被忽略的地方。

计划管理内容

1、范围管理计划

需求确认:大多数软件项目都会提供需求文档、交互文档,用于和项目方进行反复核实和确认,在软件实现的功能范围一般不会有太大的问题,重点是做好确认工作。在范围管理计划中可以明确需求确认的周期、方式及参与者。

范围变更:世界上唯一不变的事情就是变。软件更是如此。因此,在软件项目的范围管理时,一定要确定变更管理过程和计划,与项目方共同确认当发生变更时,由谁进行评估、核准,如何变更项目进度,如何通知到所有参与方,如何计算变更费用及由谁承担等。

以上两项一般不会遗漏,但是,以下三点内容在实际执行过程中,通常会被遗忘:

合同管理:合同是项目最主要的输入内容,关系项目最终是否能够顺利交付的关键标准。在范围管理计划中一定要体现合同管理计划(有些项目在启动时甚至未签署合同),当项目范围变更时,要将变更内容体现在合同中(部分项目签署的是框架合同,项目需求文档作为合同附件存在)。在很多时候,由于项目方处于强势地位,合同相关条款中对于范围没有进行明确说明,如果此时销售不给力,很多时候会造成软件项目看似利润高,最终算下来,还会赔钱。

验收管理:在范围管理中,要对验收方、验收标准、验收时间和地点、验收环境等进行说明,确保项目可以按时、按质交付。对于软件项目来说,验收意味着回款(或一个迭代的完成),因此,在范围管理计划中要纳入验收管理计划内容。

运维管理:项目上线之后,由谁负责运维、运维内容包含哪些、运维周期确定等均需要在范围管理计划中体现,避免出现争议或混乱。

以下是一个项目工作分解结构(WBS)表,供参考。

 

项目工作分解结构表

注:初步范围确认和范围变更内容可以在合同管理计划中进行安排,需求确认可以在项目研发阶段进行。

2、时间管理计划

在进行时间管理计划制定时,关键有如下三点需要注意:

优先级:在项目过程中,工作有先后缓急之分,因此,要明确各事项的优先级。优先级可以根据功能、可用性、验收要求等进行确定。

关键里程碑:在项目执行过程中,所有影响下阶段工作开始的事项,就是项目的关键里程碑点。关键里程碑关系项目的整体进度,在计划阶段要向所有参与方进行明确和确认。

时间估算:在进行项目时间估算时,需要综合考虑人力资源情况、人员能力情况、技术及协调风险点等。一般我个人会在乐观时间的基础上增加10%—15%的缓冲时间。在时间估算时,最需要注意的是有第三方参与的情况,通常情况下,有第三方参与的时间会是预估时间的1.5倍到3倍的时间不等,主要是对交互接口、业务理解、沟通协调及彼此优先级不一致等原因造成。

3、成本管理计划

任何项目均需要制定成本管理计划,根据计划控制项目投入成本,对于软件项目来说,最大的投入成本是人力资源成本,其次是差旅成本,另外还有一部分来源于变更成本。

人力资源成本

通常软件项目经理都是根据项目团队/部门/公司现有的人力资源情况来安排项目,从现实情况来看,没有任何问题。但是,从项目角度来讲,这是对项目成本不负责任的表现。

 

软件研发成本控制方法

为了节省人力资源成本,可以通过如上三个方面进行考量。

技术:每做一个项目都应该考虑相应的内容,尽可能使项目成果多元化,除了整体可运行程序以外,一定要考虑代码的模块化、模板化,考虑整体是否可以实现框架化。另外,项目的过程资产也应予以整理,包括合同、文档、培训等。

外包:外包在国企和事业单位经常采用,一般的公司很少使用,其实,研发资源非常昂贵,尤其是无法连续使用的技术和人员,此时,采用外包是更好的节省成本的方式。

实习生计划:实习生计划应作为企业常规人力资源的一项工作,使用实习生可以节省非常多的成本,尤其在实现技术模块化、模板化和框架化的前提下。

差旅成本

    

差旅成本对于需要长时间进行本地运维或联调的时候是一件非常大的开支,此处有很多方式来降低差旅成本:

a、加强出差前的准备工作:例如可以进行远程调试、安装和测试;

b、招聘本地支持人员;

c、使用当地临时支持:例如第三方公司的员工等。

变更成本

变更成本的控制需要项目管理人员和销售市场人员共同做工作。在这里需要注意的是:

a、销售一定要全程参与项目,做好与客户的协调,很多时候变更不是坏事,关键是要产生变更收益;

b、做好前期沟通确认,需求尽可能细化,原型确认。任何后续的麻烦都来源于前期的懈怠。

4、质量管理计划

质量管理计划中重点是要明确各阶段的输入和输出内容的检查项(CheckList)和检查手段,要将质量管理落实到计划中。

一般我们都会进行软件质量管理,这里会疏忽的是文档质量的管理,由于文档质量引起的成本、客户投诉、工期推迟等情况非常多。

有条件的企业可以构建符合自身的PDCA质量循环体系,建立组织级的质量管理库,注意对人员质量意识和质量检查手段/工具的培养和积累。

5、人员管理计划

项目管理最主要的就是对人的管理。在软件项目管理中,人员管理计划最主要是需要明确所需要的人员到位和释放的时间点。

此处需要重点说明的有两点:

a、一定要建立项目级的RACI矩阵(各个公司可以对矩阵项自行定义,参见上面工作分解结构表的内容,里面没有定义审批人,增加了参与者),关于RACI矩阵大家可以百度一下。

b、确定项目骨干人员:一个项目或团队离不开骨干员工的支持,其他枝叶部分都可以随意变动,一般影响不大,但是,要确保骨干成员在项目开展期间的安全性(主要是离职风险),并能够真正发挥骨干员工的主观能动性(主要是激励)。

6、沟通管理计划

通常情况下,软件项目的沟通管理都是采用约定俗成的方式进行的,例如站会、周例会、季度会等,但是,最好还是制定一份沟通管理计划,明确在什么阶段、发生什么事情时、以什么形式、告知什么人,确保整个项目的沟通顺畅。

沟通计划表

再次附上这个沟通计划表,以作强调。

7、风险管理计划

风险管理要贯穿项目的始终,还记得上一章中在启动阶段强调的假定和约束条件吗?再加上本章中所提到的范围、时间、成本、人员等管理计划中所需要注意的点,这些就构成了项目风险管理的内容,将这些记下来,确定何时进行跟踪和确认,在跟踪确认之前需要准备哪些内容,然后形成项目整体的

8、采购管理计划

一般软件项目很少会用到采购管理,此处个人的经验也有限,但是,有一点是很明确的,前期做的努力越多,后续承担的风险越少。再次强调一下:任何后续的麻烦都来源于前期的懈怠。

这里有个分享的例子给大家:前两天一个同事要装修房子,有些不放心,总要要去盯着。一次吃饭,他问我买房装修的时候是否也盯着看了?我看了他一眼,淡定的说:没有。他说你真是放心啊。我接着跟他说:所以后续我交了很多额外的费用,墙皮脱落、卫生间漏水、橱柜下沉。他目瞪口呆。这就是因为前期装修时我省心了。我又问了他一个问题:你装修的时候物业找你麻烦了吗?他说:别提了,老糟心了,这个不让做,那个不让弄,买个沙子还得用指定的。我淡然的对他说:那是你买房的时候偷懒了。

9、计划变更管理

有些项目经理在完成计划制定后,会长出一大口气,似乎以为项目已经完成了。在这些项目经理中,还有一部分人,在制定完计划之后,从来再也不会看一眼这个计划,计划只是为了完成任务而做的,你们身边有没有这样的人

计划制定只是万里长征的第一步,后续还有一大堆的事情要完成。对计划的跟踪执行和变更是项目管理者要每天都要做的事情。

项目经理一定要:监督和落实项目计划执行情况,及时处理计划变更事项,实时更新项目计划。

软件项目管理其实一点都不比建筑类项目管理难度低,之所以会觉得容易,是由于我们还没有关注到项目的方方面面。有人说产品经理是CEO的学前班,我说CEO是项目经理的就业班。


最新发布

    FreeMarker template error (DEBUG mode; use RETHROW in production!): For "#list" list source: Expected a sequence or collection, but this has evaluated to an extended_hash+string (com.jmis.contentmng.pojo.Content wrapped into f.e.b.StringModel): ==> [in template "jmis/pm/article/detail.html" at line 185, column 13] ---- FTL stack trace ("~" means nesting-related): - Failed at: #list content as chann [in template "jmis/pm/article/detail.html" at line 185, column 6] - Reached through: @cm_content_list count="5" titleLen="... [in template "jmis/pm/article/detail.html" at line 179, column 8] ---- Java stack trace (for programmers): ---- freemarker.core.NonSequenceOrCollectionException: [... Exception message was already printed; see it above ...] at freemarker.core.IteratorBlock$IterationContext.executeNestedContent(IteratorBlock.java:285) at freemarker.core.IteratorBlock$IterationContext.accept(IteratorBlock.java:200) at freemarker.core.Environment.visitIteratorBlock(Environment.java:595) at freemarker.core.IteratorBlock.acceptWithResult(IteratorBlock.java:81) at freemarker.core.IteratorBlock.accept(IteratorBlock.java:67) at freemarker.core.Environment.visit(Environment.java:326) at freemarker.core.Environment.visit(Environment.java:368) at freemarker.core.Environment$NestedElementTemplateDirectiveBody.render(Environment.java:2727) at com.jmis.contentmng.action.directive.abs.TemplateDirectiveUtil.ProcessTemplate(TemplateDirectiveUtil.java:316) at com.jmis.contentmng.action.directive.ContentListDirective.execute(ContentListDirective.java:254) at freemarker.core.Environment.visit(Environment.java:426) at freemarker.core.UnifiedCall.accept(UnifiedCall.java:101) at freemarker.core.Environment.visit(Environment.java:326) at freemarker.core.Environment.visit(Environment.java:332) at freemarker.core.Environment.process(Environment.java:305) at freemarker.template.Template.process(Template.java:384) at com.jmis.contentmng.common.web.springmvc.SimpleFreeMarkerView.renderMergedTemplateModel(SimpleFreeMarkerView.java:153) at org.springframework.web.servlet.view.AbstractTemplateView.renderMergedOutputModel(AbstractTemplateView.java:167) at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303) at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1286) at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1041) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:984) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at com.thetransactioncompany.cors.CORSFilter.doFilter(CORSFilter.java:209) at com.thetransactioncompany.cors.CORSFilter.doFilter(CORSFilter.java:244) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:123) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at com.frame.core.cache.PageEhCacheFilter.doFilter(PageEhCacheFilter.java:102) at net.sf.ehcache.constructs.web.filter.Filter.doFilter(Filter.java:86) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449) at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365) at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90) at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83) at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:383) at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362) at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at com.frame.core.web.filter.XssFilter.doFilter(XssFilter.java:20) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:673) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1526) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1482) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:748)