xiyuan技术圈

溪源的技术博客,技术的不断进取之路


  • Home

  • 技术

  • 随笔

  • 读书

  • 管理

  • 归档

深圳敢为软件技术长沙研发中心招聘.NET工程师(前后端)

Posted on 2019-10-28 | Edited on 2021-04-25 | In 管理

深圳敢为软件技术长沙研发中心招聘.NET工程师

招聘岗位1:.net初级研发工程师

任职资格:

1、大专及以上学历,计算机或其他相关专业;

2、1年以上相关工作经验,能够进行独立设计与开发工作;

3、熟悉C#/Java语言编程,了解Asp.NET、ASP.NET Core架构开发;

4、熟练掌握SQL SERVER、Mysql数据库进行开发;熟练掌握WEB开发相关 HTML、JavaScript、DIV+CSS、AJAX、JQuery 或者熟悉Vue,Angular等前端开发或了解WPF、U3D、WebGL开发;

5、具有良好的基础知识和优良的编程风格和习惯,并有软件工程概念;有独立项目经验优先;

6、有冲劲,肯学习,想往物联网、智能家居等新技术方向发展。
待遇5-7k

招聘岗位2:.net中级研发工程师

任职资格:

1、全日制本科及以上学历,计算机或其他相关专业;

2、3年左右相关工作经验,能够进行独立设计与开发工作;

3、熟悉C#/Java语言编程,熟悉Asp.NET、ASP.NET Core架构开发;

4、 熟练掌握SQL SERVER、Mysql数据库进行开发;熟练掌握WEB开发相关 HTML、JavaScript、DIV+CSS、AJAX、JQuery 或者熟悉Vue,Angular等前端开发或熟悉WPF、U3D、WebGL开发;

5、具有良好的基础知识和优良的编程风格和习惯,并有软件工程概念;有独立负责项目的经验;

6、有冲劲,肯学习,想往物联网等新技术方向发展。

岗位职责:

1、按项目计划完成系统分析、设计、编码和内部测试;

2、能够在架构师的帮助下独立完成项目需求调研、分析、设计;

3、及时跟进及反馈工作情况,并根据实际情况提出改进建议;

4、进行文档编写以及用户培训等工作。
待遇7-13k

招聘岗位3:.net高级研发工程师

任职资格:

1、全日制本科及以上学历,计算机或其他相关专业;

2、5年左右相关工作经验,能够进行独立设计与开发工作;

3、熟悉C#/Java语言编程,熟悉ASP.NET、Asp.NET Core架构开发;

4、熟练掌握SQL SERVER、Mysql数据库进行开发;熟练掌握WEB开发相关 HTML、JavaScript、DIV+CSS、AJAX、JQuery 或者熟悉Vue,Angular等前端开发或熟悉WPF、U3D、WebGL开发;

5、具有良好的基础知识和优良的编程风格和习惯,并有软件工程概念;有独立负责项目的经验;

6、熟悉Three.js、对U3D,IOT,WPF有经验或.NET Core 容器化和基于Kubernetes 的云原生应用开发经验优先;

7、有冲劲,肯学习,想往物联网等新技术方向发展。

岗位职责:

1、按项目计划完成系统分析、设计、编码和内部测试;

2、能够独立完成项目需求调研、分析、设计;

3、及时跟进及反馈工作情况,并根据实际情况提出改进建议;

4、进行文档编写以及用户培训等工作。
待遇10-18k

联系人:18973108440 。(溪源)
地址:梅溪湖金茂悦31楼,猪八戒众创空间

怎样的项目,才能称为“成功项目”?

Posted on 2019-10-09 | Edited on 2021-04-25 | In 管理
  • 引子

这个故事讲的是一家拥有百年历史的制造业大厂的信息化转型过程中的波折。这家企业拥有超过三万名员工,它是某行业的领先品牌,但是在信息化程度上却非常古老。
有许多人说,国企的项目都是坑,相当一部分项目都是为了骗经费而忽悠人的,换一拨管理层,基本上就得换一拨项目,油水全被领导捞走了。我很遗憾,没能经历这些事情。这个项目实打实都是做出来给基层用的项目,也确实获得了各方较高的满意度。
以中台为政治正确的互联网开发者们会说,这个项目从商业层面和技术上来说似乎毫无价值,首先它不是互联网产品,其次它没什么技术含量,什么大数据,云计算,容器,微服务,无服务,它都不需要,这甚至有点像我们身边许多人十年前就做过的项目,技术上平淡无奇,看起来毫无挑战。
显然并非如此,每个项目存在都有价值。作为过程中的亲历者,我希望通过总结这个项目过程中存在的不足,引起思考和引发讨论。尤其是随着时代的进步,像这样信息化转型的传统制造业企业,或许越来越罕见,这样的项目看起来非常独特,但也是软件项目中的一个典型,对于未来从事行业互联网开发的软件从业人员来说,也许能带来一些思考。

  • 背景

2015年开始,制造业企业开始流行起一种新的趋势:工业4.0,在这个大背景下,国内则提出了中国制造2025的计划,旨在通过几年努力,实现中国制造业的智能化水平在上一个台阶。
该大型国企作为该某领域的领先品牌,其生产的产品远销国内外,也是军民融合的典型代表,但是在过去的发展过程中,由于产品本身比较特殊,许多关键步骤仍然是采用手工完成,无法完全实现自动化替代,包括检验管理体系也同样建立在一大堆纸质表单的基础上完成。集团管理层看到了中国制造2025带来的巨大机遇,也想奋斗一波,但要做的事情实在是太多了。
康威定律说,一个组织的软件架构设计,实际上是组织管理形式的体现。在9012年的今天,为了维持这样一套以纸质表单为核心的检验管理体系,需要维持庞大的管理体系,带来了巨大的管理成本,对于组织来说本身成为了一种巨大的负担。更何况这种管理形式,问题实在是太多了。
1,需要花费大量的时间和精力来维持现有组织,哪怕是非关键岗位的离职,都可能影响一些流程的开展。(纸质资料的交接,其实跟口口相传没什么区别)。
2,过程资料存储困难,为了存储资料,更是需要专门安排档案室和档案保管员,一旦出现产品质量问题,需要进行问题追溯时,要从档案中检索问题要花许多时间。
3,一旦发生灾害或事故,例如火灾洪灾,就意味着过程资料将成为废纸,产品的关键过程信息将无法寻觅。
在互联网技术飞速发展、实体经济受到巨大冲击的今天,原有管理模式,过于臃肿,已经无法适应组织发展的需要。行业越来越不景气,企业的管理成本居高不下,负担越来越重,每年裁员裁了许多人,却没能从管理效能上带来多大的提高,因此提升企业的信息化管理水平其实势在必行。
但是如果要实现一步到位,直接构建基于工业4.0的智能制造新体系,在这个行业都不太现实,毕竟许多产品都是属于定制生产、小批次制造,部署全套自动化制造生产线成本非常高,事实上新生产线也在建设,但是一直没有投产,老的生产线也得维持好,这样才能持续创造价值。做好眼前能做的,实现检验管理的信息化(无纸化),然后再逐步实现制造业的智能化,只有实现了这一小步,才有未来的无限可能。

  • 立项

之前提到中国制造2025的大背景,还有一个小背景。企业内部企业的中高层普遍都是八十年代末毕业拥有更高学历的管理层,不乏清北的高才生。他们那些大学同学正是这一波大时代的受益者之一,有许多同学都借助于企业的腾飞实现了个人价值,但是留在这家国企的高管们,却看着企业越来越差,也想有所作为,却感觉一个拳头打在了棉花上,他们的压力很大。
然而,大家都清楚,传统国企的体质僵化,很多时候看起来两个字所谓“变革”,实际上却有点像个美梦而已,就拿“信息化”系统建设来说吧,信息化对于国企来说并非第一次提起,许多国企的老员工,乃至管理层其实已经经历了过去十年信息化失败之苦,许多信息化系统在领导们的支持下,看似搞得轰轰烈烈,热热闹闹,但是最终都难逃被抛弃的下场,而每个信息化项目的失败,都会让好几位负责对应信息管理工作的牵头人背锅,并最终从公司离开。
在IT从业人员看来或许很简单的信息化建设,从来没那么顺顺利利,极有可能会由于基层的抗拒最终失败,并给信息化建设的参与者带来职业生涯上的污点。
于是在国企把信息化提上议程之后,获得了两种不同的声音。很戏剧性的,不懂信息化的业务部门是信息化的支持者,他们说这个事情一定要搞,砸锅卖铁都要上,再难也要上。另外一种负责信息化工作的信息中心的负责人对信息化的态度很明显,要我支持,没问题,我可以把网络做好,但是要我参与软件的研发过程,恕我无能为力。
因此最终牵头组织这个过程资料信息化的,是平时对这一块需求最大的检验管理部门。在他们的推动下,项目正式提上了议程。
这是个涉及面非常广泛的项目集,包括了三个项目,分别是面向民用领域单一事业部的A项目,面向融合领域的B项目,以及面向整个集团全方向的C项目。事实上集团曾经购买过现成的产品,但是都没能用起来,所以这个项目集最终是以定制开发的形式承包出来。

  • A项目

A项目作为最先立项的项目,万事开头难,承受了巨大的压力,许多人在等着看一出好戏。
A项目的负责人W部长今年三十八岁,在此之前他一直是从事检验管理出身,对产品检验有自己明确的要求,他奠定了整个项目的基调。没有设定特别宏大的目标,不是做一个通用的大系统,就做一个简单实用的系统,能够给基层带来方便,为管理层带来便利就够了。
项目的早期,承建单位曾经试图引入新技术和更加友好的用户体验,但是w部长却不这么认为,他说在这样的老国企,没必要用新技术,一切以满足可用性为目标,操作越简单越好。于是最终承建方设计了一个基于cs的数据采集系统和基于bs的管理系统。不仅功能尽可能的简单,而且连流程都能省就省,尽量让参与者减少学习成本。
这个设计简单实用的系统,恰好满足了敏捷项目所要达到的构建简单项目、实现快速迭代小步快走的管理目标。驻场开发期间,及时发现问题和解决问题,让软件得到了非常充分应用,获得了甲方的高度评价。
而且如果说早期项目的应用的推进需要靠组织的强势推动,到了后期就令人欣慰了,由于这个系统无需特别复杂的操作流程就能完成资料的录入,提交和汇总,基层岗位的工作人员只要会用计算机、简单培训就能迅速上手,无形中为项目的快速铺开奠定了良好的基础。而且承建方与甲方的基层人员之间沟通融洽,有问题及时反馈,及时处理,为项目推进带来了非常不错的效果。
在项目运转正常一年之后,主导项目工作的部长调任了,新接手的负责人是曾经不太看好这个系统的车间主任,他也被基层工作人员带动,成为了这个项目的拥趸。

  • B项目

