在vibe coding概念火了之后,这个理念很快暴露了它的问题:它只关注最终的效果,不关心代码层面是如何实现的,但是AI的特点又是"输入决定输出",这就意味着,如果不对AI进行任何约束,那么AI就会像脱缰的🐎一样横冲直撞。因此才会有"spec工程"和后来的"harness工程"这两个概念。"harness",本意就是指马术运动中的马具,是马镫、马鞍、辔头、马嚼子、缰绳、马鞭......的统称,是用来约束和指示🐎的行为和方向的,在AI编程领域则引申为"约束工程"的意思。
但是网上的很多介绍harness工程的文章,往往都是先告诉你harness工程的理念,然后教你如何在你的工程中实践harness工程。但这样的叙事逻辑有一个问题------脱离实际,没有做到实事求是和"具体问题具体分析"。拿着这样的文章从头开始一个harness工程,没问题;但假如你按照这种文章里面的说法,去把一个现有的项目改造成harness工程,你就会发现这样的文章就是驴唇不对马嘴。先给大家看看这种文章里说的一个标准的harness工程会有多少约束文件吧:
plantext
repo/
├─ AGENTS.md # 🌟 通用OpenSpec规则,AI的导航地图
├─ CLAUDE.md # 🌟 Claude系统提示词,定义AI的基础行为
├─ REVIEW.md # 🌟 只读评审代理提示词,规范评审标准
├─ docs/ # 📚 项目知识库目录,存放所有项目知识
│ ├─ architecture/ # │ ├─ 🔥 整体架构知识
│ │ └─ index.md # │ │ └─ 项目架构总览,让AI快速了解系统整体设计
│ │ └─ implicit-contracts.md # │ │ └─ 隐性业务约定/项目坑点,核心中的核心
│ ├─ product/ # │ ├─ 🔥 产品知识
│ │ └─ index.md # │ │ └─ 产品规则,明确业务逻辑边界
│ ├─ standards/ # │ ├─ 🔥 相关规范
│ │ ├─ testing.md # │ │ ├─ 测试规范,明确测试要求和标准
│ │ └─ database.md # │ │ └─ 数据库与SQL规范,规避数据层风险
├─ openspec/ # 📚 OpenSpec执行目录,管理变更生命周期
│ ├─ changes/ # │ ├─ 变更目录,存放当前和历史变更
│ │ ├─ <changes名> # │ ├─ 当前正在执行的change,每个change对应一个需求
│ │ │ ├─ specs/ # │ │ │ ├─ 该change的工作原理说明
│ │ │ ├─ proposal.md # │ │ │ ├─ 需求实现提案,拆解需求边界
│ │ │ ├─ design.md # │ │ │ ├─ 具体执行方案,明确技术实现细节
│ │ │ └─ task.md # │ │ │ └─ 执行步骤节点,拆解具体工作内容
│ │ └─ archive/ # │ │ └─ 归档文件,存放已完成的change
│ └─ specs/ # │ └─ 当前系统工作原理说明,让AI了解系统现状
├─ .claude/ # 📚 Claude项目级配置目录,核心约束层
│ ├─ settings.local.json.example # │ ├─ 🔥 项目级权限设置,定义AI可操作范围
│ ├─ skills/ # │ ├─ 🔥 项目级Skills,团队专用能力沉淀
│ │ ├─ prepare-review/ # │ │ ├─ review前变更审计,生成评审摘要
│ │ │ └─ SKILL.md # │ │ │ └─ 该skill的具体执行逻辑
│ │ ├─ spring-architecture-review/ # │ │ ├─ Springboot分层架构检查,避免分层混乱
│ │ │ └─ SKILL.md # │ │ │ └─ 架构检查的具体规则
│ │ └─ sql-risk-review/ # │ │ └─ SQL、Mapper、批量更新等风险检查
│ │ └─ SKILL.md # │ │ └─ SQL风险检查的具体规则
│ ├─ agents/ # │ ├─ 🔥 子代理,承担专项审查职责
│ │ └─ reviewer.md # │ │ └─ 只读评审代理,做独立代码审查
│ └─ hooks/ # │ └─ 🔥 Hook,硬护栏的核心实现
│ ├─ guard_write.py # │ ├─ 文件写入保护,拦截高风险路径写入
│ ├─ ensure_change_context.py # │ ├─ 上下文变更保护,检查change是否存在
│ └─ run_checks.sh # │ └─ 编译检查,自动执行编译、测试等
├─ src/ # 📚 项目源代码目录,和常规Spring Boot项目一致
│ ├─ main/
│ │ ├─ java/
│ │ └─ resources/
│ └─ test/
│ ├─ java/
│ └─ resources/
├─ pom.xml # 📚 Maven配置文件
└─ .gitignore # 📚 Git忽略文件
这么长的约束文件,基本上这一个harness工程文件就占了整个项目近50%的文件数量了。除非是Claude这种1M上下文的大模型,否则像GLM-5.1这种上下文只有200k的,这些文件还没读完,上下文窗口就满了,更别提让AI干活了。但问题是,并不是所有人都用得起、用得到Claude,那是"何不食肉糜"的傲慢。因此,这就是我说这种文章"脱离实际"的原因之一。
原因之二在于,这种目录结构没有普适性。编程语言、是个人项目还是团队协作项目、是企业级的项目还是个人项目、开发者的个人习惯......这些都是变量,要不然你以为为什么总有人说"计算机就是个黑箱"?你以为我为什么在上一篇文章中说"技术人员更应该学习政治理论"?因为只有马列主义才能让技术走在正确的道路上,一个出色的软件工程师必然也是一个优秀的马列主义者。如若不然,那么他的出色也只是一个偶然罢了。
这就好比原先的模具是用来统计🍎的个头数据的,现在你在没有调研的前提下造了一个模具,反过来衡量🍎有没有达标一样,这就是典型的"倒果为因"。应该是项目决定了harness工程应该长什么样,而不是反过来。
更何况有些写harness工程文章的作者,他自己都对harness工程一知半解,写出来的文章像大学老师靠念PPT上课一样,文章内容漏洞百出,这样的作者写出来的文章又有什么指导意义?
而且,随着AI大模型越来越智能、能力越来越强,其所需要的约束也是越来越少的。对于Infra大模型,过多的约束反而限制了它们的发挥,其性能反而下降,结果不禁让人皱眉:"这大模型怎么这么傻,我明明让他不要干/要干这件事,怎么它不遵循我的提示词?"
在上一篇文章在AI时代,如何从0接手一个项目?里,我们从0接手了一个项目,现在我们要为这个项目做自己的贡献了。但是在正式开始之前,我们还需要做一些准备工作------对IDE和这个项目做一些个性化设置。
TRAE(The Real AI Engineer)是一款字节跳动旗下的主打业余开发者的编程需求的AI IDE。虽然在企业开发方面赶不上隔壁阿里的Qoder,但对只掌握最基础的编程常识的新手个人开发者非常友好,而且它引用文件是#,切换Agent是@,再加上TRAE可以上传技能压缩包,而且SOLO模式支持调用子智能体,这些都非常适合我的个人习惯,深对我意,故此用TRAE来当案例进行讲解,其他AI IDE都是同理。
开始前的准备工作
需要注意的是,随着AI大模型越来越聪明,进行约束的提示词必然是越来越少的。而且不同的AI对不同的提示词有不同的反应,所以每次大模型更新,原则上来讲,规则、技能、命令、智能体......都需要重新进行调整。Claude模型更新之后,Claude code的系统提示词删除了上万字。所以,还是那句话:没有什么东西是不变的,你也要随时调整你的提示词。
规则
规则,说到底就是上下文工程与harness工程中非常重要的一部分。AI能否生成你想要的代码,规则是很重要的因素。同时,规则也是上下文的一部分,当你发送请求时,规则也将会和系统提示词和你的请求一起作为AI大模型的输入。具体可看下文《项目文档》部分。
根据影响的范围不同,规则分为"全局规则"和"项目规则"两部分。全局规则影响的是每一个打开的项目,因此如果这个规则对你打开的每一个项目都适用,那么就可以考虑创建一个全局规则;如果这条规则只对某一个项目有效,别的项目都不适用,那就设置成项目规则。
设置规则最重要的原则就是,每一份规则文件都只约束一件事。如果你的规则文件讲了不止一件事,那就说明你的规则还可以继续拆分。这么做的好处有两个:
- 让AI不蒙圈
- 提高规则的维护性,降低维护成本
是的,这篇文章提到的所有东西,都不是静止的、一成不变的 。随着你的成长、项目的持续推进,规则、技能、项目文档......都是要持续维护、持续改变的。而且每个项目都是独一无二的,从这一点来说,虽然本文所讲的东西都是相通的,但是具体落实到不同的项目上,所应用的方法论还是不同的。一定要具体问题具体分析。
全局规则
一个开发者所接触到的项目不止一个,因此对于适用于多个项目的规则的情况,全局规则便诞生了。全局规则的路径是C:\用户\用户名\.trae-cn\user_rules\。由于全局规则会作用于每一个打开的项目,所以推荐将以下内容放到全局规则里:
- 本机开发环境和硬件参数
- 放之四海而皆准的设计原则或产品设计思维
- 通用的编程原则(如:特殊的语法规则、提高代码复用性,避免"重复造轮子"的提示词)
- 你的个人背景信息和个人喜好(如:我是自闭症人士,需要用以下原则跟我交流;我是ADHD患者,在改完代码之后需要生成一份mermaid图表;我不会编程,因此你的语言要通俗易懂;你是一袋猫粮......)
- AI和你的对话交流风格
项目规则
项目规则的路径则是项目根目录\.trae\rules\。项目规则只对当前的项目生效,所以可以将如下东西放到项目规则中:
- 项目目录结构(可选,TRAE已经内置了"索引与文档",输入
#workspase即可自动全局检索与问题有关的文档。而且随着大模型越来越聪明,它会自己搜索工作空间,找到问题有关的文件,现在的GLM-5.3已经可以做到了) - 本项目的产品设计原则
- 本项目独特的编码规则
- 软件架构原则与设计(参考下文《站在编程门外汉的角度:首先要成为软件架构师》部分)
- 产品文档与需求文档
技能
技能(skill)是Anthropic公司发明的概念,旨在将一整套提示词封装成一个能力,安装这个技能,AI就相当于自动拥有了这个能力。一个技能文件夹,通常由以下四部分组成:
- SKILL.md(技能定义,包含元数据和主要指令,是核心文件)
- references/(背景知识和参考文档)
- scripts/(可执行脚本)
- assets/(静态资源)
从具体实践反馈来看,人们往往会一口气装四五十个技能,但是真正被调用的技能寥寥无几。这主要会带来两个问题:
- 上下文腐化。要知道,
skill.md是用YAML格式+Markdown语法实现的。安装技能后,YAML的description部分会作为上下文的一部分来作为AI的输入,从而让AI知道它具备哪些能力。现在的AI大模型多是MOE架构,对于这个架构,上下文过多就会造成上下文腐化,降低大模型的输出质量。 - 浪费Token。因为技能的description部分会作为上下文的一部分来作为AI的输入,而且每发送一次请求都会携带这部分信息,即使不用也会发送,因此假如一个技能用不上,一来二去,也会造成巨量的Token浪费。
因此,为了避免上下文腐化,除了"一个对话窗口只完成一个任务"之外,定期清理不用的技能也是非常有必要的。另外,有的人总抱怨额度不够用,"没个几个亿Token根本完不成企业级开发任务",除了反思一下自己的规则有没有问题之外,还应该检查一下自己到底安装了多少技能?一般来说,最多30个技能就已经够用了。
而且,从技能市场下载的技能,未必适用于自己的项目。举个例子,有一个技能励志做适配整个前端开发技术栈的,react和Vue都支持,于是技能文件特别冗长。但是你的项目只用Vue,根本用不上react,于是对你来说,react的那一部分Token就浪费了;同时,为了适配所有的情况,skill.md写得特别抽象,导致这个技能的调用率特别低,于是上下文腐化和浪费Token的问题又来了。因此,对于下载的技能,根据自己的需求进行个性化改造也是非常有必要的。
命令
命令,相当于提示词模板,在TRAE中用/调用。在调用了命令之后,我们依然可以输入提示词。根据TRAE的设计,在调用了命令之后,我们还可以输入补充性的提示词。这就给我们提供了更多的使用方法。TRAE内置三个命令:spec、plan和goal。
- plan:在执行之前,先生成待办清单,然后照着待办清单执行任务,避免AI忘记自己要干什么。TRAE也支持大模型自动创建待办清单
- spec:在harness工程出现之前的AI约束工程概念。该命令会生成3个文档:任务主旨文件、待办清单、验收清单。人类只需要阅读检查任务主旨文件(
spec.md)即可,其他两个是给AI看的 - goal:相当于Claude code和codex的
loop(循环)。以任务目标为导向,不达目的不罢休
我们也可以自定义一个命令,格式和前文skill.md很像,同样是YAML+Markdown实现。比如这是我自定义的通过问题现象来反推问题原因的命令,和全局规则组合使用:
yaml
---
name: 反向定位
description: 当AI说"已解决"但问题依旧时,不要让它继续尝试修复,而是用以下提示词强制它进行逆向推理。
---
根据项目宪法,我现在遇到的问题是:【描述错误现象】。
1. 请从错误现象出发,逆向推导,找出导致该现象的最直接原因。
2. 然后找出导致该直接原因的上一级原因,依此类推,直到定位到具体的代码行。
3. 必须使用"因为【A变量/函数】,导致【B变量/函数】,最终引发【现象】"的格式回答。
4. 最后,请指出修复【A】的具体方法,并说明修复后【B】会如何变化。
这个命令主要是降低AI幻觉的。众所周知,AI的幻觉无可避免,有时会假装自己写的代码已经通过了测试,有时又会假装你自己找到了bug的关键原因。有了这个命令,AI就可以真正去研究,而不是瞎编。
智能体
在Anthropic发明技能之后,有很多人都分不清智能体和技能有什么区别。依我愚见,智能体和技能最大的区别就在于,技能没有人格,而智能体有人格。
技能描述的是一个行为本身,比如同样都是编写Vue前端代码的功能,技能只是告诉AI该如何写代码,顶多可以调用/scripts文件夹里的自动化代码;但是智能体却需要自主决策,能够感知环境、理解意图、规划任务和调用工具,是"主动的任务执行者":这里为什么不需要重复造轮子,那里为什么需要代码复用。因此,技能和智能体并不是一回事,也不是冲突的东西,一个前端开发工程师智能体,倘若搭配Vue技能,其能力势必会更上一层楼。
MCP工具
MCP工具是最早实现了让AI调用工具的方式。AI生成命令,通过调用MCP工具,即可实现AI与现实软件基建进行交互。比如通过MCP工具,AI可以将代码托管到github或者gitee上,或者从上面拉取代码;或者通过MySQL MCP工具,直接运行SQL语句来查询数据库的表结构或数据;还可以利用context7这个MCP工具,查询最新的官方开发规范,从而弥补AI大模型本身知识不够新的问题,等等。以前还需要安装操控浏览器的MCP工具,但是随着现在的Agent普遍都内置无头浏览器了,这类MCP工具也无需安装了。这更加证明了本文开头所说的,任何事物没有一成不变的。
正式开始:代码生产出来,首先是负债
代码生产出来,首先是负债。
这句话是阿里云CIO蒋林泉在团队中反复强调的一句话。如果生成的代码无法对业务客户产生正向价值,那么规模化地生产代码,本质上就是规模化地生产负债。毕竟,任何代码进入生产环境后,要即刻引入维护成本、增加系统复杂度,其与现有代码的依赖关系需要持续管理。 代码能否转化为资产------即对业务客户产生正向价值------是不确定的。
他的经典逻辑是:增加的大量代码「可能」是资产,但「一定」是负债。理解这一点,是后续全部AI工程实践的逻辑基础。
要知道,本文的标题是《在生产环境中和AI协作编程》,生产环境和个人做一个demo是截然不同的。个人做的demo只需要功能实现就行了,代码是否有bug、页面是否美观,这些反而不是最重要的。但是demo和高度可用的商业化项目相比,除了外观和功能之外,最重要的就是------商业化项目代码是需要稳定的,是需要长期维护的,就像蒋林泉所说的一样,它是一个负债。负债的意思就是,它需要持续投入成本进行维护和升级。代码本来也不应该成为一个次抛产品。
因此,本系列的两篇文章,以及软件工程这门学科,都是在教你如何生产出可靠的商业化项目,而不是一个可以被随时抛弃的、bug一堆还难看的、随便一个外行都能做的demo。所以,我希望你们能抱有一种严谨的、认真的工匠精神,借助AI来打磨一件艺术品,而不是"玩玩就行了"的心态。
站在程序员的角度:程序员要先成为优秀的业务员
计算机科学研究的是"一个问题能否被计算",以及"如何计算";软件工程则是将"如何计算"落实到具体的代码。计算机科学研究的是理论,软件工程研究的是实践。但无论是计算机科学还是软件工程,归根结底都是服务于一个具体的需求------"一个问题能否被计算"。这个需求,就是现实中无数公司的一个个具体的业务逻辑。如果你是程序员,却不懂业务,那就像空有屠龙术和屠龙宝刀,却没有龙一样,空有一身本领却无处施展。我在大连交通运输集团写了半年的代码,更加体会到"程序员要懂业务"的重要性。
很多程序员其实只会写代码,并不懂公司具体的业务。以前的程序员是对照《产品架构文档》和《项目文档》来写后端代码,照着设计师发来的图片来写前端代码,我们称这种程序员为"码农",因为施工图纸已经给他了,他只要能看懂图纸,然后把图纸变成现实就行了;但是现在经济下行、企业降本增效(又让马儿跑,又不给马儿吃草)的当下,企业对员工的希望就变成了一人身兼多职。因此程序员不能再当码农了,而是要有主观能动性,如何站在用户的角度去设计产品,什么样的软件架构才更符合用户画像------是的,不光要有产品思维,还要有软件架构师的判断经验。未来互联网行业的各个岗位肯定是越来越交汇融合的:产品经理要会写代码,软件架构师要会分析用户需求并设计产品,程序员要懂UI设计和软件架构,设计师则要会前端开发。人力资源和财务会计也需要借助AI编程来解决自己工作中的一些痛点和自动化的需求。于是现在不仅"人人都是产品经理"了,而且也"人人都是程序员"了。这个改变就是因为在AI的帮助下,每个人都成为了超级个体所导致的。
站在编程门外汉的角度:首先要成为软件架构师
上一篇文章里,我向大家介绍了开发一款软件,会有多少职业参与进来,彼此之间的职能和协作方式是怎样的。其中最重要、对软件影响最大的,就是软件架构师,因为他直接决定了软件以什么形态出现。因此,如果你不会写代码,却想让AI帮你开发一款软件,那么哪怕你不懂产品经理的专业知识,你也应该学习一些软件架构的知识。
AI的工作原理,就是数学领域的"拟合"。AI根据输入数据,然后经过拟合,决定输出什么内容。但问题是,现在的AI并不是世界模型,它们不懂得什么叫因果,不懂得什么叫规律,只是人类对它们说"我说什么,你记住就完事了"。于是AI只能做到局部最优解,做不到全局最优解。虽然GLM-5.3基于GLM-5.2做了编程领域的后训练,强化了网络安全和后端编程能力,但是软件架构的能力依然需要人类有意识的提及,它才能加载软件架构的知识,否则它是不会主动站在全局最优解的层面去考虑问题的。因此,想让AI生产企业级商业项目的代码,用AI的人就必须具备软件架构的知识。
一个最简单、最容易理解的软件架构知识,就是代码复用。比如我在给大连交通运输集团开发内部综合管理系统时,这个系统需要上传文件的地方很多。那么,上传文件的功能能不能给它抽出来,成为一个独立的模块,这样在需要用到的地方,就直接引用这个模块不就行了吗?这样做的好处就是降低了模块之间的耦合度,修改一个地方,全局都生效;即使这个模块有问题了,也不会影响依赖这个模块的功能。但是这样的软件架构,AI可不会主动设计。所以我认为,在AI时代,软件架构比编程语法更重要。编程语法只是一个技能,AI学得别人快,也比人更懂。但是软件架构是经验,是意识,现在的AI还达不到提经验和意识的程度。
如果有什么软件架构的书是适合门外汉或者新手阅读的,那我推荐马克·理查兹的《软件架构:架构模式、特征及实践指南》机械工业出版社。读完这本书,然后多实践,经验多了,你就能设计出非常实用可靠的软件架构了。AI是能力放大器,到时,它就能够将你的软件架构能力放大成一个优秀的商业级项目。
项目文档
项目文档不仅是给人看的,同时也是AI非常重要的上下文。上一篇文章我向大家介绍了不同的岗位需要写哪些文档。但其实,其中有很多文档都是用来负责团队沟通的,并不直接生产代码------事实上,一个开发团队中,有80%的时间都浪费在团队沟通上了,只有20%是真正用来生产代码的。具体在下文《更宏观的视角》中讲解。
AI大模型会越来越聪明,能力会越来越强,因此项目文档也需要不断调整、删除。但是有一些文件依然不可或缺。我推荐一个项目中包含以下内容:
- 《产品需求文档(PRD)》:明确产品有哪些功能,用户操作路径是什么
- 《系统设计说明书》:明确软件架构,让AI知道有哪些接口,设计新接口时,应该遵循什么格式
- 《产品设计说明书》:确立统一的品牌设计及UI设计原则,避免让用户感觉页面及操作设计存在割裂感
- 一个专门用来记录团队之间的口头约定的文档,包括"这里为什么不能这么做,而应该那么做",还有踩坑记录
- 其他有必要的文档,如任务书
之所以要将口头约定落实到文档中,是因为有些时候,会议和团队沟通也会做出足以影响软件架构的决定。这背后也隐藏着一个非常重要的AI使用理念:要把AI作为团队成员之一,而不只是单兵工具。 既然AI是团队成员之一,那么你不让AI参加会议,导致AI和你们团队存在信息差,到时候AI生成的结果存在问题,甚至产生影响深远的漏洞,那你这算不算是在霸凌AI?所以,要么让AI参加会议,要么将会议结果落实到文件上,总之你们得让AI知道你们会议的讨论结果是什么。
至于踩坑记录,是因为AI没有真正的记忆,新开一个窗口就像是失忆了一样。所以同样需要让AI知道"当初这里为什么是这么设计,而不是那样做",这样AI生成的代码才不会重蹈覆辙、一错再错。
Git
Git是Linux之父Linus对开源社区的又一伟大贡献。它的诞生就是服务于Linux代码的协同开发的。一个开源项目的贡献者可能来自全世界各地,此时就不可能让所有的开发者都坐在同一间办公室里了。就算是都坐在同一个办公室里,每个人贡献的代码如何管理也是一个问题。而Git,就是为了解决这个问题的。它的理念如此先进,以至于哪个程序的代码不用Git来管理,那简直就是原始社会。
Git本身和AI没什么关系,但是目前我们人类还没办法完全信任AI写出来的代码,因此我们需要Git做版本管理,一旦出现bug,我们可以直接回退版本;同时我们可以将AI的成果持续上传到代码托管平台,这就叫"CI/CD(持续交付、持续集成)"工作流。
Git的使用方法也特别简单,网上也有很多使用教程。下面是一套从零到能干活的 Git 速成指南。
准备工作:安装 Git
去 git-scm.com下载 Git,安装后打开终端(Windows 用 Git Bash,Mac/Linux 用自带终端),输入 git --version 能看到版本号即安装成功。
一、配置 Git(一次性设置)
安装 Git 后第一件事是设置你的用户名和邮箱------每次提交代码都会带上这个信息,以后不可更改。
bash
# 设置全局用户名和邮箱(--global 表示本机所有项目共用)
git config --global user.name "你的名字"
git config --global user.email "你的邮箱@example.com"
# 查看已配置的信息
git config --list
说明 :如果某个项目想用不同的用户名/邮箱,去掉
--global在项目目录下单独设即可。
二、给项目添加 Git(初始化仓库)
假设你已经在本地有一个项目文件夹:
bash
# 进入项目目录
cd 你的项目文件夹
# 初始化 Git 仓库(会在当前目录生成一个 .git 隐藏文件夹)
git init
# 添加所有文件到暂存区(staging area)
git add .
# 提交到本地仓库,-m 后面写本次提交的说明
git commit -m "Initial commit(首次提交)"
原理 :
git init创建仓库 →git add把文件放入"暂存区" →git commit把暂存区的内容真正存到 Git 仓库中,生成一个唯一的 commit ID。
三、创建分支(Branch)
分支让你可以在独立的代码线上开发新功能,不影响主分支的稳定版本\[5]。
bash
# 查看当前有哪些分支(* 号标记的是当前所在分支)
git branch
# 创建新分支(例如叫 dev)
git branch dev
# 切换到新分支
git checkout dev
# 上面两步可以合并成一步:创建并切换
git checkout -b dev
实践建议 :不要在
main(或master)主分支直接改代码。开发新功能就新建一个分支,开发完成后再合并回去。
四、合并分支(Merge)
在 dev 分支开发完功能后,要把它合并到主分支。
bash
# 先切回要合并到的目标分支(比如 main)
git checkout main
# 把 dev 分支的内容合并到当前(main)分支
git merge dev
如果合并时没有冲突,Git 会自动完成合并,生成一个合并提交。
如果合并时有冲突,Git 会提示哪个文件有冲突。打开那个文件,你会看到类似这样的内容:
<<<<<<< HEAD
当前分支的代码内容
=======
被合并分支的代码内容
>>>>>>> dev
你需要手动编辑这段代码,决定保留哪部分、删除哪部分,然后:
bash
# 标记冲突已解决
git add 冲突文件名
# 完成合并提交
git commit
也可以用图形工具
git mergetool辅助解决冲突。
五、推到远程代码托管平台(Push)
你需要先有一个远程仓库(GitHub / GitLab / Gitee / 公司内部的代码托管平台)。
方式 A:本地已有项目,关联远程仓库
bash
# 在远程平台上创建一个新仓库,复制它的远程地址(HTTPS 或 SSH)
# 然后在本项目里添加远程仓库地址
git remote add origin https://github.com/你的用户名/仓库名.git
# 把本地 main 分支推送到远程(-u 设置上游,以后只用 git push 即可)
git push -u origin main
方式 B:从远程克隆已有项目到本地
bash
# 克隆远程仓库到本地
git clone https://github.com/用户名/仓库名.git
# 进入项目目录
cd 仓库名
克隆之后,远程地址已经自动配置好,可以直接用
git push和git pull。
推送其他分支:
bash
# 推送到远程的 dev 分支
git push origin dev
六、从远程拉取别人的代码(Pull)
当同事推送了新代码到远程仓库,或你在另一台电脑上改过后,需要拉取最新代码:
bash
# 拉取远程 main 分支的最新内容,并自动合并到本地
git pull origin main
# 或者(如果已经设置了上游分支):
git pull
git pull=git fetch(下载远程更新)+git merge(合并到本地) 。拉取时如果本地也有修改,也可能产生冲突,解决方式与上面合并冲突一样。
七、比对代码差异(Diff)
在提交或推送之前,先看看自己改了哪些内容,这是非常实用的习惯。
bash
# 查看工作目录(尚未 git add)与暂存区的差异
git diff
# 查看暂存区(已经 git add 但未 commit)与上次提交的差异
git diff --cached # 或者 git diff --staged
# 查看工作目录与最近一次提交(HEAD)的差异
git diff HEAD
# 比较两个分支的最新差异
git diff main dev
# 只看文件名,不看具体内容
git diff --stat
git diff 输出中,以 - 开头的是删除的行,以 + 开头的是新增的行。
八、完整的日常协作工作流(一张图总结)
假设你加入了一个团队项目,典型的一天工作流如下:
git
# 1. 第一次拿项目
git clone https://公司仓库地址/项目名.git
cd 项目名
# 2. 每天开工前,拉取最新代码
git checkout main
git pull
# 3. 创建一个新分支开发自己的功能
git checkout -b feature/我的功能
# 4. 写代码... 写完后看改了啥
git diff
# 5. 把改动加入暂存区并提交
git add .
git commit -m "完成了XXX功能"
# 6. 推送自己的分支到远程
git push origin feature/我的功能
# 7. (可选)在 GitHub/GitLab 上发起 Pull Request,让同事审查代码
# 8. 审查通过后,合并回 main 分支
git checkout main
git merge feature/我的功能
# 9. 把合并后的 main 推送到远程
git push origin main
常用命令速记表
| 阶段 | 命令 | 说明 |
|---|---|---|
| 配置 | git config --global user.name/email |
设置身份 |
| 初始化 | git init |
本地创建仓库 |
| 查看状态 | git status |
看哪些文件改了/没追踪 |
| 添加 | git add <文件> 或 git add . |
加入暂存区 |
| 提交 | git commit -m "说明" |
生成一个版本 |
| 分支 | git branch / git checkout -b 分支名 |
查看/创建分支 |
| 合并 | git merge 分支名 |
合并指定分支到当前分支 |
| 推送 | git push origin 分支名 |
推送到远程 |
| 拉取 | git pull |
拉取远程最新代码 |
| 差异 | git diff |
查看文件改动 |
| 克隆 | git clone 远程地址 |
从远程下载项目 |
建议 :可以去 learngitbranching.js.org 在线交互式练习分支和合并操作,对新手特别友好。
现在你只需要按上面第一步 → 第八步的顺序在你自己的项目上过一遍,就能上手日常工作流了。即使遇到自己不会的问题,也可以求助AI,或者直接让AI自己操作,甚至可以设置自动化工作流,AI改完代码之后直接自动化上传。现在AI特别擅长使用Git。
后记:更宏观的视角------《人月神话》
《人月神话》是软件工程专业里大名鼎鼎的一本名著。"人月"的意思是每人每月,是计算工作效率的一个单位。这本书主要以两个观点而闻名:
- 一个项目,假设原来需要10个人一年完成,能不能再加一倍的人,变成20个人半年时间交付?答案是做不到。该命题所揭示的核心悖论在于,增加人力并不能线性缩短项目周期,原因在于人际沟通复杂度呈几何级数增长,且新成员缺乏系统上下文、需要高成本的知识传递。
- 程序员只有20%的时间用来编程,其他的时间都在开会、审核别人的代码、确认需求等沟通事务上。
因此,AI只能让20%的时间变成2%,但是无法压缩那80%的时间。
随着AI大模型的进步,给AI看的文档越来越少,给人类看的文档却越来越多,对此我深有体会。
一个企业级的项目,光是数据库文件(SQL语句组成的文件)都有近10万字,而GLM-4.7时代,因为上下文只有200k,所以还需要专门写一篇文档作为项目目录的索引,然后再在agent.md文件中引导AI阅读这份索引,这样AI的路径就是:agent.md→目录索引→目标文件夹,然后再思考如何完成任务。
但是从GLM-5.2时代开始,AI自己就能生成查询命令,调用Agent工具直接定位到SQL文件里面具体的SQL语句,然后再利用1M上下文的能力,根据SQL语句去追查mapper层(我们集团的项目是用springboot框架开发的)的语句映射是否有问题。(顺带一提,这种解决问题的思路,也是软件架构思维。)于是,项目目录索引文件便不再需要了。
索引文件只是举个例子,实际上不必要的文件和规则越来越多,比如类似于《阿里巴巴代码编程语法规范》这种东西也没必要作为规则约束了,因为这些东西早就存在于后训练里被反复强化学习过了。但是软件架构至少在现在依然很重要,因为AI提高了生产代码的效率,但是AI却并不具备大局观,这就导致AI让问题越来越多、出问题的频率也越来越频繁,而且随着AI越来越强,AI挖掘的漏洞也越来越严重。
对此,蔡崇信也不断提到"左移"这个概念。所谓"左移",就是在问题出现之前解决它,也就是说,要在设计软件架构,甚至设计产品功能时就提前避免问题。他指出,左移的想法是对的,但投入太大、ROI不够。左移本质上,就是「跨部门转移责任」。 也就是说,原来在右边承担责任的人,要把责任转移到左边,但左边的人接不接、认不认、有没有能力承担?这要付出巨大的软件工程能量,还有组织摩擦力。所以实际上,左移的难度非常大。而到了AI 时代,原来那些上下文和知识资产,AI可以从存量代码里抽取出来,再加上增量的 PRD、Spec 等上下文,业务复杂系统能够简化成一个大家可理解的上下文框架 ------ 无论对新成员还是不同岗位之间,都能在一个业务链路里更低成本、更高效地对齐。至于上下文腐化的问题,蒋林泉给出他的一个观察:AI生成的代码更规整,用规范化的代码还原Spec,准确度越来越高,上下文一致性越来越好,于是,维护Spec的成本反而在下降,「文档与现实脱节」的老问题会随时间慢慢消失。
而且左移了之后,相当于第一步是确定需求,需求确定了,就相当于确定了正常情况和异常情况,这样就能把测试的步骤提前,先写测试用例,然后再让AI编程,这样也可以提前知道AI编写的代码有没有问题,降低了带病上线的可能性。而且将测试用例告诉AI,也可以让AI生成更符合预期的代码,事故率也下降了。
在我看来,AI最起码打破了其他行业的人对开发一款软件的刻板印象,以为"软件开发只是程序员这一个岗位的事"。但实际上,从设计产品到设计UI,从软件架构到前端开发、后端开发,最后再到部署运维,这一整条链路上的所有环节都很重要。AI让两个软件开发工作流重新发扬光大了,一个是最小化系统开发(MVP),也就是"CI/CD(持续交付/持续集成)",小步快跑式的持续迭代;另一个就是"左移",从一开始就想好这个软件应该怎么设计,没想好的话就先开发一个demo,然后用这个demo不断跟客户确认需求,对这个demo进行最小化系统开发,等所有需求都确定了,客户说"这就是我想要的",然后再开发正式版。正如蒋林泉所说:
在AI时代,定义清楚一个问题,这个问题可能就解决了95%。
他甚至给出更大的可能性,按照当下AI的能力和进化速度,我们定义问题的这一「最左边」的权重,可能会从95%进一步逼近99%,甚至无限趋向100%,这也许就发生在下一个财年到来之前。
归根结底,和所有其他的"工程"一样,软件工程就是管理学,而提到管理学,就离不开人与人之间的沟通。有了AI以后,沟通反而成了最大的成本。这就是为什么打工人觉得自己的工作效率提高了,但是领导人并不觉得整个团队的效率或者投入产出比有什么变化,就在于此。
所以现在大家都在探索能不能将沟通也外包给AI,每个人都领养一个小龙虾,让小龙虾来代替人类进行沟通,这样沟通成本就能小很多。只能说,还是要用发展的眼光来看待问题,世间唯一不变的,就是没有什么东西是永恒不变的。软件工程也是同理,软件架构更是如此,理查兹的那本《软件架构》,里面出现最多的词汇就是"看情况"。还是要"实事求是"啊。