时间尽头是周五——结对编程3
项目 | 内容 |
---|---|
这个作业属于哪个课程 | 2021春季软件工程(罗杰 任健) |
这个作业的要求在哪里 | 作业要求 |
GitLab项目地址 | 2021_Ziming_Yang-Jingwen_Tian_pair_work |
Yang学号后四位 | 3080 |
Tian学号后四位 | 3420 |
一、结对项目实践反思
1、分析问题
针对前面两个阶段中出现的问题,分析问题的特征、产生的根源和对质量的影响程度
第一阶段:沟通不充分,合作不协调。第一次结对编程我们在很多方面都处于一种磨合状态中,并没有很好地发挥出结对编程的优势。对于驾驶员和领航员来说,驾驶员专注于自己的代码设计,领航员费力于理解驾驶员的设计思路,虽然一直没有停止沟通,效果也优于个人作战,但是也没有快速地进入两个人共同设计的状态,导致最后出现了一些在我看来应该在编程阶段就解决的问题,包括最后的设计框架我们也感觉不够清晰。
驾驶员和领航员不断轮换角色,不要连续工作超过一小时,每工作一小时休息15分钟。领航员要控制时间。
——《构建之法》
第二阶段:需求复杂多变,测试阶段困难,为保持测试的一致性,驾驶员持续驾驶。第二次结对编程的指导书不可谓不复杂,虽然在进行框架设计之前已经对指导书进行了通览,但是由于在设计以及编码过程中我们专注于单个操作的介绍,因此忽略了很多诸如“不再赘述”、“...等同理”的描述,这导致了我们测试阶段任务量巨大,单元测试难以完全覆盖。又由于之前实验过的Code With me同步性能差,代码测试又覆盖多个文件,所以我们很难同时进行,测试阶段近乎于串行,这导致了我们在测试阶段工作量往往压在一个人的身上,花费了大量时间,实际上造成了驾驶员持续驾驶的情况。
2、需求分析实践体会
总结结对项目中的需求分析实践体会,并分析哪些bug是因为需求分析不足而带来的
需求分析这部分,我认为我们的结对作业中存在两类有关于需求分析的问题。
一是需求中存在一些细节需求,我们在需求分析中没有梳理清楚:比如软硬链接的重定向、异常的抛出等。然而我们没有在需求分析阶段对这些细节进行梳理,包括很多逻辑方面的问题,我们是在设计过程中才发现的,这导致我们在第一次设计时感觉思维混乱,难以继续进行。如cp
和mv
操作,在测试阶段,很多bug都是在这里发现的,而在参考issue区并细查指导书后,发现对于其的细节描述早已存在,不过被我们忽略了而已。
二是需求是在不断变动的,我们过于依赖issue区讨论得到出的结果去实现对应功能。导致了我们事实上不断地在自己的代码上打补丁,造成了代码上存在了许多可以避免的冗余,也使得代码的可读性较差。事实上,我们应该考虑到需求可能存在的变动,对这部分存疑需求,我们也应该纳入自己的框架设计中予以考虑,而不是弃之不顾,等到issue区盖棺定论再塞进自己的框架里。这时框架已经定型,且由于框架设计不够灵活,没法将这部分有机地融入进去,只能陷入“如果早知道”的懊悔之中。
这里列出一些我们在博客讨论区和助教对需求分析的相关讨论:
助教:如果让你们从头来过,你们是否会选择花2-3天时间把预期需求分析清楚后(包括跟甲方-即与 issue区的助教们之间进行交流)再开始编码工作呢?
我: 我们对「需求分析」的理解是,这次大体的需求是什么,针对本次结对编程,我们认为是分析出「需要在之前的文件系统的基础上增加软硬链接和其他指令,增加用户系统」。
而您提到的「预期需求分析清楚后(包括跟甲方-即与 issue区的助教们之间进行交流)」,我们认为是具体实现细节上的问题,这部分计入了我们的「具体设计」的时间中。针对您提出的问题,我认为从头来过我们也不会那样选择,原因如下:
结对编程是敏捷开发的实践,而不是瀑布开发的实践,我们不应预期甲方在某个时间之前可以将需求以完美的形式呈现(...... issue区的讨论很多时候是在编码过程中才产生的)。因此,我们面临的情况是「需求持续迭代」,在这个基础上,我认为用最短时间把握到需求的大体框架,尽快交付一版具有可用性的代码,比在编码前搞清所有细节然后再动手,更符合现实情况的要求。
3、架构设计实践体会
总结结对中的架构设计实践体会,描述通过改进设计来提高程序的性能改进的思路和方法,并分析哪些bug是因为架构设计不足(特别的,需求变化)而触发的bug
善用接口
在第一次的结对作业中,我们观察到这样一个事实:File
和Directory
都要实现getInfo
方法,因此我们设计了一个Infoable
接口,让FileSysEntry
实现这个接口,并作为二者的父类来对二者进行一定程度的抽象。同样的,在第二次,我们观察到SoftLink
和HardLink
都有要重定向到其所链接的文件或目录的需求,因此我们设计了Redirectable
接口,让软、硬链接实现该接口。
需求解耦
针对要根据路径来查找文件或目录的需求,我们设计了一个工具类Parser
,它的parse
方法负责路径合法性检查,和将给出的路径化为最简形式(在第二次中,重构为返回目录或文件名的列表,使之更符合后续调用的实际需求),将这部分与文件系统解耦。同时我们在Directory
中设计了find
方法,使其根据路径在该目录下查找文件或目录。
及时重构
在本项目的设计过程中,我们采用由上至下的设计方法,首先设计上层架构,梳理各个操作的逻辑,定义要使用的方法,然后在编码阶段具体实现所定义的方法。然而在具体编码过程中,我们发现很多细节的实现会导致我们可能想要访问起初并不想让这个方法访问的属性,会想要抛出并不想在这部分抛出的异常,但是囿于架构的设计,我们不得不这么做了。所以在第二阶段刚开始时,我们对第一阶段的设计架构重新进行了梳理,对其中不“合适”的地方进行了重写,这也使我们在第二阶段的编码过程中受益颇多。
测试驱动
在第一次的结对作业中,我们观察到指导书对路径规定了不超过4096个字符
,同时还有指令数不超过500条
。因此我们假设了强测中会出现利用上这两条说明的极限数据:
mkdir -p a/a/a/.../a/ #路径长度4096, 也即2048层目录
cd a/a/a/.../a #进入该文件夹
mkdir -p a/a/a/.../a/ #重复这样的操作
cd a/a/a/.../a/
... #将这两条指令重复249次
fwrite "1" a.txt
fwrite "2" b.txt #尝试在第2048*249层目录下创建文件
然后使用JProfiler
查看项目执行该代码时的性能情况。我们发现,由于存在2048*249
层目录,因此如果将absPath
作为类的属性存在内存中,将会使用高达260,051,552,256B
的内存,这远远超出了内存限制。我们随即决定不存absPath
,而是在调用getAbsPath
方法时,一直向上查询,拼接出自己的绝对路径,用时间换空间,规避了这个问题。
Bugs
我们在第二次结对过程中,自己进行测试时测出了不少的bug。一方面是上文所说的,对需求的细节分析不到位导致的;另一方面也有需求的变动导致的(需求的变动也导致了一部分Bug消失了,我愿称这部分为feature
)。前者主要集中在mv
, cp
, ln
, ln -s
这些指令关于重定向的方面。后者一个典型的例子是issue#43
:软链接指向硬链接时的行为应该是什么,按照我们对指导书的理解是软链接指向硬链接所指的文件的路径,但是助教澄清的说法是应让软链接指向硬链接本身的路径。这导致了我们在该处出现了我们意料之外的Bug。
4、结对过程实践体会
总结结对过程中的进度、质量和沟通管理实践体会,并分析哪些哪些bug是因为两个人的理解不一致而导致
在本次项目结对过程中,我们的进度管控整体来说是比较清晰的,我们利用了大量下午以及晚上的时间来完成结对项目,按照 设计=>编码=>测试=>优化=>总结 的过程逐步进行,虽然每天长时间的编码使我们有些疲惫,而且第二阶段最后也没有精力做优化,但是每个阶段我们都并不仓促,也很少熬夜。
关于代码质量的问题,在经历第一阶段的部分重写工作后,我们在第二阶段有意识地使用一个较为完善的设计架构,使每部分代码较为独立;将功能性的部分抽象成方法,使操作实现尽量清晰有序;也定义了一套继承于OO的代码规范,来约束我们的编码行为。不过最后的测试阶段还是发现了很多问题,感觉我们的架构设计需要进一步改动以更好地适应课程组的需求。
在沟通管理方面,在整个项目过程中,我们基本按照“串行编码、并行测试”的思路,通过腾讯会议来及时共享屏幕、语音交流,通过git或者微信互传分阶段同步项目进程,两个人对代码都做到了绝对的把控。同时,在其余时间一方对代码进行了小的改动时,也会及时详细地通知另一方,另一方可以在提交记录中查看具体改动内容,达到绝对的同步,起到了良好的沟通效果。
5、建议
提出建议:根据三个阶段的结对项目的实践经验,对如何更好的实施和管理结对项目提出自己的建议
沟通与设计,是我认为结对编程中两个很重要的因素。
一个项目,两个人完成,在制定好统一的编码规范后,能否顺利地进行同步开发,很大一部分就在于能否设计一个完善具体的代码架构以及能否及时进行充分的沟通。并行开发无疑是提高效率的一大利器,但是使用不好也会造成两者之间合并困难,产生大量bug。
因此,在我看来,如若不能进行很好的平衡,使用结对编程中最朴素的思想,轮流担任领航员和驾驶员的角色,进行串行开发未尝不是一个好的选择。但这样也要保证领航员能够充分发挥自己的作用,要“深刻地想”而非“傻傻地看”,及时为驾驶员提供上帝视角的想法与意见,做到两人共同设计开发,提高项目质量。
二、CI体验感想
通过这次结对编程,你对CI的使用体验如何?你对这一工具有何认识?
这次项目,使我们对于CI工具的使用有了更进一步的认识,也体会到了Gitlab-CI的强大。相比之前简单的提交测试,这次我们在ci上实现了复杂的部署、测试、提交等功能,并且在ci使用cobertura生成了测试结果报告。yml文件的使用大大简化了冗长的配置工作,许多人性化的如手动触发等功能,也很大提高了我们的体验感。
三、结对编程感想
1、结对总结
描述你们结对的方法、结对过程中遇到的困难与收获,结合自己的结对经历,说明结对编程的优点和缺点,分享可以推广的结对妙招
在本次结对过程中,我们做到了很好的沟通,对于设计或者出现的问题,通常都是两个人一起讨论解决的办法,然后选择一个较为保险的,两个人总能比一个人考虑更加全面。这样的过程,很大程度提高了我们的代码质量。
然而结对编程的串行性,其实在一定程度上导致了它的效率较低,虽然知道很多组都在采用分工合作的方式,但是我们并没有这样,一是想要更加真切地体会一下结对编程,二是想在时间允许范围内尽量满足课程组的要求。但是这样确实导致了我们的开发速度远慢于并行开发的团队,甚至慢于独立开发者(加入有的话,因为我们要花费时间进行沟通、讨论)。
总之,这样看来,结对编程似乎更加适合于时间充裕的开发者(这里就不强调对项目质量的要求了,因为开发高质量的项目是开发人员共同的追求)。
2、队友评价
评价你的队友,使用汉堡点评法评价你的结对伙伴,务必给TA 提改进意见
TO YANG
Yang在架构设计方面思路很清晰,对代码的质量也有很高的要求,学习能力也很强,通过这次项目,我从他的身上学到了很多。不过这次很多时候都是Yang在充当驾驶员的角色,让我感到有些惭愧。我能体会他面对程序编写时跃跃欲试的心态,我们的整个编码过程也完成得很愉快,但是也希望他能够适时将压力交给伙伴,让自己能够得到及时的放松。
TO TIAN
Tian在代码细节的关注比较到位,经常能在我意识模糊的时候指出我所犯的低级失误,让我时常感叹“还好是结对编程,这要是OO,这Bug我得de多久啊!”。但是她似乎对 git 工具的掌握不是很熟练,导致了中途某次回退版本时产生了一些问题,稍微耽误了一下进度。整体而言,和Tian的合作能够产生有效的沟通,及时地避免了许多问题的出现。
3、使用工具
描述在本次结对编程的过程中,你们使用了哪些软件工具,是如何应用于实践的
在交流方面,我们使用了腾讯会议来共享屏幕以及语音交流。
在代码编写方面,我们使用了 IntelliJ IDEA 软件,创建 maven 项目进行代码的管理。
在代码协作方面,我们首先尝试了Code With Me 工具,在两者同时操作同步延时的不良体验下,我们放弃了对此工具的使用。
在代码同步方面,我们使用了 git 工具,及时上传项目,进行不同版本的记录。
在代码测试方面,我们除了使用 Junit 4 单元测试工具之外,还是用了 cobertura 进行覆盖率检测并生成检测报告。
在项目部署方面,我们使用了 gitlab-ci 完成项目的自动部署,通过配置 yml 文件定义自动部署要求。
4、感悟体会
描述通过本次结对编程的感悟和体会,对本次作业的有哪些想吐槽的,觉得本次结对作业内容可以在哪些方面做出改进?
TIAN
本次结对编程作业确实使我深切体会到了何谓结对编程,以及它的优缺点,具体上面(包括之前)已经说了很多了。
不得不要吐槽的,就是任务量太大了,我们每周都要花费几十个小时的时间来完成这个项目,这几乎占用了我们的全部课余时间。不说我们还有许多其它科目的作业要完成(实际上,软工的结对项目导致我堆积了大量的ddl),单说其它软工班级的同学们,据我所知,他们已经在最终大作业的需求分析部分进行了多轮的迭代了。不说我们的步调要保持一致,但我们确也有大作业的任务(且这为软工课的核心),然而不知道是否会因为前几周我们忙于结对项目,而导致我们之后大作业的时间和任务也会被浓缩,给我们造成更大的压力。
我对于体验结对编程这个初衷是毫无疑义的,但是后面第二阶段关于细节方面的把控问题,确实使我感觉失去了体验的初衷,个人感觉这部分的设置应当做一些改变,不能让结对编程莫名变成了oo的延伸,由两人结对变成了一味地挖指导书debug。
YANG
想吐槽指导书。指导书成功地使我患上了“不再赘述ptsd“,当我出现Bug的时候,我第一反应是ctrl+f
搜索“不再赘述”,看看是不是我遗漏了。这个词造成的后果可能和指导书组的预想背道而驰,我猜想其原意是为了防止一个条目过于冗长,造成同学们理解上的不便和障碍,但说实话现在的“不再赘述”反而造成了指导书不同条目之间的耦合,使得严格按该部分去实现功能时会受到“不再赘述”的背刺。
关于改进:指导书的问题是源于助教精力的有限性和指导书文本的线性。当助教因为issue区的讨论而对指导书进行迭代时,由于精力的有限性,往往只能根据记忆对issue所涉及的指导书的相关部分进行分析。而由于指导书文本是线性呈现的,最细致的查看也只不过是从头到尾看一遍,很难直接将issue的所有相关部分考虑到,更不用说还有着许多隐性的联系需要深入分析。
显然,助教精力的有限性是客观无法改变的事实,想要解决这个问题,只能从指导书文本的线性入手。我们可以列具出许多因为指导书文本的线性所导致的问题:
-
为了使指导书不至于过于冗长,在一些概念或指令中添加了“不再赘述”的描述,这使得许多指令和许多之前的文本紧密耦合,如果想对这部分耦合的内容进行修改,势必需要极大的精力付出才能保证其正确性;
-
每次对指导书的修改,没有足够的上下文信息指示这个修改的来源,需要阅读相关issue才能明白这个修改的原因。
如果说指导书可以是非线性的,假设指导书中存在实体
和链接
:实体
指和实现有关的重要概念,例如本次作业中的路径、目录、软硬链接、各个指令等;链接
指实体
之间的联系,例如指令中会涉及到路径的处理,会涉及到目录、文件这些概念。如果我们分别描述好实体
,并且将链接
显式地展示出来,那么当issue中出现相关问题时,就可以直接定位到相关实体
,并且根据链接
去看哪些相关内容需要修改。这似乎和互联网当年超链接出现的情形有相似之处。
文字的内容和书写方式都会同时影响书写者和读者,我希望能通过改变指导书书写方式的办法,在不改变内容含义的前提下,对指导书的内容进行解耦。