借着A项目成功的东风,马上启动了B项目。
前面提到了,B项目是面向融合产业的,因此对这个项目的要求也相对而言更高。
当这个项目完成立项后,B项目建设方的组织架构也发生了比较大的变更,首先是之前推动A项目的集团公司大领导由于工龄届满,已经退休,而集团的整个管理层都相继发生了变化,包括推动项目立项的一位高级工程师也从公司离职,意味着项目早期干系人已经完全换了。
组织架构调整其实是为了给年轻人机会。调整后,B项目负责整个项目的,都是87后的年轻人。
这群年轻人们,相对于A项目的负责人来说,对信息化有了更加深入的认识。事实上而言,A项目建设,其实不过是一个类似于协同办公的一个模块,他们认为这个项目没什么技术含量,甚至有点low。他们经常出去考察学习,对用户体验和功能应用都有很多想法,他们渴望真正打造一个能够真正打造一个符合企业未来发展目标的信息化一体化平台。
这些想法再跟现有政策、以及组织架构混合在一起发酵,最终变成了一个无比巨大的项目,用专业术语来说,就是一个“大泥球”系统。
在项目立项之初,合同上原始需求其实只有19个条目,而且为了让这些内容容易落地,前期的推动者甚至尽可能的减少流程的复杂度,但是等合同签订后、组织结构调整完,新的干系人们在原合同的基础上,对范围进行了大幅修改。最终等需求调研落地,变成的是一个基本上涵盖了公司大部分审批事项、涉及几十个流程,涉及全公司数百人、超过十几个小系统的大项目。
为了完成这个项目,双方投入了最少20个人,超过十人的研发团队驻场开发了超过半年时间,也没折腾出几个让甲方满意的东西。建设单位现场条件恶劣、涉及流程复杂、涉及特殊的管理机制,要开发的功能点特别多,不断的完善,不断的修改,整个项目研发前前后后折腾了至少一年时间之后,才通过直接负责人的认可,但是给领导汇报时,却根本不可用,得推翻重做。
项目到今年项目依然没有上线,这离合同交付日期已经超过两年了,显然可以称之为“失败项目”了。
承建方虽然早期意识到了项目风险,但是建设方过于强势,无法拒绝;建设方贪多求快,忽略了组织本身的复杂性、低估了软件研发的工作量以及软件实施的难度,最终双方都竹篮打水一场空。

  • C项目

C项目是在B项目完成了到一半时启动的新项目,其目标是将A项目成功的经验,推广到全集团。(不包括B项目的事业部)。
C项目的实施异常顺利,合同期一年,实际上只花了8个月就把项目做完了。

  • 总结

当我年轻时曾经向其他拥有丰富经验的项目经理请教什么样的项目才是成功项目时,他们说:

“刚刚好”:刚刚好满足客户的需求,就是一个不错的项目。

一个充满智慧的回答。不过“刚刚好”其实很难把握这个度,我觉得一定是功能简单、实用即可。
在这个故事中,那个最简单实用,几乎没有什么技术含量的项目在组织中获得了很大的成功,而一开始就打算做成全流程信息化的系统,最终却一败涂地。
这恰好就像我们经常见到的项目,简单纯粹的产品才能最先站稳脚跟、获得不错的用户反响,那些一味画饼的PPT项目,都是忽悠人,几乎没几个成功了。
除此之外,每一个软件行业从业者,越爱钻研技术,越容易陷入技术的迷思,总以为靠技术能改变世界,仿佛一个项目没用上什么新技术就容易就无法体现出自己的价值来。然而一个组织的技术体系,必须依托组织的实际组织架构而存在。再优秀超前的技术,脱离了土壤都是空中楼阁,永远没有最优秀的技术,能真正为组织带来价值的技术,才是最合适的技术。

一位996\Crud开发者平凡的一天

Posted on 2019-10-09 | Edited on 2021-04-25 | In 随笔

记一笔流水账

今天我打算记一笔流水账,主要记录我的一天中干的事情,并思考效率低下的原因,同时分析一些可用的解决方案。

清早·开始做计划

早上六点四十,被梦想唤醒,然后看一会书,吃早餐,送娃上学。
九点来到公司,开始一天的工作。在工作开始之前,我会花五分钟先做一个当天的计划,大概是这样的。

  1. (讲道理应该有每日站会,事实上我是xx项目的负责人,但是。。我把站会给省了,把站会取消对项目的危害非常大。后期再讨论)
  2. 对xx项目的周计划进行跟进和修订。
  3. 检查昨天完成的功能,并记录和指派bug。
  4. 整理文档,对昨天完成新功能的特性进行说明。
  5. 解决属于自己名下的bug。
  6. 开始两个下一阶段需要交付的新功能,比较简单的业务接口,代码行预计不超过80行。

这些任务中,除了第五项和第六项相对来说可能会耗时比较长外,其他每个单项任务基本上可以在25分钟内完成,而且也确实是按任务优先级和重要性顺序来安排的,看起来还挺合理的,总体上属于在8小时内可以完成的工作量,而且其实或许还略微有点不饱和。。。

执行任务(下面是流水账,可以略过)

于是我喝了一口水,开始完成第一项任务:对xxx项目的周计划进行跟进和修订。
(如果是周一,以前我还会根据上周完成情况对月计划和总体计划进行适度的总结,但是。。自从来到互联网公司后,我把这个好习惯也丢掉了,好吧,是因为假装要敏捷要拥抱变化,所以把总体计划和月计划省掉了)。
但是,当我开始处理这项事务时,计划外的第一件事情发生了。在测试环境下,客户端反映某接口出现了不该出现的问题,于是我被迫打断这项任务,花了一分钟时间,跟他对接口问题进行了检查,发现是对方参数传错了。
嗯。问题解决。继续开始刚刚的任务。
到哪里了?哦。。还在做计划,接着我迅速调整状态,花了几分钟就把任务完成了。
然后开始第二项任务。
这时,刚刚客户端又找我了,他说接口还是有问题。
我以为又只要花一分钟,事实上这次我花了30分钟,因为确实是原来的代码逻辑中存在缺陷,需要进行代码修改、然后发布、再测试代码。
确认这个问题已经得到解决后,还是处理之前搁置的任务。花了20分钟处理任务3。
开始处理任务4,这项任务也相对来说比较简单,所以不到五分钟解决了。
开始处理任务5。。。在我名下共有20个bug。。。以每个bug5分钟来衡量,我大概需要花100分钟才能解决。但是当我开始解决第一个bug时。
又有其他人开始找我了,运营开始找我,说xxx场景下似乎出现了xxx逻辑不对。
线上问题必须优先解决,赶紧的,仔细思考问题发生的条件、对链路服务进行跟踪和分析、查看半年前编写的代码逻辑,最终花了15分钟分析出问题,并花了10分钟将问题妥善解决。
继续开始修复bug。在bug修复的过程中,发现是产品逻辑存在缺陷,于是跟产品对任务进行进一步明确、梳理业务、设计更加具体细化的流程。花了1小时。
到中午12点,我上午共完成任务3项,修复了一个bug。
下午不属于问题的高峰期,但是又发现了产品逻辑之外的一些其他问题,最终解决了15个bug。
积压了5个bug,留到晚上来解决吧。
当夜幕降临,我需要花2个小时来解决我剩余的bug和2个未完成的新功能开发任务。
事实上等到晚上八点半时,我完成了剩余bug,新功能完成了一个,但此时效率已经差的不行了,没办法,硬着头皮也得完成今天的任务。
(会不会欠下新债,显然毋庸置疑)
晚上9点,所有任务已基本上圆满完成。

总结完成情况

总结今天完成的任务,共完成任务五项,其中修复bug20个,写了60行新代码,共耗时10小时。
显然我的工作效率是很差的,尤其是晚上效率更差,我最佩服那些自称晚上效率很高的人,尤其还有一些人特别喜欢深夜撸码,倒上一杯小酒,借着凌晨的寂静,写着爱写的代码,他们很厉害,因为他们很会自欺欺人。
来统计当天完成工作的工时占比:
| 工作内容 | 时间(分钟) |
|:—-|:—-|
| 梳理日计划 | 5 |
| 修订周计划 | 10 |
| 接口联调 | 31 |
| 运营对接 | 25 |
| 修复20个bug | 250 |
| 编写新功能 | 120 |
| 日常项目沟通 | 120 |
| 其他 | 40 |
| 总计 | 601 |

图片

问题分析

以上流水账实际上是我们这样一家普通互联网公司的日常,当然,对我个人而言,实际上投入到运营对接中的时间相对来说是不算多的,我了解我们公司有的开发者每天需要花至少3小时与运营人员进行问题的对接,这显然会直接影响了开发者的工作效率。
(我相信一流互联网公司一定不是这样的)
从上图可以看出我们的日常工作安排存在以下问题:

  • 修复bug这种还技术债的任务,耗时接近47%,占了将近一半的时间。嗯,能力确实不行,能不能采取措施让债不欠这么多,这是人才三角(专业技能、行业知识、软实力)需要达到的目标。我曾经打算在功能开发中引入TDD来减少返工率,但是最终决定还是先搁置这个想法。
  • 我司项目管理的形式是虚拟团队,产品经理和测试工程师主要在深圳,而研发团队在长沙,因此,每天投入到团队沟通中的时间占比达到20%。事实上虚拟团队这种开发模式,作为目前比较新兴的项目沟通形式,已经被互联网公司广泛采用。但是虚拟团队成员间分处异地、无法面对面沟通,由于文化、工作节奏、技术等原因,容易造成比较大的沟通成本。可以采取以下措施进行优化:
    • 1、打造高保真原型图,进一步拆解任务目标,让任务目标细分。
    • 2、需求讨论时间前置,需求的特点是渐进明细的,应尽量将对需求的沟通在研发阶段开始前进行落实,减少对于研发阶段过程中的额外时间浪费。
    • 3、快速冲刺阶段尽可能面对面沟通。
    • 4、功能交付缺乏Desktop Check,意味着产品经理和测试工程师无法及时了解功能的实际开发情况,也意味着团队间对于成果的交付进度、实现方式,本身是存在疑问的,这将提高沟通成本。
  • 如果按每天工作十小时,为3小时为与运营沟通问题的解决来算,占比达30%。说明对于项目成果的交付上,依然存在不少可以优化和提升的空间。或许可以采取以下措施。
    • FAQ文档的进一步细化。
    • 知识共享。
    • 项目成果移交本身需要有更加规范化的管理措施。

结论

