AI时代的软件工程学习
在 AI 能够生成代码的今天,学习开发是否需要手写代码成为一个热门议题。过去,一个初学者往往要先学习语法,再尝试把自己的想法逐步写成程序;现在,我们可以先得到一份生成的代码,再去理解它为什么能够运行。获得代码变得容易了,学习顺序也有了更多选择。
这带来了一个值得讨论的问题:当工具可以替我们完成越来越多的工作时,我们应该把哪些能力留在自己的学习过程中?对于希望长期从事开发的同学来说,这会影响我们如何使用 AI,也会影响我们怎样理解编程基础和专业分工。
我认为,AI 时代的开发学习,应当逐步建立一种独立开发能力:理解需求如何成为程序,解释程序为什么产生某种行为,并在需求变化或结果出错时,有能力作出判断和调整。围绕这个目标,我们既需要拓宽对完整软件的认识,也需要从基础知识开始学习,更需要不断手写代码训练自己的代码直觉与逻辑组织能力。
前后端融合是大趋势
在 Web 开发中,前端主要负责用户直接接触的界面与交互。它决定信息怎样呈现、操作怎样得到响应,以及界面在不同状态下如何变化。优美的网页、炫酷的动效都是前端的范畴。前端的交互代码通常运行在用户的浏览器或者设备中(以下均统称「用户设备」),为用户提供一个清晰、容易使用软件的页面。
后端主要负责在服务器上处理请求、执行业务规则、管理数据和检查权限。将需要的数据从数据库中搬运、加工并提供给前端。后端的代码通常运行在服务器中,为用户提供复杂功能实现。前后端通常通过约定好的接口交换信息:前端将需要处理的请求交给后端,后端完成相应处理并返回结果,再由前端向用户呈现。用形象的话来说:前端负责跟用户沟通,后端负责处理逻辑。
在过去的学习中,业界常见的做法是按前端和后端划分学习方向。这种分工有其技术基础:用户设备与服务器处于不同的运行环境,界面交互、业务处理和数据存储也各有复杂性。随着系统规模扩大,专业分工能够帮助开发者在各自的领域深入,提高代码质量和开发效率。
不过,一个软件的功能通常会跨越这些边界。界面如何表达信息,取决于系统能够提供什么数据;业务规则如何执行,又会影响交互方式和数据组织。各部分之间存在持续的相互配合与彼此约束。因此,理解某一端的实现,还需要理解它与其他部分如何共同满足需求。这也是在工作中,后端开发工程师通常与前端开发工程师合作更频繁的原因。
全栈开发远早于这一轮 AI 的普及,在硅谷,全栈开发已经是一种主流的开发模式。Next.js 这样的全栈框架早已允许开发者在同一个项目中组织界面与服务端逻辑,减少不同技术之间的衔接工作。AI 对代码生成和解释的帮助,又为开发者进入不熟悉的技术领域提供了重要的支持。这些工具让个人和小团队更有条件围绕一个完整功能开展工作。
由此来看,前后端融合的趋势,主要体现在开发过程和能力要求上。一个人负责的范围可以扩大,相应地,他也需要理解更长的因果关系,判断局部选择会给整个系统带来什么影响。这种融合仍然需要清楚的技术边界。客户端与服务端承担的责任不同,权限、数据一致性和运行成本也不会因为代码由同一个人或同一个工具生成,就能自动解决。对整体的理解越深入,越需要知道哪些工作适合放在一起,哪些责任必须明确划分。
对于初学者,这意味着可以先建立对完整开发过程的认识,再逐步选择深入的方向。广度让我们理解自己的工作处于什么位置,深度则让我们有能力处理其中的复杂问题。前后端融合为学习提供了更宽的视野,而专业积累仍然决定着我们能够把问题理解到多深。
基本功仍要从手写代码开始
随着 AI 承担更多实现工作,阅读和审查代码会变得愈发重要。但是,对刚开始学习的同学来说,学习期间亲自手写代码非常必要。这里所说的手写代码,强调的是自己完成从问题到实现的推导,包括如何表达数据、拆分步骤、组织条件与循环,以及处理不符合预期的情况。
这个过程之所以重要,是因为人的理解常常比自己想象得更模糊。我们可以在自然语言层面认为某个思路已经完整,但程序要求每一步都有明确含义。
例如,“把学生按成绩从高到低排序”听起来已经很清楚,但成绩相同时按什么顺序排列,缺考的成绩如何处理,仍然需要作出约定。写成程序时,这些细节都会影响数据的表示和比较规则。在阅读 AI 书写的代码时,人脑可能会认为这些东西「理所应当」而忽略这些细节实现。但实际编码时,这些细节会成为程序运行的关键因素。在简单的系统中,这些细节可能影响不大;但在复杂系统中,任何细节处理不到位,都有可能引发系统的整体崩溃或造成严重损失。
亲自实现时,那些尚未考虑的条件、无法衔接的步骤和彼此矛盾的假设,会逐渐显现出来。为了让程序成立,我们必须把这些空缺补齐;程序运行之后,编译器和调试器又会给出进一步的反馈。预想中的结果与实际结果之间的差异,促使我们重新检查程序逻辑。经过这样的往复,变量、函数和数据结构才逐渐成为能够用于思考的工具。我们开始理解它们适合表达什么,又会带来哪些限制。
这也是为什么「有经验的开发者大量减少手写代码」的情况下,「初学者省略编程训练」非常危险。前者可以调用已有经验,对生成结果作出预测和判断,并在关键细节处进行纠错;后者还需要形成这些判断所依赖的认识。同样一份 AI 生成的代码,对不同基础的人而言,能够提供的学习价值也会不同。切勿好高骛远,如果想成为一名专业的开发者,熟练的基本功需要长期磨炼。 因此,手写训练的价值应当由它带来的理解衡量。查阅语法、使用补全、请 AI 解释概念,都可以参与学习;关键在于,当前正在学习的思考过程,是否仍然由自己认真完成。机械抄写代码很难实现这个目标,单纯规定每天手写多少行,也不足以证明能力是否增长。
随着基础逐渐形成,常规实现可以更多地交给 AI。我们仍然可以通过独立修改和重新实现,检验自己对陌生部分的理解。这个比例应当随学习目标调整:已经掌握的内容可以借助工具提高效率,尚未掌握的内容则需要保留充分的思考和反馈。
阅读和调试,让理解经得起检验
在 AI 时代,开发者会面对更多由他人或工具生成的代码。阅读能力因此值得系统训练。读懂语法只是其中的起点,更深入的阅读,需要理解数据如何加工、模块如何协作,以及整个实现依赖了哪些前提。只有把这些关系联系起来,我们才有可能判断修改的影响。这里需要留意一种容易出现的错觉:一份解释读起来通顺,并不代表我们已经能够独立运用其中的知识。理解是否扎实,可以通过预测程序行为、解释设计取舍,以及应对条件变化来检验。当原有前提发生变化,我们还能判断哪些部分需要调整,才说明自己掌握了代码背后的关系。
调试进一步要求我们把这种理解与事实相互核对。程序出现异常时,开发者需要根据观察提出假设,再用运行结果检验假设。这个过程训练的是对因果关系的判断。即使 AI 能够给出有效的修复,我们仍然值得弄清楚错误为什么发生,以及修改为什么能够解决它,否则相似的问题再次出现时,之前的经历就很难转化为自己的能力。
验证也需要独立的依据。实现与测试如果来自同一套错误假设,即使测试通过,仍然可能遗漏真正的需求。因此,判断程序是否正确,需要回到目标和约束,检查验证方式本身是否成立。能够评价一段代码,也包括能够评价支持它的证据。
亲自编写、阅读和调试,应当在学习中相互促进。编写让我们理解方案如何形成,阅读让我们接触更多组织方式,调试与验证则帮助我们发现认识中的缺口。AI 可以参与每一个环节,而学习者需要在这个过程中逐渐形成自己的判断。
从编程基础,走向系统的认识
对于从零开始的同学,我建议先通过一门语言建立稳定的编程认识。语言的选择可以结合课程和兴趣,学习时则应把注意力放在其中共同的概念上:程序怎样表达状态,怎样组织计算,怎样把复杂问题分解成可以理解的部分。掌握这些关系以后,接触新的语言和工具才有可以依靠的基础。
进一步学习时,数据结构、算法、操作系统、计算机网络和数据库仍然值得投入时间。它们帮助我们理解软件运行所依赖的条件:计算需要资源,通信存在延迟和失败,数据的组织与更新需要遵循规则。开发工具可以封装许多细节,但设计和排查问题时,我们仍然可能需要回到这些条件,解释系统的行为。
这也说明,基础知识的价值会延伸到工具选择和方案判断。只有理解一个问题受到什么约束,我们才能评价某种抽象是否合适、某个方案付出了什么代价。AI 可以提供多个看似可行的答案,而基础知识使我们有能力进一步追问:它们分别在什么条件下成立,哪一种更符合当前需要。
这些知识可以与实践逐步结合。实践让我们遇到真实的限制,课程和教材则帮助我们把零散经历组织成较完整的认识。一个项目所涉及的内容有限,系统学习能够补足其中尚未遇到的部分;基础概念如果长期缺少运用,也容易停留在能够复述的程度。两者之间持续往返,才能让知识逐渐成为解决问题的能力。
在此基础上,还需要理解软件随时间变化的过程。第一次完成实现,只能说明当前方案在一定条件下能够工作。需求变化、缺陷修复和团队协作,会继续检验它是否容易理解和修改。版本管理、测试与文档的意义,也由此变得清楚:它们帮助我们保存修改的依据,让软件在持续变化中仍然可以被理解和维护。
所以,学习的深入可以体现在我们能够承担的责任逐渐扩大。从解释一段程序,到理解模块之间的关系,再到判断一次修改对整个系统的影响,每一步都需要此前的知识作为支撑。AI 可以帮助我们接触更广的工作范围,学习进度则仍应以自己能够理解和验证的范围为依据。
总结
AI 时代的开发学习,需要同时关注两个方向:在广度上,理解前端、后端与数据如何构成一个完整的软件;在深度上,通过亲自编写、阅读、调试和系统学习,建立可靠的基本功。
从手写代码开始,是为了亲自经历思路成为程序的过程。在这个过程中形成的理解和直觉,会帮助我们阅读更复杂的代码、评价 AI 的输出,并承担更完整的开发工作。随着工具承担越来越多的实现,我们可以把更多精力投入需求、设计和验证;而这些工作的最终质量,取决于初学阶段的基本功水平。