73
社区成员
发帖
与我相关
我的任务
分享| 项目 | 内容 |
|---|---|
| 这个作业属于哪个课程 | 2024 年北航敏捷软件工程 |
| 这个作业的要求在哪里 | [I.1] 个人作业:阅读和提问 |
| 我在这个课程的目标是 | 学习软件工程相关技能,符合互联网企业的求职要求 |
| 这个作业在哪个具体方面帮助我实现目标 | 通过批判性阅读,增加对软件工程的理解 |
作者在第 14.2 节中,引用亚当·斯密在《国富论》中对于分工的表述,表达了软件开发需要有独立的测试角色的观点,并称:
分工促进劳动生产力提升的原因有三:第一,劳动者的技巧因专业而日进;第二,由一种工作转到另一种工作,通常要损失不少时间,有了分工,就可以免除这种损失;第三,许多简化劳动和缩减劳动时间的机械发明,只有在分工的基础上方才可能。……在我的印象中,那些伟大的前锋大多数只管一件事——进攻。
作者似乎认为,测试的角色应“专习一种特殊业务“来可以提高效率。但是我认为,有些错误可能只有同为开发者的人才能发现其中的坑。
对于测试,我有一个观点是,这个角色和 CTF 比赛的内容很像——都是在对已有的程序进行查找漏洞的过程。而在 CTF 比赛这种模式中,你常常需要有相当高的开发基础,才能发现解题的思路。只有在一线的开发者,才会:
时刻关心新的技术潮流、框架和漏洞——CTF比赛中常常涉及新的编程语言、框架和技术。比如在 HackerGame 2023 中,有涉及到大语言模型、新 Linux 内核的题目,如果不是相关的开发者,很难想象有人能有足够的知识完成这些挑战。
拥有足够的练习,熟悉一些测试中所要解决的低级问题,有一定的实战经验——不会出现作者在第 3.3 节中描绘的“把时间都花在‘解决(低层次)问题’上的窘境。
因此我认为,与亚当·斯密时代的分工不同,当代社会很多任务是创造性的任务,而创造性任务需要多方面的知识和技能。 分工将任务分解成一个个细小的步骤,每个步骤只需要特定的知识和技能,但可能会导致思维狭窄、缺乏创造力的后果。**对于重复性的任务,分工的确有效,但对于创造性的任务,分工可能反而会阻碍效率和质量的提升。 **
作者在 2.1 节强调了单元测试的重要性:
单元测试应该覆盖所有代码路径。单元测试应覆盖所测单元的所有代码路径,包括错误处理路径。为了保证代码覆盖率,单元测试必须测试公开的和私有的函数/方法。
作者忽视的一个最大的问题是,工程问题不能不考虑 trade-off。团队的经历总是有限的。与其追求 100% 的覆盖率,不如将精力放在其他更重要的事情上。大部分软件对可靠性要求不需要那么高,单测覆盖热点路径即可。
此外,单元测试要求软件尽可能地解耦,但是对于那些重业务、重 UI 的软件,很多测试涉及到复杂的状态管理,模块很难拆分,函数常常有副作用,问题最容易出现在各个模块相互影响的时候。
对于编写单测的人来说,它还有一个问题,就是是不够直观。单测的结果都是文本形式的,难以直观呈现测试结果。
因此,我认为,对于专业性比较强、可靠性要求比较高的软件,追求单测及覆盖率无可厚非;对于主要以业务为主,涉及到复杂的状态管理的客户端程序,单元测试意义不大。这种情况下,应着重使用端到端(e2e)测试。
第 4 章的全篇内容是关于代码风格的。作者在开篇说:
程序员写的代码是给人看的,还是给机器看的?人也看,机器也看,但是最终是人在看。我们的代码要让“旁观者”看得清清楚楚。
但作者随后又在具体的内容中大谈代码的正确性和开销,比如:
异常是在“异乎寻常”的情况下出现的,它的设置和处理都要花费“异乎寻常”的开销,所以不要用异常作为逻辑控制来处理程序的主要流程。了解异常及处理异常的花销,在C++语言中,这是不可忽视的开销。
我认为,与其说代码最终是为了给人看,不如说代码风格是一种在人与机器之间的妥协,即使是在现在也是如此。
首先,没有完美的编程语言和框架。如果面对性能的严苛要求,我们是否还能保持代码的清晰易读,让旁观者一看就懂?
其次,现代的很多代码规范实际上已经固化成了 linter 程序中的各种规则甚至是语言设计,比如 eslint 就有很多针对耗时的 CSS 选择器(如 *)的规则;甚至有些项目会为 Git 设置 pre-commit 规则、为 CI 加入 linter 的过程来确保规范的实施。在这个意义上,口味之争反而是最为次要的,只要一个项目内统一就行。
最后,很多代码风格其实是为了确保程序在版本管理工具中正确 diff,维持团队的规范化。如果每个人在编辑的时候都以自己的喜好为标准随意更改代码的风格,那么就会产生很多无意义的变更。而目前的 diff 工具都是基于文本,而不是 AST 的,即使忽略掉 whitespace 也会产生很多冗余(比如是否使用 early return)。这样会增加代码阅读者的负担,不利于 review。
此外,作者在这章中罗列了很多具体的细则,也不适用于很多新的语言了。
譬如,Rust 中为 block 提供了代码块作为表达式的支持,而 Kotlin 也有 if-else expression。在这种情况下,如果要将
let a = if some_condition { 0 } else { 1 };
格式化成多行,反而会降低可读性。
对于缩进,作者的结论是使用4个空格。但我认为应该使用使用 tab,它的语义是明确的,没有异议的——tab始终都是用于行间对齐的,代表一级缩进。
作者在 10.1 节中,提出了“Persona”的概念,描述了一种从典型用户、典型场景到用例的对用户分析的方式。但是,通过这种方式,我们真的能设计出来适合我们想要的目标用户的软件吗?
我在思源楼给老人做手机使用辅导的志愿活动时有一些很有趣的经历:
有些人对软件的常见概念有自己的一套理解——比如把“消息”叫做“信号”。作者说软件应遵循常规和预期,但这种常规真的是好的吗?
有些人会在手机的各种弹窗下被误导——把自己需要的软件当作有害应用处理删除。软件常常会使用各种耸人听闻的描述,导致部分用户惮于操作。
有些人在软件的某处改动了设置,但最后却忘了自己做过的操作——而作者认为软件应记住用户的选择。或许像 Firefox 那样定时提醒恢复出厂设置更好?
我们应该如何在不同用户在效率和可用性之间权衡?目前,我没有看到比较好的做法。
在 4.5 节中,作者称:
对开发人员自身来说,结对工作能带来更多的信心,高质量的产出能带来更高的满足感。
但是,据我的体验,和他人共同完成工作的时候,常常有以下感受:一直被他人观察,容易分心,无法进入专注状态;担心自己的代码水平被对方评价,导致压力增加。
结对编程涉及到两个人之间的磨合。因此,我对结对编程具体实现的方式仍有疑惑。