以上是一位CRUD互联网996开发者的一天,看起来似乎过得很充实, 却依然需要通过加班来完成当天的任务,而且甚至长期工作时间大于10个小时,与体力劳动者本身没有太大区别。也许总是像机器一样活着,思考都成为一种负担。总以为靠蛮力可以解决,实际上输出的或许是一种无用的解决方案。这样的付出,大概会觉得毫无价值。
然而我们必须停驻脚步,认真思考当下的价值,思考效率和意义的平衡,让我们的生活更加有意义。
牢记准则:“做正确的事,正确的做事”。

坚持一个好习惯,从现在开始

Posted on 2019-10-09 | Edited on 2021-04-25 | In 读书

引子

这段时间以来我都在坚持写博客,但是更新得比较少,每周才能一更,不过好歹也勉勉强强也能坚持下去了。
我们长沙.NET技术社区还组建了一个博客互勉群,不过除了个别人能经常打卡外,大部分人都是混个脸熟,凑个热闹。
在这篇博客中,我打算分析不能坚持写博客的主客观原因,也以此作为自省,同时也期望能给读者们带来一些思考。


1、我的灵感,就像天际的浮云

人们在那里高谈阔论着天气和灵感之类的东西,而我却象首饰匠打金锁链那样精心的劳动着,把一个个小环非常合适地连接起来。 ——海涅

坚持靠灵感来写博客,似乎是每个刚刚开始写博客的人的通病。灵感就像街角稍纵即逝的少女,邂逅的时光只有那么一念之间。
一旦灵感来了,俨然一代军师附体,嗯,指点江山、激扬文字、以键盘为沙盘、以文字为千军万马、随随便便洋洋洒洒大几千字不在话下,还可以针对某个主题写好几个连续的博客,写完了之后都意犹未尽,好生痛快。
但是,灵感这个东西,也太不靠谱了,有时候你还没来得及抓住,她就转瞬间消失的无影无踪,然后写博客就成为一件无比困难的工作,又要花时间,又要花精力,想得头昏目眩还不能敲出几个字来。


2、抓不住的时间流沙、留一点给我写博客吧

年难留,时易损。

时间太少,确实是不能写博客的一个主要原因。写一篇博客确实需要一点点时间,从开始写到发布,怎么也需要几个小时吧。尤其是涉及到使用代码编写的算法实现的博客,这种技术博客往往耗时很长,从开始思考问题,到设计算法流程,到使用代码实现,再调试、测试,然后加到博客中,排版,大概得半天小时,如果遇到一些技术问题,可能一天就结束了。当然,每每这样精心设计的博客都会受到读者的欢迎,这些高端博客,由于其门槛比较高,往往会让许多读者受益匪浅,更是成为许多公司基础代码中非常宝贵的部分。不过这种博客其实非常少,有时逛一天园子都难得出现一次。
作为一个996的开发者,每天属于自己的可支配时间非常少,每天早上六点多到七点起床,然后到公司九点,晚上9点下班,回到家十点,再洗簌,到睡觉前,大概只有不到一小时的时间学习新的技能,又如何能抽出时间来写博客?
当然,如果不上班,难道有时间写博客了吗?并没有,周末了,带孩子玩一玩,把孩子哄睡,闲暇时再吃个鸡,呀,一拍大腿,又晚上十点半了,又没时间写博客了。每天就是这样的循环播放,每天都在做同样的事情,不停的工作。
为了生活而拼搏、等有空的时候去学习,但是每天永远只有24个小时,不管怎么勤奋的去完成目标,却或许永远也抽不出那么一段闲暇的时间来写学习、去写博客。

3、天马行空、无处放飞的年轻人的遐思

优秀开发者们的想法,总是容易天马行空、肆意放飞,这也是年轻人们最充满朝气的一个客观表现。实际上到了写博客这个问题上,也是真实存在的。
例如、经常时不时会冒出许多的想法,有时看了一篇不错的文章、与作者心有灵犀,有时解决了一个不错的问题,这些都会让我们有感而发,然后,嗯,赶紧记下来。但是一旦到了要写的时候,又是毫无头绪,不知从而下笔;例如担心这篇博客写了之后,太在意其他人的看法会造成某些不太好的影响,怕自己写的博客泄露了在某些方面的不足。
于是,每一次写博客都成为开坑之旅,最终笔记中,留下了一堆只有标题、没有内容的奇怪东西。

4、过分追求眼前的回报

我也跟优秀的开发者、微软MVP汪宇杰老师交流这个问题,我问他是从什么时候开始的?他说大概是读大学时开始的,当时是从写笔记开始、后来才逐渐的开始写博客,最开始也担心博客写得不好,也同样会被人嘲讽,但是他依然不畏人言,一直坚持写,到目前已经坚持了十几年。他也运营了一个自己的公众号和网站(edi.wang),这个网站几乎每周都会.NET相关的技能,而且由于是英文撰写的,获得了来自大洋彼岸大量的阅读量。
张善友老师也说运营社区、运营他的公众号(dotnet跨平台),他坚持了四年,同期与他一起做.NET的公众号还有许多,但是只有他坚持了最后。为了这个公众号,他每天需要花好几个小时时间去搜集素材和整理文章,而且每篇文章他都会认真阅读和记录,积累了目前的几万用户,已经成为.NET技术圈公众号名副其实的第一大号之一。
汪宇杰老师总结道:

有的开发者认为,与通过短期拼搏获得高工资相比,通过博客这种方式还是太慢了,写博客大概就像种竹子,每天要不停的浇灌,持续五年才能获得回报。


5、破解之道在哪里

在《程序员的思维修炼》这本书介绍了德雷福斯模型这种从新手到专家的能力成长模型,描述了我们能力发生演进的原因。而如果把当写博客也当作一种能力,大概也可以把写博客这种技能分成五个层次:

  • 1、新手:是指学习某个技能一开始的阶段,包括未入门的外行人,这个阶段往往经验很少,需要借助于网络或其他人的指导才能完成。在写博客这个技能上,大概就是能够基本的梳理出自己的思路,能够把字数凑出来,形成一篇读得通的博客。新手由于缺乏经验和表达能力,这也让他们总是过份在意其他人的看法,一旦被人质疑几句,可能就患得患失,不敢继续写下去了。

  • 2、高级新手:有一定的经验和自我解决问题的能力,表现在写博客这个事情上,大概就是能够作用灵感这种天赐的魔法把自己的博客写出来,但是却很不稳定,有灵感就能写,没灵感憋都憋不出来。不太能控制自己的法术,文笔质量参差不齐。

  • 3、胜任者:感觉笔者即将达到这个层次,能够有效的运用灵感,写出一些文字,也能在没有灵感时,运用自己的积累或经验,或素材把博客写出来。

  • 4、精通:处理文字游刃有余,具有全局思维,能够从更高的战略层面思考问题和解决问题,并通过博客灵活的表现出来。

  • 5、行业专家:凭直觉驱动,笔力雄厚,不轻易发表自己的观点,但是每每发声一定能直击问题的要害,并影响一个领域。

    许多朋友吐槽之所以不写,文笔不好也是一个重要原因,不必苛求文笔和辞藻,尤其是技术类型的博客,鲜有使用了许多修辞手法的博主,大部分博客都只是用直白的文笔流畅的表达自己的观点。
    吴军老师在《浪潮之巅》(第二版·下部)中的最后一页这样写道:

    他要感谢他的语文老师….几乎所有的中学生,为了让文章写得漂亮,常常冥思苦想编造动人的清洁,寻找华丽的辞藻。我曾经也是这样的一个人,但是我的这位语文老师用了两年都的时间彻底改变了我的写作方法,他让我关注内容,用朴实的文风表达自己的体会。….我在美国的导师库坦普教授,他训练了我讲话和写作的逻辑性,比如怎么立论,并用论据支持论点。他是一位细节大师,要求我做到在公众场合讲话时,不多说哪怕是一句废话,也不落下任何一句关键的花。其实要想写得好,首先要说得清楚。然后再用大家喜欢的语言,把要说的话描述出来,就形成了好多作品。

写博客是开发者一项非常重要的技能,作为新手的我们,也不要自怨自艾,人总是要慢慢进步才能逐渐走向理想丰满 ,不管写什么内容,请记住,一定要坚持。
写博客也好、IT行业也好,贵在坚持。共勉之。

《猎豹行动·硝烟中的敏捷转型之旅》读书笔记

Posted on 2019-10-09 | Edited on 2021-04-25 | In 读书

简介

在9012年的今天,已经很少有人没有听过敏捷了。但敏捷真能解决这样的问题么?毫无疑问不太现实。毕竟中国式敏捷的笑话,也不是第一天出现在世人面前。许多公司都曾经实践过敏捷,却最终由于各种原因无法执行下去,水土不服是这些西方管理思想在国内最常见的问题。
而刘华老师也是在这样的背景下编写了这本书《猎豹行动·硝烟中的敏捷转型之旅》,这本书的出版在敏捷社区掀起了一番波澜。与其他介绍敏捷方法的书长篇阔论的介绍方法论不同,这本书以一个小说体的形式介绍了一个金融公司盛远金融的敏捷转型之旅,这一个个的小故事,既有受挫、又有成长,最终在这家企业中完成了转型,实现了劳动生产力的解放和软件研发实力的腾飞。
这家企业是如何做的?让我们跟着作者的笔触一步步走进这一幕幕吧。

角色介绍

作者引入了几个角色,分别是来自咨询公司的思域咨询公司的咨询顾问王章、曾经在某电商企业担任过管理层的盛远CTO思文、技术经理李俊、资深PMO关杰、项目总监张丽等主要角色。

  • 王章:敏捷顾问,有丰富的敏捷实施经验,对新思维、新方法狂热。
  • 思文:公司敏捷的推动者,执行能力强,面对困难从不抱怨。
  • 李俊:IT部门经理,严谨、沉着,对新思维、新方法保持谨慎。
  • 关杰:部门利益捍卫者,对敏捷保持怀疑态度。
  • 张丽:敏锐、务实,思维严密。

李俊是一位经验丰富的技术管理者,对项目管理拥有丰富的经验。他总是善于制作严密的计划,并能做到很好的把控。在过去的职业生涯中, 他在客户和业务部门的口碑非常好,他一诺千金,总是能做到按时交付。但是在这背后,却是压榨得最为凶残。团队的流动性也非常大。他是一位瀑布模型的践行者,虽然听过敏捷的概念,但是却认为敏捷是在为不做计划和不写文档找借口。他认为项目成功的关键靠的是强大和严密的计划能力、项目跟进能力和沟通能力,并努力实现客户的承诺。在此之前,他也经历过组织变革,但是这些变革往往雷声大雨点小、要么与实际严重不符,最终并没有带来什么实际的好处。
而作为敏捷顾问的王章,作为盛远金融敏捷转型的实践者,也充分得到了组织授权,感受到了思文对于敏捷的热忱,渴望在这家公司创造一番事业。事实上敏捷很容易获得基层的认可,不仅仅是因为敏捷不写文档,而是敏捷倡导组织间的相互信任、自治以及通过技术手段例如自动化测试来取代繁文缛节的文档。他很快就根据公司的实际情况建立了具体的启动方案,包括全面扫盲、体察民情、教育客户的三大步骤,并获得了思文的认可。

