[I.3] 个人作业:结课总结
| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | 2025春季软件工程 |
| 这个作业的要求在哪里 | [I.3] 个人作业:结课总结 |
| 我在这个课程的目标是 | 掌握软件工程原理与敏捷协作,实现高质量系统全周期工程化,开发出一款令人满意的软件 |
| 这个作业在哪个具体方面帮助我实现目标 | 提问回顾与个人总结 |
提问博客链接
“实践是认识的来源、目的、动力以及检验认识真理性的唯一标准。”
在学期初,我们阅读了《构建之法》,并带着许多疑问开启了这门课程。当时我记录的博客是:[I.1] 个人作业:阅读和提问。
一个学期过去,我们经历了个人项目、结对编程,以及两个阶段的团队项目,中间还有激动人心的转会环节。我们使用 Flutter 从零到一构建了一款跨平台应用。现在,是时候回过头,审视那些曾经困扰我的问题,看看实践是如何给予我答案的。
一、 对曾经提出问题的解答与反思
1. 微软的PM模式能否适用于小团队?会不会增加沟通层级?
曾经的困惑: 我曾质疑,在像我们这样的小型学生团队中,引入专门的 Program Manager (PM) 角色是否会“杀鸡用牛刀”,反而因增加层级而降低效率。
实践后的解答: 我现在认为,小团队可能不需要一个“PM”的头衔,但绝对需要“PM”的职能。
在我们的团队项目中,虽然没有正式任命谁是PM,但团队的组长在事实上承担了大量的PM工作。例如:
- 需求澄清与同步: 当一个成员对“用户个人资料页”的功能理解有偏差时,组长会组织一个简短的会议,拉上相关成员,对照原型图(我们用Figma设计)澄清需求,确保前端和后端对数据字段的理解一致。这避免了后期的无效返工。
- 进度跟踪与风险管理: 我们使用类似 Trello 的看板来管理任务。组长每天会检查看板,当发现某个关键任务(比如“实现基于Firebase的认证模块”)延期时,他会主动询问负责的同学是否遇到了技术难题,并协调另一位有经验的同学进行结对编程,共同解决。
- 沟通的“路由器”: 项目中期,前端和后端因为一个API接口的数据格式问题在群里争执不休。组长介入后,没有直接做决定,而是将双方拉到一起,明确了这是一个“需求定义”问题,而非技术实现问题,最终通过回顾最初的用户故事,快速统一了意见。
结论: PM的核心价值在于降低沟通成本和确保团队目标一致。在小团队中,这个职能可以由组长、产品负责人甚至某个有此意识的成员分担,但这个职能本身不可或缺。它不是增加层级,而是为混乱的“网状沟通”提供了一个高效的“中心节点”。
新的问题: 在学生团队中,承担PM职能的同学往往也是开发者。如何平衡好“管理协调”和“代码贡献”两种角色?当两者时间冲突时,应优先保证哪个?这是否会导致该同学的个人技术成长受影响?
2. “技能的反面是解决问题”是否低估了创新价值?
曾经的困惑: 我曾认为,将“解决问题”视为技能的对立面,是低估了工程师创造性解决复杂问题的能力。
实践后的解答: 我完全理解了作者的深意。“技能”指的是那些应当被内化为“肌肉记忆”的基础操作,而“解决问题”指的是在更高层面的、有创造性的挑战。
这个学期的Flutter开发经历完美地诠释了这一点:
- 初期(挣扎于低层次问题): 项目刚开始时,我们花了大量时间解决“低层次问题”,比如:如何用
Row和Column实现一个居中对齐的布局?setState为什么没有刷新UI?FutureBuilder到底该怎么用?这些时间,我们其实是在“解决问题”,但这种问题解决阻碍了我们思考真正的核心架构。 - 后期(专注于高层次问题): 当我们熟练掌握了Flutter的常用组件和Dart的异步编程后,这些都变成了“技能”,我们几乎可以不经思考地写出界面布局和处理数据请求。这时,我们的大脑才被解放出来,去思考真正有价值的“高层次问题”:
- 状态管理: 如何选择一种合适的状态管理方案(Provider、BLoC还是GetX)来避免复杂的UI逻辑和业务逻辑耦合?
- 性能优化: 如何优化拥有上千条数据的
ListView,避免滑动时的卡顿? - 架构设计: 如何设计一个可扩展的、分层的应用架构(如Repository模式),让代码更容易维护和测试?
结论: 作者并非贬低“解决问题”的能力,而是强调基础技能的熟练是进行高级别、创造性问题解决的前提。你不可能在还在纠结砖头怎么砌的时候,去设计一座宏伟的大教堂。
3. 瀑布模型在现代是否仍有不可替代的适用场景?
曾经的困惑: 我怀疑,在需求永远在变的今天,是否存在真正“需求绝对稳定”的项目,让瀑布模型得以应用。
实践后的解答: 纯粹的、从头到尾的瀑布模型在我们的项目中确实不存在。但是,“微型瀑布”或者说“类瀑布”的流程在敏捷开发中随处可见。
我们的APP开发就是一个混合模型。对于整个APP来说,我们采用敏捷迭代,每周向助教和老师演示新功能,并根据反馈调整下周计划。但对于其中一些高度确定、技术成熟的模块,我们的开发流程是线性的:
- 第三方登录集成: 需求非常明确——“用户可以通过微信/QQ登录”。技术方案成熟(使用现成的Flutter插件)。于是,我们几乎是按照“需求分析 -> 设计接口 -> 编码实现 -> 单元测试 -> 集成测试”这个线性的、类似瀑布的流程一次性完成的。这个模块的需求几乎没有变过。
结论: 瀑布模型的核心思想——“计划驱动、阶段清晰”——并没有完全过时。在敏捷的大框架下,对于那些需求稳定、技术风险低的子模块或功能点,采用一种计划驱动的线性开发模式是完全可行且高效的。现代软件开发更像是“敏捷”和“瀑布”思想的灵活组合,而非二元对立。
4. 敏捷流程的“面对面交流”是否与远程协作存在根本矛盾?
曾经的困惑: 书中强调敏捷依赖“面对面”的高效沟通,这似乎与我们这种成员分布在不同宿舍、甚至不同校区的分布式团队现实相悖。
实践后的解答: 我们团队就是一个典型的分布式团队。实践证明,“面对面”的本质是“高带宽、低延迟、信息同步”,而非物理上的“脸对脸”。现代化的工具可以很好地弥补物理距离。
我们是这样实现“伪面对面”的:
- 每日站会: 每天早上通过腾讯会议进行15分钟的站会,视频全开,快速同步进度、计划和障碍。
- 结对编程: 使用 VS Code 的 Live Share 插件,可以实时共享代码、终端,甚至进行语音通话,体验与坐在旁边几乎无异。
- UI评审: 设计师将Figma原型链接发到群里,大家可以直接在设计稿上评论,反馈非常直观、高效。
- 异步沟通: 所有的任务、Bug和讨论都记录在我们的项目管理工具和GitHub Issues中,确保信息可追溯,减少了因口头沟通导致的信息丢失。
结论: 敏捷的核心价值观(如沟通、协作、响应变化)可以通过工具和流程在分布式团队中实现。我们学到的是,工具只是载体,更重要的是建立一个纪律严明、沟通透明的团队文化。例如,我们约定:任何超过三条消息无法说清的技术问题,立刻发起一个5分钟的视频会议解决。
5. NABCD模型在动态市场下是否仍能有效指导需求分析?
曾经的困惑: 我担心NABCD模型过于静态,一次性分析完成后,可能无法应对快速变化的市场需求。
实践后的解答: NABCD模型对我们项目的启动阶段至关重要,但我们很快发现,它不应该是一份写完就束之高阁的文档,而是一个需要持续迭代和验证的“动态假设”。
- 初始NABCD (v1.0):
- N (Need): 学生需要一个工具来统一管理所有课程的作业DDL。
- A (Approach): 开发一款能手动添加、并有提醒功能的跨平台Flutter App。
- B (Benefit): 简洁、无广告、跨平台,比使用多个不同的日历或备忘录App更方便。
- C (Competitors): 通用的待办事项App(如Todoist)、手机自带日历、大脑。
- D (Delivery): 生成APK/IPA包,在同学间小范围分发。
- 迭代后的NABCD (v2.0):
- 在第一个版本发布给同学试用后,我们发现最大的痛点不是“忘记”,而是“信息孤岛”。于是,我们的NABCD演化了。
- N (Need): 学生团队需要一个协作平台,共享课程资料、同步任务进度,并看到所有成员的任务状态。
- A (Approach): 在原有基础上,增加团队空间、文件共享、任务分配等功能。
结论: NABCD模型在敏捷开发中扮演的角色是“初始导航的罗盘”,而不是“一成不变的地图”。每一次迭代、每一次用户反馈,都是对NABCD假设的重新验证。我们应该以“NABCD-as-code”的思维,持续更新它。
6. Triage过程中如何避免主观偏差,建立客观标准?
曾经的困惑: Bug修复优先级的判断极易陷入“公说公有理,婆说婆有理”的境地。如何建立客观的评估标准?
实践后的解答: 这个问题在我们的项目中非常突出。一个后端认为无伤大雅的空指针异常,可能导致前端UI在特定场景下完全崩溃。为了解决争论,我们参考业界的做法,建立了一个简单的Bug评级矩阵。
我们在GitHub Issues中创建了标签,每个Bug都必须被打上至少两个维度的标签:
- 严重性 (Severity):
S1-Blocker: 导致App崩溃、数据丢失、核心功能完全无法使用。S2-Major: 严重影响功能使用,但有临时解决方案。S3-Minor: UI显示错误、非核心功能问题。S4-Trivial: 拼写错误、对齐问题等。
- 影响范围 (Impact):
I1-All: 影响所有用户。I2-Some: 影响部分用户或特定场景。I3-One: 仅影响个别用户或在极端边界条件下出现。
修复优先级 = f(严重性, 影响范围)。一个S1 + I1的Bug(比如“所有用户登录时App闪退”)就是最高优的“Must Fix”。一个S4 + I3的Bug(比如“在某个冷门安卓机型的深色模式下,‘关于’页面的一个图标颜色不对”)则可以推迟修复。
结论: 完全的客观不存在,但一个量化的、多维度的评估框架能将主观的感受转化为相对客观的决策依据。这个框架的价值不仅在于评级本身,更在于它迫使团队成员在报告和讨论Bug时,从“我觉得这很重要”转变为“这个Bug会影响多少用户,造成多严重的后果”,从而让讨论更加聚焦于事实。
二、 在“做中学”中提炼的知识点
- 需求阶段 - 用户故事 (User Story): 我们学会了用“As a [角色], I want [功能], so that [价值]”的格式来描述需求。这比简单的功能列表更能让我们理解开发的初衷。例如,将“开发一个搜索功能”变为“作为一个健忘的学生,我希望能按课程名搜索作业,以便快速找到DDL”,这直接指导了我们的UI设计和后台查询逻辑。
- 设计阶段 - 设计系统 (Design System): 在Flutter项目中,我们初期各自为战,导致按钮样式、颜色、字体大小五花八门。后来,我们建立了一个
app_theme.dart文件,统一定义了ColorScheme、TextStyle和各种组件的主题。这不仅统一了UI风格,也极大地提高了后续页面的开发效率。 - 实现阶段 - 状态管理 (State Management): 这是我们用Flutter开发时遇到的最大挑战。从最初的
setState导致代码难以维护,到后来学习并引入Provider包,我们深刻理解了“UI与逻辑分离”的重要性。好的状态管理,是复杂App的“定海神针”。 - 测试阶段 - 单元测试 (Unit Testing): 我们为核心的业务逻辑(如DDL计算、数据模型解析)编写了单元测试。这让我们在后期重构代码或添加新功能时非常有底气,因为只要测试通过,就说明核心逻辑没有被破坏。我们真切地体会到,测试不是开发的负担,而是质量的保障。
- 发布阶段 - 持续集成/持续部署 (CI/CD): 我们配置了GitHub Actions,每当有代码推送到
main分支时,它会自动运行测试、构建Flutter应用并生成APK安装包。这让我们摆脱了“在我电脑上是好的”的魔咒,保证了每次交付的版本都是稳定可靠的。 - 维护阶段 - 日志与错误监控 (Logging & Error Monitoring): 项目后期,我们集成了
sentry_flutter这样的错误监控工具。当测试用户报告“App崩了”时,我们不再需要一遍遍地追问操作路径,而是可以直接在Sentry后台看到详细的错误堆栈和设备信息,定位问题的速度提升了数倍。
三、 个人心得与总结
回顾这一个学期,从个人项目中手忙脚乱地学习Flutter,到结对编程时与搭档为一个变量命名争得面红耳赤,再到团队项目中为了一个共同的目标而协同作战,我最大的感悟是:软件工程的核心,是解决“不确定性”的学科。
- 需求的不确定性,我们用敏捷迭代和用户故事去应对。
- 技术实现的不确定性,我们用单元测试和结对编程去化解。
- 团队协作的不确定性,我们用PM职能、沟通工具和标准化的流程(如Triage)去管理。
- 项目进度的不确定性,我们用CI/CD和版本控制去保障。
这门课教会我的,远不止是Flutter的开发技巧,更是一种工程化的思维方式:如何将一个模糊的想法,通过一系列的规范、流程和工具,一步步变为一个可靠、可维护的软件产品。那些曾经在书中读到的抽象概念,如今都已化为我项目经历中一个个鲜活的场景。这,或许就是“做中学”的真正魅力。

浙公网安备 33010602011771号