[I.3] 个人作业:结课总结
[I.3] 个人作业:结课总结
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | 2026年春季软件工程 |
| 这个作业的要求在哪里 | [I.3] 个人作业:结课总结 |
| 我在这个课程的目标是 | 掌握现代软件工程方法,提升团队协作与项目开发能力 |
| 这个作业在哪个具体方面帮助我实现目标 | 回顾一学期实践,对最初的问题给出阶段性回答,并总结个人、结对和团队项目中的收获 |
旧问题博客链接
学期开始时,我在阅读《构建之法》之后提出了五个问题。那时的很多想法还停留在书本和已有经验上,更多是“读到这里觉得有矛盾”。一个学期之后,经历了个人作业、结对项目和 CodePorters 团队项目,再回看这些问题,很多问题不能说有了标准答案,但至少有了更贴近实际开发的理解。
一、提问回顾
问题一:单元测试追求 100% 覆盖率,在实际工程中是“银弹”还是“奢侈品”?
当时我的困惑是:书中强调单元测试应该尽量覆盖所有路径,但实际代码中总有一些很难触发的异常路径,例如内存分配失败、磁盘写满、网络突然断开等。如果为了覆盖率强行构造这些情况,会不会变成“为了测试而测试”。
现在我的理解是:覆盖率是参考指标,不是质量本身。
在结对项目“花见小路”中,规则判定函数的输入输出比较明确,所以测试用例很容易围绕规则边界展开。比如一方分数达到 11 分、一方获得 4 枚倾心标记、双方平局时如何判定等,都可以写成具体样例。这类模块追求比较高的覆盖率是有意义的,因为分支本身就对应游戏规则,漏测很容易导致错判。
但在团队项目中,情况就复杂很多。CodePorters 做的是 2D 横版硬核动作/RPG 游戏,很多质量问题并不只是一个函数输入输出是否正确。比如 0.1 秒弹反窗口是否舒服,技能追踪是否符合预期,Boss 战中多个特效同时触发时会不会卡顿,这些都不是简单覆盖率能说明的。Beta 阶段我们更关注核心流程、严重 Bug、性能压测和回归测试,比如严重 Bug 是否清零、20 个怪加 Boss 同屏时是否能稳定 60FPS、核心机制用例是否通过。
所以我现在认为,100% 覆盖率不是银弹,也不一定是必须付出的成本。更合理的方式是先识别风险:核心逻辑、公共模块、容易回归的部分应该尽量测充分;低概率、难模拟、影响较小的异常路径可以用代码审查、日志、兜底机制和人工测试补充。测试的目标不是让数字好看,而是让开发者有信心修改代码。
问题二:goto 是“洪水猛兽”还是“灵丹妙药”?它的使用边界在哪里?
我当时对 goto 的疑问来自书中观点和过往学习经验的冲突。课堂和教材通常会说 goto 破坏结构化,能不用就不用;但书中又提到在某些场景下用 goto 可以统一出口,使代码更清晰。
现在回看,我觉得这个问题的本质不是 goto 本身,而是控制流是否清楚。
在团队项目中,我们没有真的大量使用 goto,但遇到了类似的问题:复杂系统里如果状态转换没有统一约束,代码一样会变得混乱。动作游戏里有很多状态,例如移动、冲刺、受击、死亡、技能释放、弹反判定等。如果每个模块都直接修改彼此状态,就算不用 goto,执行流程也会非常难读。
CodePorters 在 Beta 阶段遇到过死亡状态锁、技能判定冲突、弹反对象误判等问题。最后更有效的解决方式不是纠结某个语法,而是用状态机、接口协议和明确的模块边界来约束行为。例如通过更清晰的状态锁避免死亡逻辑循环调用,通过 ICounterable 这类接口思路明确哪些对象可以被弹反,通过物理层级划分减少错误碰撞。
因此,我现在会把 goto 看成一种工具,而不是道德问题。它可能在底层 C 代码的统一错误处理里有价值,但在多数现代项目中,更重要的是设计清楚状态和责任边界。只要控制流能让后来的人快速理解、修改时不容易引入隐蔽错误,就是好的实现;反过来,即使完全不用 goto,一堆互相调用的条件分支也一样难以维护。
问题三:敏捷开发中的每日立会会不会变成形式主义的“站会表演”?
这个问题在团队项目中有了比较直接的体验。每日立会确实有可能变成形式主义。如果每个人只是说“昨天写了一点代码,今天继续写,没什么问题”,那会议基本没有价值,还会打断开发节奏。
但如果例会围绕阻塞和交付物展开,作用就很明显。
CodePorters 的 Scrum Meeting 中会记录每个人两日内完成的工作、接下来计划完成的工作和遇到的困难。比如 Alpha 阶段推进各模块开发时,例会能让大家知道谁在写战斗模块,谁在处理动作设计,谁在做 UI、属性系统、音频模块和技能设计。到后期联调时,会议的价值更明显,因为很多问题不是单个模块内部能解决的,而是模块之间对接产生的,例如技能特效、碰撞判定、角色状态、音频反馈同时作用时的冲突。
所以我现在的答案是:每日立会本身不是问题,问题在于会议有没有围绕真实进展。有效的立会应该回答三个问题:完成了什么可验证的东西,遇到了什么阻塞,下一步交付什么。它不应该是向 PM 表演自己很忙,也不应该要求每个人每天都必须制造“看起来有进展”的内容。
如果某个功能确实需要几天才能完成,那也不必硬拆成没有意义的小任务。更合理的做法是汇报中间状态,例如接口是否定了、关键风险是否解决、测试样例是否准备好。这样立会才会变成协作工具,而不是压力来源。
问题四:结对编程如何避免变成“一个在开车,另一个在睡觉”?
结对项目“花见小路”让我对这个问题有了更具体的理解。结对编程的价值不是两个人坐在一起就自动产生的,它需要明确分工和有效反馈。
在花见小路项目中,两个人一起阅读规则、讨论判定逻辑、设计测试样例,比一个人闷头写要稳一些。尤其是规则类任务,很容易出现漏判或边界情况考虑不全。一个人写代码时,另一个人可以从规则和测试角度即时发现问题,这种反馈很有价值。
但我也发现,结对编程不能完全替代个人深入思考。复杂逻辑真正写到代码里时,驾驶员仍然需要较完整地掌握上下文。如果频繁切换,可能反而打断思路;如果不切换,另一个人又容易变成旁观者。所以比较实际的做法是:在设计、测试、代码审查阶段加强共同参与;在连续编码阶段可以允许一人主写,但另一个人要承担明确职责,例如记录设计决策、检查测试覆盖、关注命名和边界情况,而不是只坐在旁边。
我现在认为,结对编程最重要的不是形式上的轮换,而是双方都对结果有责任。只要另一个人能持续提出具体反馈,哪怕键盘主要在一个人手里,也不是无效结对;反过来,如果只是机械换人,但对代码没有共同理解,也很难真正提升质量。
问题五:修复 Bug 和开发新功能,在优先级上真的存在“贪心最优解”吗?
经过团队项目之后,我更加确定这个问题没有固定答案。Bug 修复和新功能开发之间不存在简单的贪心最优解,真正的优先级要结合发布目标、用户影响和风险来判断。
在 CodePorters 的 Beta 阶段,我们既要补充技能树、装备词缀、Boss 战斗等内容,也要修复 Alpha 阶段暴露的问题。最后团队的判断标准更接近“核心闭环优先”和“严重问题优先”。例如会导致 Crash、死亡状态锁死、云存档失败、核心战斗无法继续的问题必须优先修复;而音频同帧并发导致的表现瑕疵,虽然影响体验,但可以作为 Known Issue 推迟。
这和我一开始“Bug 应该发现就立刻修”的直觉不完全一样。实际项目里时间总是有限的,如果所有 Bug 都立刻打断当前工作,团队会很难完成阶段目标。但如果严重 Bug 被拖到最后,也可能导致发布前集中爆炸。因此比较合理的方法是分级管理:严重 Bug 和核心流程 Bug 立即处理,普通体验问题记录到 Issue Tracker 中,和新功能一起按价值排序。
所以我的阶段性答案是:优先级不是“修 Bug”或“做功能”的二选一,而是看它对项目当前目标的影响。一个低优先级 Bug 可以延期,一个会阻断核心流程的新功能也可以暂缓。真正重要的是团队要有明确出口条件,而不是临近发布时凭感觉决定。
二、未解决问题
原来的五个问题都有了阶段性理解,但还有一个问题我仍然没有完全想清楚:体验型软件的质量如何被可靠地测试和度量?
CodePorters 做的是 2D 横版动作/RPG 游戏,这类项目和普通信息管理系统不太一样。它当然有很多可以测试的部分,比如角色是否会掉出地图、技能伤害是否正确、死亡后是否能复活、存档是否同步、性能是否稳定等。这些可以通过测试矩阵、回归测试、性能压测和人工黑盒测试来覆盖。
但还有很多东西很难量化。比如 0.1 秒弹反窗口是否“刚好”,打击停顿是否有爽感,技能追踪会不会让玩家觉得强但不失控,Boss 战难度是否公平,这些都不是简单的通过/不通过能描述的。它们依赖玩家反馈,也依赖开发者反复试玩。自动化测试可以告诉我们功能没有坏,但很难告诉我们体验是不是好。
因此,我现在仍然不太清楚:对于动作游戏这类强体验系统,如何建立一种既不完全依赖人工试玩,又能真实反映体验质量的测试体系?这可能需要更多数据记录、玩家行为分析和灰度测试,但在学生团队有限时间里还很难做完整。
三、新的问题
1. AI 辅助开发后,代码责任如何划分?
结对项目中我们使用过 VS Code 的 Copilot 辅助完成部分算法实现。AI 确实能提高效率,尤其是在生成样板代码、补全函数结构、给出初版思路时很方便。但它也带来一个新问题:如果代码是 AI 生成的,最后谁为它负责?
我的直觉是,AI 可以生成代码,但不能替开发者承担质量责任。只要代码被提交进仓库,模块负责人就应该理解它、测试它,并对它造成的后果负责。如果一个人只是把 AI 生成的内容粘进去,自己并不理解,那么后续调试和维护都会很困难。
这也让我产生了进一步的问题:如果未来 AI 参与代码生成、测试生成甚至代码审查,那么人工审查的边界在哪里?一个人对自己没有逐行写过的代码,究竟需要理解到什么程度,才能说自己对它负责?
2. 如何量化学生团队中的真实贡献?
团队项目中每个人负责不同模块,例如 PM/程序、动作设计、UI 设计、属性系统、音频模块、技能设计等。不同模块的工作形态差异很大,很难用代码行数衡量。技能设计可能涉及大量调参、试玩和设计讨论;UI 和音频也不一定体现为大量代码;性能优化可能只改几十行,但价值很高。
Issue、PR、提交次数、代码行数、功能点完成情况都能反映一部分工作,但都不完整。比如一个人关闭很多小 Issue,不一定比解决一个严重状态机 Bug 更有贡献;一个人提交很多代码,也不一定比设计一个清晰接口更重要。
所以我产生的新问题是:学生团队中,如何更公平地量化真实贡献?是否应该结合任务难度、完成质量、对核心目标的影响、协作成本和后期维护价值,而不是只看可见产出?
3. 体验型软件如何建立更可靠的自动化验证?
团队项目中,我们可以对核心流程和性能做测试,但技能组合、Boss 战、弹反手感、音频并发等内容很难完全自动化。尤其是动作游戏中,玩家可能用各种意想不到的操作顺序触发问题,例如疯狂重置死亡、连续释放技能、在极限帧率下触发特效错位。
因此我想继续追问:对于这种体验型软件,自动化测试应该做到什么程度?是否可以把部分玩家行为录制成脚本,做回放测试?是否可以通过日志统计技能触发、死亡次数、帧率波动来辅助判断体验问题?这些问题在本学期的项目中还没有彻底解决。
四、各阶段知识点
1. 需求阶段
需求阶段我学到的知识点是:需求不是功能清单,而是用户画像、核心场景和核心痛点。
团队项目一开始很容易陷入“多加功能”的想法,但功能多不一定代表需求清楚。CodePorters 项目真正稳定下来,是因为我们明确了目标用户:动作游戏爱好者和成长型 RPG 玩家。前者追求操作上限和 Boss 挑战,后者希望通过刷怪、装备和技能成长获得变强的反馈。确定这一点之后,精准弹反、Boss 战、装备词缀、技能树等功能才有了比较清晰的位置。
所以需求分析的核心不是列出“我们能做什么”,而是回答“用户为什么需要它”。
2. 设计阶段
设计阶段我学到的知识点是:接口、状态机和模块边界要尽早统一。
团队项目中很多问题都出现在联调阶段。单个模块自己运行时看起来没问题,但一旦战斗、技能、属性、音频、UI 同时工作,就会出现各种冲突。比如技能判定和弹反判定互相影响,死亡状态和攻击状态没有完全隔离,属性变化和装备系统之间需要统一结算方式。
这些问题让我意识到,设计阶段不能只画一个大概框架,而要尽量把模块边界和交互方式讲清楚。否则后期每个人都按自己的理解写,最终合并时就会付出更高成本。
3. 实现阶段
实现阶段我学到的知识点是:功能能跑只是第一步,可读性、可复用性、性能和模块隔离同样重要。
在结对项目中,规则判定如果写成一大段条件分支,也许能跑,但后续修改会很麻烦。因此我们会设计中间量,把分数、倾心标记数量、最高分标记等信息提取出来,让判断逻辑更清楚。
在团队项目中,功能能跑更不等于可交付。动作游戏如果逻辑正确但帧率不稳,玩家仍然会觉得体验很差。技能如果能释放但接口混乱,后续加新技能也会很痛苦。因此实现阶段不只是把需求翻译成代码,还要考虑后面是否能维护、能扩展、能被别人理解。
4. 测试阶段
测试阶段我学到的知识点是:测试需要矩阵、分级和回归意识。
以前我对测试的理解比较简单,觉得功能跑一遍没问题就算通过。但团队项目中,测试对象明显复杂得多。不同平台、不同硬件、键鼠和手柄输入、Boss 战、技能组合、云存档、性能压测,都需要考虑。
Beta 阶段修复 72 个 Bug 的经历说明,测试不是最后随便玩几遍,而是要系统化地覆盖关键场景。严重 Bug 和核心流程要优先保证,修完之后还要做回归,防止修一个地方又破坏另一个地方。对于课程项目来说,完整自动化测试很难做到,但测试矩阵和 Bug 分级至少能让团队知道风险在哪里。
5. 发布阶段
发布阶段我学到的知识点是:发布需要明确出口条件。
如果没有出口条件,发布前很容易陷入“再改一点”的状态。CodePorters 在 Beta 阶段更明确地提出了核心机制用例通过、严重 Bug 清零、性能达标、核心流程可跑通等要求。这样团队才能判断当前版本是否可以交付,而不是完全凭感觉。
发布不是把程序打包发出去这么简单,而是对外承诺这个版本至少满足哪些质量要求。对于游戏项目来说,能进入游戏、能完成核心战斗、能正常死亡和复活、能稳定运行,都是基本要求。
6. 维护阶段
维护阶段我学到的知识点是:Issue Tracker、PR 记录、Known Issue 和文档更新是后续迭代的基础。
团队项目不是写完一次就结束。Alpha 到 Beta 的过程,本质上就是维护和迭代:根据反馈修复问题,补充功能,调整优先级。Issue 可以记录问题,PR 可以保留修改原因,Known Issue 可以说明暂时不修的内容,文档可以帮助后续成员理解系统。
如果这些内容不维护,项目会很快变成只有当事人能看懂的状态。尤其是在 AI 辅助开发越来越常见的情况下,过时文档还可能误导 AI 生成错误代码。因此维护阶段并不是“扫尾”,而是保证项目能继续演进的关键。
五、心得体会
个人项目
个人作业让我最早意识到,软件工程不是只看代码实现。
第一次阅读提问时,我更多是在书本概念和个人经验之间找矛盾。比如测试覆盖率、敏捷立会、结对编程这些问题,当时都比较抽象。到软件案例分析时,我开始从用户角度看产品,分析需求匹配、Bug、评分体系和改进方案。这些练习让我逐渐习惯从“用户是否真的需要”“功能是否解决问题”“质量如何评价”这些角度看软件,而不是只看代码能不能写出来。
现在回头看,个人作业其实是在训练一种观察方式:把软件当作一个产品和工程,而不是单独的程序。
结对编程
结对项目“花见小路”让我体会到规则拆分、测试设计和即时沟通的重要性。
这个项目的规则并不算特别庞大,但如果不认真拆分,很容易在边界情况上出错。两个人一起讨论规则时,可以更快发现自己没考虑到的情况。写测试样例时,也能互相补充思路。比如胜负判定、行动记录解析、策略选择等部分,都需要先搞清楚规则,再落实到代码。
但结对也让我意识到,两个人合作不等于效率自动翻倍。真正有价值的是在设计、测试和复审环节互相提供反馈,而不是机械地要求两个人一直盯着同一段代码。结对编程更像是一种降低盲区的方法,而不是减少工作量的方法。
团队项目
团队项目是这一学期最接近真实软件工程体验的部分。
我们 CodePorters 做的是 2D 横版硬核动作/RPG 游戏。团队中有 PM/程序、动作设计、UI 设计、属性系统、音频模块和技能设计等分工。我负责技能设计相关内容,这让我更直接地感受到模块协作的复杂性。一个技能不是单独写出效果就结束,它还要和角色状态、属性系统、敌人判定、动画、音效、UI 冷却提示等模块对接。
在这个过程中,我对“接口”和“边界”有了更实际的理解。比如技能系统需要知道哪些数据来自属性系统,哪些行为由战斗模块触发,哪些反馈交给 UI 和音频模块表现。如果这些约定不清楚,后期联调就会不断出现问题。Beta 阶段的技能树、法术追踪、瞬移、时间回溯等内容,看起来是玩法设计,背后其实需要工程结构支持。
团队项目也让我认识到测试和发布压力。课程项目中大家还有其他课业,后期很容易赶 DDL。如果没有 Issue、PR、测试报告和明确出口条件,项目会很难收尾。Beta 阶段修复大量 Bug、做性能压测、整理发布说明,让我第一次比较完整地感受到“交付”这件事的重量。
总结
一学期之前,我对软件工程的理解还比较简单,觉得它大概就是把一个比较大的项目写出来。现在看,写代码只是其中一部分。真正困难的是在多人协作下,让需求能被理解,让设计能支撑变化,让实现能被维护,让测试能发现风险,让发布有明确标准,让后续维护有依据。
这门课最大的收获,是把书里的概念放进了具体实践里。单元测试、敏捷开发、结对编程、需求分析、项目管理、测试发布,这些词单独看都不难,但只有真正经历过 Bug、联调、分工、进度压力和发布验收之后,才会知道它们为什么重要。
所以我现在对软件工程的理解,从“把功能写出来”变成了“让多人协作下的项目能够持续迭代”。这也是这学期最重要的收获。

浙公网安备 33010602011771号