问题剖析

随后,王章给全体IT同事进行了一次全面的培训,从以下四个角度介绍了敏捷转型的具体过程。

  • 从传统模式的问题(剖析瀑布模型的适应局限以及给业务部门和IT部门带来的痛点)、
  • 转向敏捷(什么是敏捷?它与瀑布模型最大的区别在哪里?具体方法和价值观是怎样的?)、
  • 实施敏捷的好处(包括对业务部门和IT部门的好处)、
  • 如何开始(具体的行动)

在培训过程种,他也与大家一起总结了各部门的痛点,而在项目做的过程,根据业务部门的需求,虽然会给出估算和计划,但是在项目开始时,却只有预算和目标交付时间是确定的,很多因素都存在不确定性,包括:范围和具体需求、可能的需求变更、人员不可控、估算的准确性、对现有系统的影响、环境搭建等。
这些正是瀑布式模型带来的典型特点,事实上瀑布模型非常适合确定性非常高的项目,而这样的项目几乎是凤毛麟角。面对软件开发过程中的不确定性,需要采取措施管理和适应,真正实现“正确的做事”“做正确”的事。
随后的培训中,王章介绍了敏捷的价值观和方法论,获得了非常不错的反馈,大家都对实践敏捷的过程充满了期待。

万事开头难

作为一家金融科技的公司,对信息安全有着近乎洁癖的追求,因此工具的选型尤为重要,是选择商业软件,还是选择开源软件,往往都需要经历一番波澜。几经周折之后,选择了比较常用的敏捷管理工具,例如JIRA、Confluence、Github、Nexus、Jenkins、SonarQube、Ansible等工具。并建立了一套完整的DevOPS流程。
随后开始挑选第一个实践项目,【信鸽】。这是一个计划工期为8个月的项目,但是由于公司项目的典型特点,需要由PMO进行需求调研和业务分析,等来到李俊的项目研发团队手中,已经只剩下六个月的时间。而李俊这边由于项目的特殊性,已经腾不出额外的资源,最终只能招聘到一位临时软件工程师,事实上这时已经只剩下不到五个月的时间。
但是这个项目依然没办法按照敏捷的流程拆分迭代周期,主要是由于项目的需求文档由许多个条目组成,每个条目就是一个功能,但是仅仅按照功能进行拆分,几乎无法独立开发、测试和上线交付事实上拆分出来的东西,单个部署都没有业务价值。而且前期采用瀑布模型进行需求、设计而后面的开发、采用敏捷,最后的测试采用瀑布模型,显然这样的效益确实有限。
最终这个项目只能采用瀑布模型继续开发。需求文档的完成和签署花了三个月,然后在花一个月涉及外部文档,两个月开发、一个月完成测试,一个月用户验收测试,然后上线,正好赶上8个月的时间。
然而现实是残酷的,最终项目延期三个月结束。

越挫越勇

虽然经历了一轮挫折,但是却并未让年轻的咨询师就此放弃,他想起了曾经学习了解过的极限编程,同时又引入了kanban的精益软件管理的工具,然后将其引入到项目中。然后让李俊的项目团队采用看板的来跟进新功能需求的研发和流程的日常优化。
而随着日常流程优化这种常规功能的研发的逐渐开展,也让团队成员对于敏捷有了更深刻的认识,在新项目开发过程中,李俊的研发团队将Scrum引入其中,完成了一次原本看起来不可拆分、不可妥协的功能开发,并获得了公司高管的认可。
在后面的项目研发过程中,又经历了几次不同的挑战,但是也让敏捷的产品研发过程逐渐在公司生根发芽,逐渐发展状态,最终成为公司的常态管理形式。

总结

成功的项目千篇一律,失败的项目各有不同。
无论是互联网公司还是传统的软件公司,为了创造独特的产品、服务或成果而发起各种不同的形式的项目是行业的普遍选择。
如果说项目的成功与否,取决于组织的管理形式本身,实际上也取决于项目经理对项目的掌控力。优秀的项目经理不仅具备的优秀的专业技能、行业知识和软实力,让他能够灵活的驾驭各种不同类型的项目还能游刃有余,而普通型项目经理却往往耗费了大量资源,最终还会让项目陷入一座又一座的泥坑不可自拔。
对于软件研发型项目经理来说,选择合适的开发模型,似乎是首要考虑的问题。当然,毫无疑问,最为深入人心的项目开发流程,莫过于瀑布式模型。这是一种种增量式开发模式,历经从计划=》需求分析=》软件设计=》软件编码=》软件测试=》软件部署=》软件验收的各个环节。各个环节间既相互依赖,又可能相互迭代。
图片
一环套一环,很更容易就陷入死循环的怪圈。例如,我们很容易就想到瀑布模型存在的以下缺点:

  1. 项目前期耗费大量的时间进行需求调研、编写了一大堆写完就过期的文档。
  2. 软件交付时,大量层出不穷的bug和需求变更。
  3. 客户对软件更改和研发的脑力支出并不认同等。

我也经常听到项目经理们的吐槽。尤其从医疗行业的项目经理那里听到了最多的吐槽。在过去若干年的发展过程中,医疗信息化领域的发展特别快。但是由于医疗卫生行业涉及的领域太广,所以让标准化产品的研发过程变得非常困难,现在依然有许多医院的信息化系统都是以定制开发为主。而这些实施定制化开发软件的公司,承受的巨大压力常人难以想象。不同的院系、不同的医生对需求的不同理解或者各种需求上的变化,总是让开发者来回倒腾而无可自拔。上次就听说长沙某大型HIS企业的技术总监,为了给客户填坑,直接倒在了医院的办公室中,还好处理得当,不然还不知道会带来什么后果。
除了HIS领域外,制造业甲方爸爸也擅长给乙方掘墓。他们的技能是甩锅。由于流程众多、涉及的人数广,所以要确认需求是一件非常困难的事情。这种情况下,除了绞尽脑汁应付其中,根本没有更好的办法。关键是他们中许多人还被免费软件的迷魂药给迷晕了头脑,总是认为软件开发不过是简单的码格子,肆意的扩大需求范围、更改需求,开发出大量华而不实的功能,让程序员们费力不讨好。
这些项目也是采用瀑布式开发模型的典型,试图通过前期严密的需求调研、功能设计和验收流程让客户尽可能少的变更需求,实际上却很难真正做到完全可控。所以项目很难避免不延迟,最终给公司带来了不小的负担。
在这样的背景之下,似乎敏捷是一缕曙光。
但究竟该怎么实施。这本书给出了一点参考。

独立开发者,程序员们的美好梦想?

Posted on 2019-10-09 | Edited on 2021-04-25 | In 随笔

在IT行业或任何行业,总有一群特别优秀的人,他们游走在公司体系之外,不靠工资为生,靠自己的能力从雇主手中承接项目来生存,这种“自由职业者”,在开发者体系中,被称为“独立开发者”。

一

不久前,某路人甲找我内推。
甲:溪源,之前看到你分享过一些求职信息,恰好我最近私活没那么忙,你可以把我的简历代投一下么?
溪源:我这边主要是帮别人发了点招聘,我没办法帮你代投的。不过你可以告诉我你想找怎样的工作,我帮你留意一下。
甲:我靠私活每个月有万把块钱收入,就想找一个没什么压力,做一些简单增删改查的工作。我觉得我做企业信息系统挺厉害的,你可以帮我推一些这样的工作。
甲:待遇要求嘛,应该像我这样八年经验的,一万多还是拿的到的。
溪源:互联网公司有兴趣么?
甲:没什么兴趣,经常要加班,压力比较大。我觉得可以找找那些外包公司之类的,做企业内部系统的就算了,业务流程太复杂,纠缠不清。
溪源:你会什么技能呢?
甲:.net三层架构和ado.net我玩得很溜了,ef了解一点点,其他技术就不太会了,项目不需要。
然后我找了一位猎头小仙女,介绍了他说的情况。
小仙女回答说,我也想找这样月薪过万,没有什么压力的工作。而且他的技术栈会不会有点太。。我担心我的雇主看不上。
然后没有然后了。
……

二

在七月左右,我有幸组织了一次读书会,后来拉了一个读书群,里面有一位开发者同样也是独立开发者,目前主要使用go语言进行开发,对vue等技术尤其擅长,关键是这些技术都是他自己这两年学会的。他不仅学得快,能够很快的获得收益,还经常去朋友的公司,以讲师的身份给他们公司的同事做培训。每年的收入挺可观的,基本上都两万+每月。他说自从成为独立开发者以后,比以前更拼了,压力更大了。
如松原来在上海从事金融科技产业链的软件研发,前几年回到长沙,找了一圈工作,都找不到特别满意的工作,毕竟国内只有北上广深杭才有金融科技应用场景的公司,于是跌跌撞撞之下,由于经常有人找他开发独立应用或企业软件,这也是一个不错的选择,于是,他也成为了独立开发者。经历了一年多,也拉了三五条枪,撸了一个小团队,业内口碑也颇为不错,勉强能维持住团队的运营。问及他创业的心得,他说,以前上班是为老板赚钱,现在是为自己赚钱,虽然不见得能发大财,但能更加灵活的选择自己想做的方向,能够获得长期积累。
当然,也有做出不同选择的,例如【ASP.NET MVC 企业级实战】的作者邹琼俊老师曾经说过,出版了第一本书之后,就经常能接到各种不同的项目,但是他都拒绝了,主要还是这些项目都看起来简单,但要花很多时间,如果全职还能勉强做下去,但是如果业余时间来做,看起来能获得收入,但是就没时间学习了,所以他宁可花时间多看看书,写写书。于是他又出版了一本新书【H5+跨平台移动应用实战开发】。(他把业余时间全花在看书写书上了,哪里还有时间做私活)
历史是如此的相似,微信公众号【吃草的罗汉】作者王晔倞老师曾经在他的公众号中分享过十多年前他做私活的经历,靠给别人做私活,他曾经获得了不亚于职场的收入,零几年初在上海买车买房,这份收入助力颇多,而且这份工作激励他钻研技能,并为他的职业发展带来了许多帮助。后来他停下了这个过程,专注于职场主业,并获得了今天的成绩。

三

似乎在每个程序员的心中,都有一颗成为独立开发者的美好梦想,在这个梦想里面,可以脱离公司规则的束缚,选择自己想过的生活,想学习时,随时随地拿起书,靠着窗户边就可以看书,偶尔接一些私活,辛苦一下很快就能获得收入,岂不美哉。也确实有许多人靠这个能足不出户就赚到不亚于职场的收入,于是又激励了他们把这份收入当作长期收入,最终或许可以借此脱离企业管理的桎梏,获得心灵上的自由。
我们来总结一下独立开发者的优点:
1、更加灵活自由的安排自己的时间,能够实现收入的最大化。在职场中,往往需要牺牲自己的时间来加班完成领导的指标,并可能看起来导致利益受损【毕竟上班会影响你赚钱】,而独立开发者则可以根据自己的时间选择适合自己的任务,并有时候能够更好的谈好工钱和工期,能够小赚一笔。
2、有机会塑造自己的个人品牌价值。市场竞争如此激烈,能够接到稳定的私活,往往需要更好的口碑,这其实就是在打造自己的个人品牌。
3、按需提高和学习,在技能的广度上有更好的优势。也许你能适当的选择一些看起来没那么难的挑战,来学习其他在职场上学不到的技能。
4、有可能打破圈子,获得不同的人脉关系。脱离职场人脉圈子的单一化,你有可能认识更多的朋友。
当然,也存在许多缺点:
1、最合理有效的工作时间依然是每天白天的八小时工作制,但是由于为了所谓自由可能会习惯于晚上完成工作,甚至有可能习惯于昼伏夜出,让生物钟紊乱,进而导致身体的长期损害。
2、长期脱离职场发展,看似不被束缚,但是职场的管理制度,其实是一种规则,虽然企业不同,规则不一定相同,但是其核心目标都是为了实现对企业参与者更好的管理,更好的实现企业劳动生产力的提升,而脱离了职场,再回归职场时,会主观上对规则形成抗拒心理,进而难以适应。
3、 虽然在职场容易造成人脉结构的单一化,但是脱离职场却不利于自身领导力和管理能力的积累,这些都是非常有价值的软实力。事实上在当今时代,单兵作战已经越来越困难了,独立开发者则往往不太可能培养管理能力。
4、技术上可能会更趋于保守闭塞。虽然可以看似根据业务需求灵活的调整自己的技能发展方向,但是往往雇主只会选择对自己利益最大化的承包商,不大可能给资源或时间让乙方来学习,这客观上也容易让开发者陷入内卷化的境地。

四

仿佛有两种截然不同的情况,一种是自律能力和学习能力特别强的开发者,从他们选择成为独立开发者开始,他们的职业生涯才算真正的开始,在这个以自我价值实现为最大价值的新平台,他们自我刷新,像创业者一般走在了新职场的康庄大道上;另外一些人则恰好相反,也许短期内有项目,并能让他们短期收益颇多,但是很容易就内卷化了,失去了核心竞争力,成为廉价劳动力的输送者,甚至还会导致他们很难再回到职场。
无论如何,独立开发者其实是一个特别需要自控能力的就业方向,如果一旦过于放纵,反而更容易被边缘化。
当然,哪怕你在职场又如何?也同样需要提高自控能力。等着别人来给自己机会,等别人来让你实现价值的观念?往往都是有机会时,缺乏能力,有能力时,机会又会离你远去。
不等不靠,才是王道。
这不是在输出焦虑,只是在探讨职场发展。欢迎拍砖。

《实例化需求》读书笔记

Posted on 2019-10-09 | Edited on 2021-04-25 | In 读书

背景

一直以来我都是从事软件研发、兼顾项目管理工作,在项目开发中,总是会遇到一些各种各样的问题,我也曾试图在项目中推动过类似于敏捷这样的项目管理方法,但是最终却收效不大。随着我工作经验的逐渐累积,发现不同的项目、不管是敏捷还是非敏捷、不管是如何失败的,其中对于用户需求的把控都是一个非常关键的因素。

用户需求,就是笼子里的狮子,你不放出来,受伤的是狮子,你放出来,受伤的是你自己。

在项目过程中,我们倾向于输出一份沉甸甸的需求文档,但是这份需求文档等项目做完了,往往会过时而用处不大。由于无穷次变更或开发者们对于需求的理解层次不同,设计业务领域的项目经理,显然不可能对软件的实现部分进行全方位的掌控,对于一些比较大的项目,掌握全局就已经很不错了,更不用说代码了,然而一切业务都凝聚在代码中,这些代码是如何实现用户需求的?
—-取决于开发者的经验,科学表明,一切靠经验来实现传承的知识体系,都是魔法。

概述

前段时间,我听张逸老师《领域驱动设计实践》课程中了解到《实例化需求》这本书,于是下单购买了,期待能获得一些启迪。
《实例化需求》是一本介绍软件工程方法论的书,这本书没有源代码、也没有特定工具的使用说明。在这本书中,他收集了来自现实世界、真实的团队以及切实的经验,同时也引入了一些建议,他希望通过这本书能够帮助大家使用这些组织过程或模式去建立活文档系统,从而提高软件项目的可控性。
在传统的项目开发中,尚且可以要求每个产品持续很长的时间,例如,一般都是以月为单位,甚至以年为单位。但是在互联网开发中,交付时间往往被压缩到一周为单位、甚至以天为单位,所以大量的前期软件设计和详细的需求分析环节将不复存在,而且传统的手工测试也耗时漫长,也被更加科学的自动化测试所替代。在这样的背景下,文档随时会过时,因此使用Axure原型软件设计高保真的UI交互原型是普遍的选择,如果有需求变更,就对原型进行修改,然后再通过原型推动功能的变更。
在作者的官方网站上,他认为《实例化需求》是一组过程模式,可以协助软件产品的变更,确保有效地交付正确的产品。 这里所说的正确产品,指的是该软件能满足客户或企业用户提出的商业需求,或者能达成预定的商业目标;同时它要具备一定的灵活性,未来能以相对平稳的投入接受后续改进。
作者认为,要有效的构建正确的产品,传统的软件开发实践首先需要满足一下几点:
1、保证所有项目干系人和交付团队的成员都对需要交付哪些东西有一致的理解。
2、有准确的需求说明,这样交付团队才避免有模棱两可和功能缺失造成的无谓返工。
3、有用来衡量某项工作是否完成的客观标准。
4、具有指导软件团队或团队结构变更的文档。
而基于Axure进行设计再推动职能团队完成功能开发的模式,看似满足了上述目标,避免了过度的需求文档造成的浪费、也能够很好的解释用户需求,但是却更关注于用户交互过程、无法涉及面子之下的里子、返工和功能维护让需求的验证过程变得越来越不可控,终输出的软件产品,顶多只能算一个花里胡哨的绣花枕头而已。
由于软件产品每周都要交付,作者认为,比较合理的解决方案应该带来如下好处:
1、避免过度说明需求从而产生浪费,避免花时间在开发前会发生改变的细节上。
2、有一种可靠的文档,可以解释系统的行为,据此我们可以很容易的修改系统行为。
3、可以有效地检查系统行为与需求说明的描述是否一致。
4、以最小的维护成本维护文档的相关性和可靠性。
5、适合短迭代和基于流的过程,这样能为即将开展的工作提供技术足够的信息。
这也是实例化说明渴望达到的目的,作者对许多企业进行了访谈,总结了实例化需求的应用案例,他认为通过引入实例化需求可以达到以下收益。。
1、更有效的实施变更。作者将其中的最重要的经验称为活文档(living documentation)的广泛应用是有长期受益的。这本书广泛涵盖了这一部分的内容。文中如是说

“活文档是系统功能的一个信息源,它与程序代码一样可靠,但易于使用和理解。它能帮助团队共同分析变更所带来的影响并讨论潜在的方案。团队还可以为新的需求扩展已有的文档,长此以往,可以使得需求说明和实施变更更有效。”

2、更高的产品质量。由于预期明确,验证变得更有效率。
3、更少的返工。由于需求写作更好,使团队确保对预期达成共识。
4、同一项目不同角色的活动协调得更好。改善写作形成定期的交付流程。

关键过程模式

作者总结了在实施实例化需求中的一些关键过程模式:
1、从目标中获取范围。许多软件在开始之前,期望由客户、产品负责人、商业用户来确定工作的范围,而商业用户明确他们的需求之后,软件交付团队就依次实现,但是由于用户不是专业的软件设计师,因此他提出的需求,固然是他所要求的,但并非是他真正需要的。所以关键模式就是从目标中获取需求,用户专注于传达所需功能希望达到的目的和期望由此带来的价值,而交付团队则致力于提出解决方案。
2、协作需求。不依赖于个人去收集正确的需求,而是与商业用户一起协作指定解决方案,这可以让我们充分利用团队的知识和经验,还创造了需求的集体所有制,让每个人都能参与到交付过程中。
3、举例说明。在编程语言实现过程中,成功的团队不会等待需求被精准表述,而是举例说明需求。用一些额外的实例用于说明边界情况,或重点标识出系统中某些特别有问题的地方,这样可以清楚功能分歧和不一致的地方,避免由误解或解释不到位导致的返工。
4、提炼需求。关键实例必须精简才有用,通过移除多余信息并为开发和测试过程创建一个具体、精确的上下文。软件的目标是该做什么,而不是该怎么做。
5、自动化验证时,不改变需求说明。
6、频繁验证。通过频繁验证,团队可以快速发现系统与需求说明之间的任何差别。团队成员可以更好的与用户交流,并决定如何处理。他们可以不断地同步系统和可执行的需求说明。
7、演化成活文档系统。活文档系统告诉系统可以做什么,并与代码一起确切可靠,但具有更好的可读性。

问题思考

这本书与其他介绍软件理论的书籍一样,知识量比较稠密,阅读过程中花费了不少时间,如豆瓣上有网友说的:

读一本书,如果能给你带来一点感悟,那就赚回书钱了。如果带给你两三点感悟,花费的时间也就值了。如果带给你五六点,甚至七八点,那就可以为作者捐款以示感谢了。**。**

这本书也是大概如此,书的重点其实是引入行为驱动开发(BDD),并让需求变得可执行的过程,让这套方法论实践起来大概需要花费更长的时间,但是也可以成为软件设计者们思考的一个方向。
个人认为,引入Cucumber这样的自动化测试工具,使用行为驱动设计的方法进行软件需求的描述编写活文档、然后再将其引入到测试过程中,或许有点难,但是也可以尝试使用这样的分析方法来设计我们的需求。
尤其是当需求不明确的时候,如果使用原型分析法或其他描述方法来定义需求,往往可能不够具体,则可以考虑使用图表或实际例子来描述,并创造性的给角色取一些花名,可以让需求的描述过程变得更加的生动。


1、《https://www.infoq.cn/article/specification-by-example-book/》《实例化需求》采访与书评。

关注细节,成就卓越代码

Posted on 2019-10-09 | Edited on 2021-04-25 | In 技术

开篇

  • 我们总是很容易就能写出满足某个特定功能的代码,却很难写出优雅代码。又最欣赏那些优雅的代码,因为优雅代码更能体现一个开发者的积累。
  • 就像写一篇散文,有的就像初学者不得其门而入,遣词造句都非常困难,然后纠纠结结,最终不了了之。或者啰哩吧嗦,看起来说了一堆,其实就像是村妇闲聊,毫无重点,不过是口水文而已。
  • 好代码应该是这样的,如涓涓细流、如同一首诗,一篇优美的故事,将作者编写代码时的情感慢慢铺垫开来,或是高潮迭起,此起彼伏,或是平铺直述,却蕴含道理。我始终相信优秀的代码是有灵魂的,代码的灵魂就是作者的逻辑思维。
  • 编写整洁代码 or 非整洁代码,就像平时生活中是否注意爱护环境的一点点小习惯,一旦坏味道代码没有及时处理,就会成为破窗效应,然后逐渐的代码越写越烂,最终这些代码要么以重构收场,要么就被抛弃。
  • 我们见过太多没有毫无质量可言的代码,许多时候开发者们由于能力原因、或者时间有限,写了许多能够满足当前工作的代码,然后就弃置高阁,不再理会。于是,代码写之前的只有自己和上帝能理解代码的意思,而写完了之后,只有上帝能懂了;还有一些开发者说:我只会写代码,不会优化代码,他们仿佛特别勤奋,每天都会比其他人都热衷于熬工时,但是写出的代码,实际上是一个个难以维护的技术债。而且许多代码的作者总喜欢找各种借口来抵赖,例如喜欢说代码出了问题都是底层框架太垃圾了、或者别人的代码封装得太差。他们总是抱怨这抱怨那,但是即便有优秀的框架、技术,就一定能写出优秀的代码么?
  • 在这里笔者列举了平时看到过一些自认为不太整洁的代码,以及与《代码整洁之道》(Clean Code · A Handbook of Agile Software Craftsmanship)一书中相对应的范例,欢迎大家一起来拍砖。
  • (经验有限,时间仓促,请轻喷。)

    一些栗子

  • 1、命名规则
    • 1.1 变量命名和方法命名

在我们刚刚开始学习写代码的古老时代,或许会有下面这种习惯。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/// <summary>
/// author:zhangsan
/// </summary>
class ZhangsanTest
{
    private void TestGetData()
    {
        int a, b, c;
    }
    private int ZhangsanGet(int s1, int s2)
    {
        int s3 = s1 + s2;
        return s3;
    } 
  private List<string> GetData()
    {
        return null;
    }
}

这是一个喜欢用自己的姓名来命名类和方法的作者,在他的代码中,经常可以看到这样奇怪的对象定义,而且他还喜欢用a,b,c,d,e,f或者s1,s2这样的命名,仿佛他的代码自带混淆特效。这样的代码嗅起来会不会觉得充斥着奇怪的味道?
另外,有没有发现有许多开发者喜欢用 GetData() 来定义获取数据的方法?然后这个方法就成为一个万金油的方法,不管是爬虫采集、或者数据绑定,无论是 C# 写的后端或者 Java 写的后端代码,或者用 vue 写的前端代码,仿佛在任何场景、任何数据应用都可以看到这样的方法。
如果一个项目中,有十几个地方都出现了这个** GetData() **方法,那种感觉一定非常难受。

  • 1.2 Model、Dto 傻傻分不清楚

随着技能的增长,或许我们会学到一些新的代码概念,例如,Model、DTO 是经常容易弄混淆的一种概念,但是在某些代码中,出现了下面的命名方式就有点令人窒息了。

1
2
3
4
5
6
public class XXXModelDto
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Alias { get; set; }
    }

这是大概是一位对概念严重消化不良的资深开发者,居然同时把 Model 和 DTO 复用在一个对象上,
(当然,一个开发者定义变量的背后一定有他的动机)。
他到底是想要的是用来在 MVC 模式解决数据传输和对象绑定的模型对象?还是用于传输数据的 DTO 呢?
–其实他定义这个对象,是为了定义存储数据对象的实体( Entity )。

  • 1.3特殊情况术语和字段对照表非常重要

近年来开发者素质越来越高,所以许多优秀开发者会倾向于使用翻译软件来翻译变量名,然后用英语来命名,但是即便如此,许多政务项目总是能嗅出一些奇怪的味道。
例如前不久看到一条这样的短信:(原图已经消失)

1
xxx公积金中心提醒您:您于{TQSJ}日进行了{TQCZ}操作,账上剩余金额为{SYJE}元。

这是个bug将xxx公积金中心的某些秘密透露在大家面前。作为一个严谨的项目,居然使用中文首字母大写命名法,这让习惯于大驼峰、小驼峰的我看了之后尴尬癌犯了,很不舒服。但是这也是许多政务信息化项目的中字段命名的规范,而且在这种情况下,往往会输出一份非常规范的数据库字段对照表,确保中文和首字母的语义不让人产生歧义。
所以特定语境下,变量和方法本身没有严格的规定,但是一定要使用恰当的语境概念,对于这样的特定场景,尽量维护一份实时更新的术语表吧。

  • 2、状态码返回值
    • 2.1业务逻辑状态码

似乎在对外提供接口时,使用下列接口状态码是一种比较常见的惯例。提供统一格式的 code 状态码以及返回的消息和成功返回结果时的填充数据,能够让开发者高效的完成接口对接,无需关心http状态码背后的含义。

1
{"code":"100101","message":"success","data":{},"count":""}
  • 2.2用 http 状态码为什么不够?

上面这是一种经典的流派,还有一种流派则会使用http状态码来返回指定的数据,事实上 http 协议本身已经提供了许多状态码,例如下面的这些大家都非常熟悉的状态码。
但是这些状态码为啥不够?主要是为了减少前后端、服务上下游之间接口对接的难度,也是一种提高效率的方式。 但是 http 状态码是一种通用的格式,应尽量使用这种方式,而不应该通过解析正常响应后的 json 来判断是否正确操作。

1
2
3
4
5
6
7
8
9
10
200 :正常响应 标准成功代码和默认选项。	
201 :创建对象。 适用于存储行为。
204 :没有内容。 当一个动作成功执行,但没有任何内容可以返回。
206 :部分内容。 当您必须返回分页的资源列表时很有用。
400 :请求不正确 无法通过验证的请求的标准选项。
401 :未经授权 用户需要进行身份验证。
403 :禁止 用户已通过身份验证,但没有执行操作的权限。
404 :找不到资源自动返回。
500 :内部服务器错误。 理想情况下,您不会明确地返回此消息,但是如果发生意外中断,这是您的用户将会收到的。
503 :服务不可用 相当自我解释,还有一个不会被应用程序显式返回的代码。
  • 3、switch 语句与判断语句
    • 3.1 面向过程式或面向对象式

我曾经跟小组中一位大佬交流他的一段代码,他的这段代码大概是这样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/// <summary>
/// 流程处理
/// </summary>
public void FlowProcess(int auditType)
{
  switch (auditType)
  {                
      case 1://通过
          //此处省略通过场景下的50行代码
          break;
      case 2://不通过
          //此处省略不通过场景下的50行代码
          break;
      case 3://再审通过
          //此处省略再审通过场景下的50行代码
          break;
      case 4://再审不通过
          //此处省略再审不通过场景下的50行代码
          break;
  }
}

(读者卒。)
且不说这位大佬的代码是写得好或者不好,仅仅就这200多行代码的4个大switch读起来大概会让人便秘难受吧。于是在我读完这段代码之后,我冒死向他请教这么写代码的原因,他说我这个流程处理就是一个简单的用例场景,哪里还有什么可以优化的余地?
我跟他介绍了20分钟代码封装的必要性,于是,他把代码写成了这样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/// <summary>
/// 流程处理
/// </summary>
public void FlowProcess(int auditType)
{
    switch (auditType)
    {                
        case 1://通过
            AuditOK();
            break;
        case 2://不通过
            AuditNotOK();
            break;
        case 3://再审通过
            ReAuditOK();
            break;
        case 4://再审不通过
            ReAuditNotOK();
            break;
    }
}
public void AuditOK()
{
    //此处省略通过场景下的50行代码
}
public void AuditNotOK()
{
    //此处省略不通过场景下的50行代码
}
public void ReAuditOK()
{
    //此处省略再审通过场景下的50行代码
}
public void ReAuditNotOK()
{
    //此处省略再审不通过场景下的50行代码
}

这酸爽令人简直难以置信。(事实上这个新鲜出炉的遗留应用,正是这样一点点堆积了许多总代码行超过千行的类文件)
《代码整洁之道》书上有一个类似的例子,大概与上文类似,Robert 大叔给出了这样的建议:

1
对于switch 语句,我的规矩是如果只出现一次,用于创建多态对象,而且隐藏在某个集成关系中,在系统中其他部分看不到,就还能容忍。当然也要就事论事,有时我也会部分或全部违反这条规矩。

上文我给出的示例,有点像面向过程的代码风格,而 Robert 大叔在他的书中写下的示例是这样的(抽象工厂模式的示例)。
图片
这清爽的感觉,让人很舒服啊。

  • 3.2 孰优孰劣?

当然,原示例是一个流程处理的例子,似乎大家的流程处理代码都习惯于使用这种面向过程风格的写法,反正要加判定条件,就加一个 case 就可以了。
而在某些特定情况下,甚至用 if / else 来写逻辑判断更简单,于是我们经常在某些销量很好的快速开发平台中,看到这样的例子。
图片
这些典型的面向过程风格的代码,确实读起来似乎更加简单、而且也易于实现。

1
2
Robert 大叔是这样说的:过程式代码(使用数据结构的代码)便于在不改动既有数据结构的前提下添加新函数,面向对象代码便于在不改动既有函数的前提下添加新类。
反过来讲也说得通:过程式代码难以添加新数据结构,因为必须修改所有函数,面向对象代码难以添加新函数,因为必须修改所有类。

所以究竟是使用面向过程式代码,还是面向对象式代码?没有万试万灵的灵丹妙药。

  • 4、奥卡姆剃刀定律、得墨忒耳律
    • 4.1“如非必要,勿增实体”

一旦开始初步掌握面向对象开发的基本原则,于是我们就会新建许多各种不同的模型对象。尤其是在webapi接口开发过程中,更是如此。

切勿浪费较多东西去做,用较少的东西,同样可以做好的事情。

  • 4.2 得墨忒耳律

假设有一段代码是这样的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public  class GrandParent
{
    public Father Son { get; set; }
    public string Name { get; set; }
    public Father GetSon()
    {
        return Son;
    }
}
public class Father
{
    public Me Son { get; set; }
    public string Name { get; set; }
    public Father GetSon()
    {
        return Son;
    }
}
public class Me
{
    public Son Son { get; set; }
    public string Name { get; set; }
    public Son GetSon()
    {
        return Son;
    }
}
public class Son
{
    public GrandSon GrandSon { get; set; }
    public string Name { get; set; }
    public GrandSon GetSon()
    {
        return GrandSon;
    }
}
public class GrandSon
{
   public string Name { get; set; }
    public string GetSon()
    {
        return Name;
    }
}

会不会为了获得某些数据,而写出这样的代码呢?

1
return new GrandParent().GetSon().GetSon().GetSon().Name;

这样就是典型的对得墨忒耳律的违背。这个原则指出:

1
2
3
4
5
6
7
模块不应了解它所操作对象的内部情形。
更准确的说,得墨忒耳律认为,类C的方法f只应该调用以下对象的方法:
C(本身)
由方法f创建的对象。
作为参数传递给f的对象;
由C的实体变量持有的对象。
对象不应调用由任何函数返回的对象的方法。换言之,只跟朋友说话,不与陌生人说话。

在上文中我举的例子,祖父只跟自己的亲儿子(Father)说话,而不跟孙子说话。

  • 5、圈复杂度

在软件测试的概念里,圈复杂度用来衡量一个模块判定结构的复杂程度,数量上表现为线性无关的路径条数,即合理的预防错误所需测试的最少路径条数。圈复杂度大说明程序代码可能质量低且难于测试和维护,根据经验,程序的可能错误和高的圈复杂度有着很大关系。
据说在Oracle数据库中有一些屎山代码,是通过一堆标识量来判断某些特定逻辑的,大概是这样的。
(示例仅供参考,由于资源限制,未能考证,还请大佬指正一二。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/// <summary>
/// 一个高复杂度的方法
/// </summary>
public string HighCCMethod()
{
    int flag = 1;
    int flag1 = 2;
    int flag2 = 3;
    int flag3 = 4;
    int flag4 = 5;
    if (flag == 1)
    {
        //do something
        if (flag1 == 2)
        {
            //dosomething
            if (flag2 == 3)
            {
                //dosomething
                if (flag3 == 4 && flag4 == 5)
                {
                    return "编译器 die";
                }
            }
        } 
    }
    return "...";
}

这是一个圈复杂度非常复杂的方法,我想任何一个读到这样代码的开发者都会对自己的人生充满了积极而乐观的判断,那就是“活着比一切都好”。
对于这样的代码,我们应该尽可能的降低代码的圈复杂度,让程序满足基本可读的需求。

  • 6、注释
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public void UploadImg()
    {
        int flag = 3;
        //标识量为3标识什么意思我也不知道,我在网上看到的。
        if (flag == 3)
        {
            //dosomething
        }
        //uploadfile();
    }

我曾经参加过一个使用objectc编写的应用的,其中有一段代码是这样的,这个flag大概是魔法值,作者未经考证直接就在代码中使用了。然后一直流传下来,成为一段佳(gui)话(hua)。
还有这样的注释。傻傻分不清楚。

1
2
3
4
5
6
7
8
/// <summary>
/// 为true标识为真,为false标识为假
/// </summary>
public bool IsTrue { get; set; }
/// <summary>
/// 是否可见,为true标识可见,为false标识不可见
/// </summary>
public bool IsVisible { get; set; }

还有这样的。

1
2
//do something
Thread.Sleep(3000); //项目经理说此处要暂停3000毫秒,以便作为下次性能改进的需求点

Robert大叔如是说:

1
什么也比不上放置良好的注释来得有用。什么也比不会乱七八糟的注释更有本事搞乱一个模块。什么也不会比陈旧、提供错误信息的注释更有破坏性。

当然很多中国程序员自称其变量命名是自注释的,例如大概是这样的。万能的 Is 命名法,只要是判断状态皆可用。
(每个程序员能够成功的生存下来都不容易,他一定有异于常人的本事。)

1
2
3
public bool IsShow { get; set; }
public bool IsGet { get; set; }
public bool IsUsed { get; set; }
  • 7、霰弹式修改

CRUD开发者或许经常会看到这样的代码,例如,如果我要对某一个对象的状态( Status)进行更改,可能会这么做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ShotGun1
{
    public void Method1()
    {
        DataStatus dataStatus = new DataStatus();
        dataStatus.Flag = 1 * 3;
        dataStatus.Status = "1234";
    }
}
public class ShotGun2
{
    public void Method2(int i, int status)
    {
        DataStatus dataStatus = new DataStatus();
        dataStatus.Flag = 1 * 3;
        dataStatus.Status = "1234";
    }
}

这种霰弹式代码中,一处代码规则的变化,可能会需要对许多处代码进行同步修改,使得我们的代码异常的难以维护。

  • 8、异常

有时候可能会遇到这样的代码,在方法中定义一些文本的状态码,然后调用方法时,再去判断这个状态码的内容,当返回错误码时,要求调用者立即处理错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class XXXApi
{
    public CurrentUser CurrentUser { get; }
    public string DoSomething()
    {
        if (GetCurrentUid() == "用户为空")
        {
            //do something
        }
        else
        {
            //dosomething
        }
        return "";
    }
    public string GetCurrentUid()
    {
        if (CurrentUser == null)
        {
            return "用户为空";
        }
        return "";
    }
}
public class CurrentUser
{
    public string Uid { get; set; }
}

不如直接抛出异常,让异常处理机制进行处理吧。

1
2
3
4
5
6
public string GetCurrentUid()
{
    if (CurrentUser == null)
        throw new NoLoginExecption("");
    return "";
}

9、边界

9.1 模块间的边界

即便是简单的CRUD应用系统,优秀的开发者也能更好的处理应用程序模块间的边界。某种意义上讲,应用程序内部的边界看起来或许没有明确的界限之分,但是稍不留心就可能导致应用程序间关系过于紊乱,让其他开发者捉摸不透。
例如,假设有一段代码是这样的,在用户操作类中,加入了一个获取应用数据的方法,确实会让人很费解吧。(而应用领域驱动设计的思维,或许是一种不错的模式。)

1
2
3
4
5
6
7
public class UserService
{
    public string GetAppData(string config)
    {
        //do something
    }
}

9.2应用间的边界

相对而言,或许应用间的边界似乎能相对清晰的分析出来?并非如此。
在当今时代,我们很少开发完全与其他应用系统没有任何关联的独立软件,这意味着我们或许无时无刻都得与其他第三方应用进行接口行为或数据的交换。这让我们必须确保采取措施让外来代码干净利落地整合进自己的代码中。
假设有一段代码是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class UserService
{
    public string GetAppData(string config)
    {
        //do something
    }
    public string UploadOssData(string file)
    {
        OssConfig oss = new OssConfig();
        OssSdk ossSdk = OssSdk.CreateInstance();
        ossSdk.UploadFile(file);
    }
}

在《代码整洁之道》书中,Robert大叔推荐应该第三方接口进行隔离,通过Map那样包装或者使用适配器模式将我们的接口转换成第三方提供的接口。让代码更好地与我们沟通,在边界两边推动内部一致的用法,当第三方代码有改动时修改点也会更少。

总结

写代码是开发者的基础技能,无论你是.NET 开发者,或者 Java 开发者,你都在努力用代码实现自己的梦想。如同韩磊老师在译作《代码整理之道》封面上总结全书,写下的那句诗

1
“细节之中自有天地,整洁成就卓越代码”。

卓越代码从来不仅仅只是功能完善、代码齐全,做好细节,每个细节就是一方小天地。优雅的代码,不仅仅只是开发者的个人能力的体现,更是开发者的立足之本。努力改善坏习惯,提高代码质量,时刻消除异味,时刻提高自己,更有助于个人技能的全面发展。

项目失控过程全纪录

Posted on 2019-10-09 | Edited on 2021-04-25 | In 管理

题外话

在此之前,笔者主要从事传统IT企业的研发技术管理工作,对项目管理虽然有一定的经验,但纯粹摸石子过河,没有系统的学习过项目管理理论,也很容易犯下技术人员对项目管理的一系列毛病。
之前带的项目一般都是非产品型项目,功能一般以实现为主,对细节没有太多要求。项目一般采用瀑布模型,项目之初一般会制定一个非常详细的研发计划,涵盖需求分析、设计、研发、测试、验收的全过程。由于用户验收测试往往需要花大量时间,因此会输出一份沉甸甸的需求说明书,然后让客户签字,并在验收阶段,在现场维持长时间的驻场开发,以便实时跟进需求的变化。曾经创造了带领10人研发团队、连续3个月,每天维持12个小时以上工时的工作记录。(堪称土劳模)
这是第一次参与互联网项目的开发,有明确的迭代周期和需求计划,但是由于各种原因,却逐渐走向失控,过程中究竟发生了什么原因?

失控全过程

话不多说,直接进入正题。
我将尽量以第三人称视角记录一个失控的项目,但事实上我实际是项目负责人,所以难免有失偏颇。
这是B公司一个面向特定行业的虚拟建站平台产品,虽然市面上建站产品非常多,但由于B公司在该行业颇有地位,能够获得一些比较实用的数据,这些数据是目标用户群体非常渴求的宝贵资源,因此如果将这些数据做成可视化组件然后打造这样的精准型建站产品,显然具有独特性的优势,能够为目标用户带来便利。
产品于2月底开始启动会,由研发部门内部碰头后,直接开始启动项目。(该公司管理比较扁平化,产品、研发、测试、设计分属技术部四个不同的团队)。
由于是互联网产品,因此往往会选择竞品。竞品为为3个比较热门的互联网建站平台,其目标是要满足这些建站平台的特性,包含一个功能强大、效果优雅、流程简单的设计器;实现自动化建站部署等。在项目初期仅明确了总功能点的约40%,初步估计需要完成大大小小的页面15个,设计数据库20几个表。
(最终做出的页面数量虽然没增加,但是前端的逻辑也非常复杂、后端逻辑也有点复杂,数据库有45个表,大小接口将近80个)
技术总监指示:采用敏捷开发模式。总工期为8周,按2周一迭代。项目启动时,共有后端开发者3人,前端1人、产品1人、设计1人,测试1人。由本人担任总体负责人和后端开发者。
这个配比本身不太合理的项目,显然8周时间不可能完成,但领导没有进一步的时间计划,只能基于现有资源制定为期八周的先启阶段,其目标是搭建符合流程的最小可行版本(MVP)。由于大部分功能都在前端,但是实际上仅配了一位前端,而且这位前端是公司的前端负责人,每天仅能花不到40%的时间投入到项目研发过程中。后端则可以开始根据现有需求进行数据库、接口的设计。
这个MVP版本包含网站定义、网站编辑、网站设计器和发布流程、目前需求范围内的接口功能开发,工期为8周。
项目组还需另外招聘3位前端开发者。
到五月底最终完成整个MVP功能开发花了十周时间。因为前端部分的工作量大于预估,仅设计器就花了6周。5月完成简单的演示,基本符合主体流程。
图片
此时项目需求已经明确了60%,技术总监认为7月底可以完成功能的开发。
于是从5月开始,到7月底,共有10周时间,开始进入倒排期。庆幸项目组成员已经配齐,至少是有人可以用。但在需求池里面的需求非常多,所以大家得加班。于是集体开始进入加班状态。
项目很给力,动作交互和逻辑非常多,许多样式和动作都需要前端开发,几乎没有任何可以复用的经验,由于设计器本身是赶工期赶出来的,功能不完善,而且还有很多组件功能需要开发,最终于7月20几号左右完成主体功能的开发,但是有较多量的bug,推迟到8月6日才能交付测试,然后还剩余的40%功能点。
图片
由于项目已经严重滞后,上线目标定在9月中旬,又是一轮加班,坚持到8月底。
需求勉强控制住了,但是bug却越来越多,已经远远不可能在9月交付了。
图片
一句话总结:开始时,没人,赶时间;后来人来了,但是任务多,赶时间;最后,任务多,bug多,也赶时间。 程序员们的996,就是这么产生的。

项目结局

进入9月,公司高层对项目提出了新的要求,项目需求将进一步增加。
由于负面情绪的影响、以及对在这家公司的发展前途迷茫、无法忍受高强度的工作、转Java等原因,本人提出离职;
而担任前端开发工作的核心开发者也同样因为待遇问题、发展前途、技术路线不匹配等原因提出离职。
项目彻底失控。

问题分析

  1. 未能及时的向上级汇报可能存在的风险,未能考虑补偿资源等问题。未能实时的进行问题的跟进,汇报和沟通,直到最终问题无法弥补而爆发。
  2. 项目管理过程中过程数据不完善,无法形成有效的经验教训知识库,容易造成组织过程资产的流失。
  3. 这个项目前期需求范围虽然比较明确,但是技术细节多,人员不足,设定工期内明显完不成。
  4. 项目团队属于重新组建的团队,都是入司未满一年的新员工,且还有一半是在项目期间组建的新员工,彼此缺乏磨合。
  5. 项目任务优先级不明确,对MVP的粒度划分也不合理。(因为设计器功能很复杂,花费了大量的时间去开发)前期的功能粒度划分不合理,测试无法第一时间介入,问题积累到后期爆发。
  6. 功能逻辑复杂,细节颇多。拍脑袋式的工时估算和计划不适合此类项目。
  7. 异地沟通本身存在时延,影响了项目执行效果。
  8. 由于为了完成不可能完成的任务,项目组成员被迫赶工期,为了盲目应对需求变化而投机取巧,难免会改出新问题。
  9. 公司内部对代码质量和工期提出了新要求,延迟这么凶的项目显然不会获得高绩效;除此之外,内部政策变动,要求实施9116的工作制度,加上提出了封闭项目等政策,让内部消极情绪开始占上风。
  10. 本身存在一些技术问题。

    结论

    使用好项目管理工具有利于知识的传承和积累,对项目管理过程来说尤为重要,这个项目虽然使用了Jira作为项目管理工具,设定了迭代目标和周期,但是未能妥善使用,依然是用excel进行项目管理,措施不科学,也无法直观的观察项目进程。
    互联网产品致力于为用户提高更高的用户体验,因此需要花更长的时间和精力进行UI级的打磨、开发者也同样需要花更大的时间来完成对应的开发任务,作为项目经理要学会做好上下沟通,遇到问题应该给出自己的专业意见,有问题应该尽早向领导汇报风险,积极的反馈意见,哪怕收不到积极的响应,也能起到提前预防的作用。
    进度和计划固然是很重要的,但是要认识到人才是企业的核心竞争力,一支对产品精益求职的产品设计团队、一支每天愿意花10小时耐心写代码、努力提高研发产出的软件工程师团队是互联网产品成功的核心关键。
    一味的压缩工时来完成产品的研发不现实,在时间、进度、质量之间,必须找到一个中间点,这就要求做好客户的预期管理。

从零开始学习TDD,测试驱动开发

Posted on 2019-10-09 | Edited on 2021-04-25 | In 技术

前言

最近有幸跟随资深ThoughtWorks咨询师熊节老师一起学习测试驱动设计,经过短暂的十几天培训,对测试驱动设计的基本原则、实践模式、技巧有了一点点初步的认识。
在此之前,经常自嘲我经历的公司实践也似乎是TDD, 这种实践往往都是由测试工程师来驱动开发者完成bug的修改,虽然也是测试来驱动开发,但是却与真正的TDD大相径庭。

什么是TDD

在维基百科中是这样对TDD下定义的:

测试驱动开发(英语:Test-driven development,缩写为TDD)是一种软件开发过程中的应用方法,由极限编程中倡导,以其倡导先写测试程序,然后编码实现其功能得名。测试驱动开发始于20世纪90年代。测试驱动开发的目的是取得快速反馈并使用“illustrate the main line”方法来构建程序。
测试驱动开发是戴两顶帽子思考的开发方式:先戴上实现功能的帽子,在测试的辅助下,快速实现其功能;再戴上重构的帽子,在测试的保护下,通过去除冗余的代码,提高代码质量。测试驱动着整个开发过程:首先,驱动代码的设计和功能的实现;其后,驱动代码的再设计和重构。

测试驱动开发也是国外许多优秀开发者向开发者们推荐的一种普遍适用的开发模式,而在熊节老师的培训课程中,他时刻在向开发者灌输来自TDD的三条原则,要求我们的编写生产代码前,一定应该先编写单元测试。

定律一:在编写不能通过的单元测试前,不可编写生产代码。
定律二:只可编写刚好无法通过的单元测试,不能编译也算不通过。
定律三:只可编写刚好足以通过当前失败测试的生产代码。

简单实践

在我之前的编码实践过程中,总是习惯梳理一遍逻辑后,在根据项目的实际情况对代码进行重构,而随着我自以为掌握了单元测试的技巧之后,就开始把逻辑代码往单元测试上套,导致这样的单元测试实际上并非为了实现测试,而仅仅只是程序的入口而已。
如果使用TDD的方法,则需要首先规划需要实现的目标,然后再定义测试方法和测试需要实现的逻辑。
例如,代码大概是这样的:
图片
我的目标是实现对Schema对象的解析,测试类采用SchemaUnitTest,并采用“should_xxx_when_xxx”的命名方式,定义了测试方法“should_return_true_when_bool”,然后定义一个Schemas的类,再定义其需要实现的需求(断言),以及需求的实现。
对单元测试方法的命名,不同的书籍有不同的命名方法,在这个项目实践中,采用的是should命名方法,而在之前看过的《单元测试的艺术》一书中,使用的is_when_return_xxx的方式,这两者只是命名方法的不同,本质上没有任何区别;使用xunit和mstest实际上也没有太多区别。
此时,这个定义的方法GetParameter是未实现的,所以会进入一个“红-绿-重构”的工作流程。
图片

1)编写一个会失败的测试,以证明产品中代码或功能的缺失。编写代码时,要假设产品代码已经能工作了,这样测试的失败就说明产品代码中有缺陷。例如我定义的GetParameter方法使用xunit进行测试会提示失败, 只有在添加需要的代码后,编译才能通过。
2)编写符合测试预期的产品代码,使测试通过,产品代码应该尽量简单。
3)重构代码。如果测试通过了,你就可以编写下一个单元测试,或者重构,消除异味或提高代码可读性。
最终,我完成了一个这样的方法。(即便是这样的代码,依然有许多可以进一步提升的空间。)
图片
显然这是一个逻辑非常简单的代码,但是如果采用全键盘操作,不使用鼠标来完成,仍然耗费了我不少时间,这个过程中,也让我对Visual Studio的快捷键操作更加熟练。

测试的不同阶段

在我们的产品研发过程中,经常遇到以下三种不同形式的测试
图片

  • 端到端测试:端到端测试侧重于软件功能应用层面的测试,主要使用人工或自动化的形式对用户界面进行测试。往往需要覆盖系统的各个功能,需要耗费的人力物力较大。
  • 服务测试:主要集中在服务接口层的测试,可以通过PostMan等测试工具对接口的稳定性和可用性进行测试。侧重于接口行为实现。
  • 单元测试:针对代码层面,例如单个方法或单个类实现的测试。属于白盒测试的一种。

三种测试从上到下实施的容易程度递增,但是测试效果递减。端到端测试最费时费力,但是通过测试后我们对系统最有信心。单元测试最容易实施,效率也最高,但是测试后不能保证整个系统没有问题。
在我们的项目实践中,更多的采用的依然是端到端测试的模式,似乎只有通过测试者的人肉测试,才能让我们的代码更加令人满意。
单元测试事实上极少在我们的项目中得到实践,其主要原因大概是因为要掌握单元测试方法,本身需要对开发者的主观能动性提出了更高的要求,但是996开发者…太容易内卷化了。

总结

写好单元测试从来就是技术活,有一段时间过分在意理论概念和工具的用法,忽略了实践,所以实际上看了好几本书,依然不知道如何写单元测试,这次参与了培训,终于摸到了一点点影子。
现阶段我大概可以这样做来逐步提高自己的技能水平:

  • 1、小步快跑,注意节奏:不要过度在意某个需求的快速实现,而是编写能够在五分钟内快速完成的代码,并确保其通过。代码行控制在五行以内,代码的缩进层次,控制在两到三层。
  • 2、练习,练习,再练习:写代码从来不是一件容易的事情,按照一万小时定律的说法,如果指望几天就熟练掌握显然不太现实,未来需要更加积极的练习,才能真正掌握。
  • 多思考、努力写好代码:写几行代码其实并不难,难的是写高质量的代码。不要急于代码实现,要多思考上下文逻辑,让代码更加优美。

参考资料:

  • https://docs.microsoft.com/zh-cn/dotnet/core/testing/unit-testing-best-practices
  • https://juejin.im/post/5c715e7d6fb9a04a0c2f12f4
1…789

溪源

81 posts
4 categories
4 tags
博客园 CSDN
© 2021 溪源
Powered by Hexo v3.9.0
|
Theme – NexT.Pisces v7.1.2
#
ICP备案号:湘ICP备19001531号-2