前言
在《荀子·劝学》中有句名言:“君子生非异也,善假于物也”。
在 LLM 普及之前,对于大部分开发者来说,开发工作的主要内容是通过各种评审会议反复确认需求、设计实现方案、查找文档、用搜索引擎寻求问题解答和通过调试去理解代码中的逻辑,因此真正用于编码的时间其实不会太多。
而对于高水平的开发者而言,在编码之前,大部分代码都已经在脑中构思好了。把代码敲出来,只不过是一种必须的负担而已。所以写代码从来都不是软件开发的核心部分。
但编码的确是一个琐碎且繁杂的活动,它甚至掩盖了我们工作中最有价值的部分:获取、学习并传递知识。
LLM 正在改变软件开发的面貌,它们可以自动化一些编码工作,但这并不意味着软件工程师的角色会消失。相反,它们将使软件工程师能够更多地专注于复杂问题的解决和创新,专注于更有价值的部分。也就是知识的获取与传递。这种改变将对个人和团队都带来深远的影响。
GitHub Copilot 就是目前会改变软件开发面貌最流行的工具之一,所以我们有必要清楚知道它是如何帮助我们自动化编码,让我们专注于更有价值的工作。网上大部分的教程都是 Visual Studio Code 来作为示例,然而作为后端开发,特别是 JAVA 开发者,使用 Jetbrains 的 IDE 居多。为了填补这方面的教程缺乏,本文会用 Intellij IDEA 来讲解如何使用 GitHub Copilot 。想要了解更多信息,可以查阅官方文档:GitHub Copilot 文档 - GitHub 文档。
安装 Github Copilot 插件
在 IDEA 中的 设置 - 插件 - Marketplace 中搜索 Github Copilot
,如下图所示的第一个插件,点击安装完后重启 IDEA 即可。
然后你就会看到有好几个 Github Copilot 的图标,如下图所示,左侧 1 个,右侧 2 个,右下方 1 个,一个插件的图标竟然有这么多,下面就来逐个介绍一下。
IDEA 主界面左侧有个问号的图标是欢迎弹窗,简单介绍一下它自身的一些能力,没啥用,可忽略。
IDEA 主界面右侧上方没有带消息框的图标是当需要它来实现代码时,它会在这里呈现多个版本的实现给你选择。
IDEA 主界面右侧带个消息框的图标是和 Github Copilot 对话的对话框,以后主要就是和它打交道,在这里可以进行对话,文章后面有更详细的使用介绍。
IDEA 主界面右下侧图标是 Github Copilot 众多功能的入口,包括:
- 登入和登出
- 查看官方文档和指引
- 快捷键设置
- 编辑设置(跳转到 IDEA 的 设置 - 语言和框架 - GitHub Copilot)
- 打开对话(和 IDEA 右侧带个消息框的图标功能一样)
在 IDEA 中的 设置 - 语言和框架 - GitHub Copilot 中还有一些设置:
- HTTP 代理
- 编辑器是否需要自动展示实现
- 对话的自然语言(比如你要用中文对话)
- 选择启用需要自动实现的编程语言
使用介绍
这个工具需要你先登录才能使用,但是只要按照步骤来进行就行,本文不再赘述。有免费版和收费版,免费版体验之后觉得对你收益很大,,付费支持一下也是可以的(作者用的是公司提供的企业版😁),具体差异可以查看官网:Github Copilot。
智能补全
当你写完一个注释按回车之后,或者写了一个方法签名后回车,甚至写完一个关键字之后,稍等一小会,会在光标后自动显示一段代码补全提示,颜色是灰色的。
当你觉得这段代码符合你的期望,按下 TAB 键,它就自动把代码给你真正地在编辑器里补全好了。
当然它提示的代码不一定是你所期望的,可以按 ESC 键取消这个代码补全提示。
只要你的注释或者方法名写的清晰和语义化,它就能理解你的所思所想,甚至有时候就感觉它还能预测你接下来的想法,当你还在懵逼的时候,它就给你提示下一步可能是啥了。
多种实现,任君选择
当然上面那种体感很好的自动填充的代码不一定是符合你期望的,这时候你可能还想知道其他的实现还有哪些,这时候在编辑器里,你把鼠标光标放置到你用自然语言描述需求所在的地方,比如一段注释,然后点击 IDEA 右侧这个 Github Copilot 的图标,再点击蓝色字 Refresh,稍等一会,它就会呈现好几个不同版本的实现给你选择。如果看到是你期望的代码,点击对应代码框的左上角的 Accept solution 按钮,它就会自动地在你光标后填充好你所选择的代码实现。
对话式交互
点击 IDEA 右侧带有消息框的 Github Copilot 图标,就会伸出 Github Copilot Chat 的对话框界面。在对话框里输入你的需求,按发送或者回车,也会给你回答代码实现,而且还有步骤提示。
如果它回答的代码实现是你所期望的,你把鼠标放在代码区域的右上角处,会显示两个按钮,一个是复制代码,另一个是直接将代码插入到编辑器的光标后。
现在还支持切换大模型,你可以测试哪个大模型更适合你的场景,现在大模型越卷越强,可能很快会支持更多更强的大模型。
上面的对话玩法是不是感觉和 ChatGPT 差不多,它的功能当然不止这么些,接下来要介绍的是斜线命令,本质上可以认为斜线命令是常用提示词(prompt)的快捷方式,比如 /explain
就相当于提示词你输入提示词(prompt)解释XXX
。斜线命令非常方便我们去执行一些常规操作,可以先输入 /help
回车看一下支持哪些斜线命令,目前版本支持这么些斜线命令:
/tests
- 生成单元测试
/simplify
- 简化代码
/fix
- 修复问题或者错误
/explain
- 解释代码如何运作
/doc
- 给当前选择的代码生成文档
/feedback
- 提供反馈的步骤
如果斜线命令之后不输入更多的提示词,默认情况下会针对你选中的代码、鼠标光标所在的位置、你在编辑器中当前打开的文件、当前的项目和你引用的文件,然后自动执行命令,这些斜线命令后面还可以继续输入更多的提示词按按照你所期望的方式来执行,比如输入 /tests 根据JUnit框架来生成
:
补充上下文,增强理解
默认情况下 Github Copilot Chat 回答前会先自动收集上下文,可以在它的回答中看到有这个步骤:
如果你没有额外指定引用文件的操作,Github Copilot Chat 会引用你编辑器中打开的文件或者智能地引用项目中的文件,你也可以在回答的最后看到引用了哪些文件:
如果你想它理解更到位,回答更准确,那你可以需要引用更多文件来补充上下文,其中一种方式是在对话框左下角的 +
号,添加你需要的引用的文件:
你也可以在指定的文件上右键单击来添加文件引用:
参与者(chat participant)
有少数的 @
开头的单词可以在对话中产生特定的作用,称之为参与者(participant)。
目前版本只有2个参与者:
@github
可以用来提及 GitHub 相关的功能或服务。具体作用包括:
- 提及 GitHub:在对话中使用
@github
可以明确表示你在讨论 GitHub 相关的内容或问题。- 请求帮助:可以用来请求与
GitHub
相关的帮助或支持,例如如何使用某个 GitHub 功能。- 通知:在团队协作中,使用
@github
可以通知团队成员关注某个 GitHub 相关的问题或讨论。
@project
可以用来提及当前项目的相关内容或功能。具体作用包括:
- 提及项目:在对话中使用
@project
可以明确表示你在讨论当前项目的内容或问题。- 请求帮助:可以用来请求与当前项目相关的帮助或支持,例如如何解决项目中的某个问题。
- 通知:在团队协作中,使用
@project
可以通知团队成员关注当前项目的某个问题或讨论。
需要注意的点
- 不要当成 ChatGPT 来用,不要问技术无关的问题,它大概率选择不回答你。
- 由于对话的历史也会用于上下文,所以如果不想之前的对话干扰到新的提问,可以删除之前的对话或者开启新对话窗口。
常用场景使用案例
编写测试
大部分软件开发者都知道单元测试的重要性,但是很多项目仍然缺乏体系的单元测试。原因基本可以归纳为这几点:
- 项目交付周期紧、资源不足
- 觉得测试不重要、更关注交付效率而非质量
- 代码结构复杂或者技术债务积累导致引入测试困难,难以实施
- 没有集成到CI/CD中,无法自动化测试,效率低下
GitHub Copilot 可帮助我们解决上面提及前3点原因,快速完成测试代码编写并提高工作效率。尽管 Copilot 在生成基本函数或方法的单元测试时表现良好,但复杂方案需要更详细的提示和策略。
测试先行
简单将 PRD 扔给 Copilot 来生成测试用例,往往不是很符合我们的期望,需要我们不断调整提示词来改进。因此测试驱动开发(Test Driven Development,TDD)的关键在于要将需求分解成足够小的任务,然后将这个任务转化为 Copilot 的提示词,提示词里要有测试策略和测试工序的描述,然后交给 Copilot 来处理,最后再将生成结果组合成测试代码,这样才能较快生成符合我们期望的测试代码。
下面我以《AI时代的软件工程》这个课程里第22课的例子来给大家呈现。
后补测试
有很多项目可能前期觉得测试不重要、或者单元测试需要持续维护,会增加负担,导致没有很积极的持续维护,或者压根就没写过单元测试。但是随着项目复杂化,技术债务不断积累,团队意识到需要引入单元测试来作为质量保证的重要手段。
在这种场景下 Copilot 可以基于已有的代码,自动生成测试用例,而且操作也非常的简单。
比如说对整个类生成单元测试,你只需要在编辑中打开一个类文件,然后在 Chat 中 输入:
/tests 编写涵盖多种场景(包括边缘事例、异常处理和数据验证)的多种测试方法,每个测试方法还需要有中文类注释
然后很快就能看到 Copilot 咔咔一顿测试代码的输出,如果你看到的代码还不能尽如人意,这时你需要调整你的提示词(Prompt),补充上下文,它就能越来越接近你期望的代码,当然你可能仍然需要自己手动调整一些代码。
上图中的示例,完整的输出如下,里面的一些变量值就需要你手动来改一下才能应用到项目中:
package com.xxx.cost.application.service;
import com.xxx.cost.application.bo.costing.CostingBO;
import com.xxx.cost.application.bo.costing.CostingDetailBO;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
class CostingQueryServiceTest {
@Mock
private CostingQueryService costingQueryService;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
@DisplayName("根据任务单号查询核价单 - 正常情况")
void queryByTaskCode_Normal() {
String taskCode = "TASK123";
CostingBO expectedCostingBO = new CostingBO();
when(costingQueryService.queryByTaskCode(taskCode)).thenReturn(expectedCostingBO);
assertNotNull(result);
assertEquals(expectedCostingBO, result);
}
@Test
@DisplayName("根据任务单号查询核价单 - 任务单号为空")
void queryByTaskCode_EmptyTaskCode() {
String taskCode = "";
when(costingQueryService.queryByTaskCode(taskCode)).thenReturn(null);
CostingBO result = costingQueryService.queryByTaskCode(taskCode);
assertNull(result);
}
@Test
@DisplayName("根据核价单号查询核价单详情 - 正常情况")
void queryDetailByCostingCode_Normal() {
String costingCode = "COST123";
CostingDetailBO expectedCostingDetailBO = new CostingDetailBO();
when(costingQueryService.queryDetailByCostingCode(costingCode)).thenReturn(expectedCostingDetailBO);
CostingDetailBO result = costingQueryService.queryDetailByCostingCode(costingCode);
assertNotNull(result);
assertEquals(expectedCostingDetailBO, result);
}
@Test
@DisplayName("根据核价单号查询核价单详情 - 核价单号为空")
void queryDetailByCostingCode_EmptyCostingCode() {
String costingCode = "";
when(costingQueryService.queryDetailByCostingCode(costingCode)).thenReturn(null);
CostingDetailBO result = costingQueryService.queryDetailByCostingCode(costingCode);
assertNull(result);
}
}
学习
当你在看你不擅长的语言的代码时,或者你对某个框架一知半解,这时你可以选中一段代码,鼠标右键单击弹出菜单,选择 Github Copilot - Explain This:
你也可以在 Github Copilot Chat 中输入 /explain
,和上面的效果一样,这样你相当于有个老师傅在手把手的教你走读代码,它还不会有脾气😏:
像上图所展示的,有时候回答是英文的,如果你想看中文的,那就在 Github Copilot Chat 中补充提示词,比如 /explain 使用中文
:
重构
在软件开发中,重构(Refactoring)是持续改进代码质量的关键实践,好处有:
- 提高可维护性,随着功能迭代,代码可能变得臃肿或耦合。重构通过简化逻辑、消除重复代码(如使用函数或设计模式)和模块化,使代码更易理解和修改。
- 降低技术债务,临时解决方案(如硬编码、快速修复)会积累“债务”。重构通过清理临时代码、修复设计缺陷(如违反 SOLID 原则),避免未来更高的修复成本。
- 适应需求变化,当需求扩展时,原始架构可能不再适用。重构可调整代码结构,例如将条件分支重构为多态(面向对象设计),将过程式代码重构为领域模型。
- 提升可测试性,高耦合代码难以测试。通过重构实现依赖注入(Dependency Injection)或分离关注点(Separation of Concerns),可以更轻松地添加单元测试。
- 优化性能与扩展性
- 改善团队协作,清晰的代码结构和命名规范减少沟通成本,加速新成员融入。
在重构现有代码之前,应确保了解其用途及其当前工作原理,可以用 Copilot 的 /explain
来帮助我们,前面已经讲解过,不再赘述。
了解完后,你既可以鼠标选中你需要重构一段代码,然后在 Chat 中输入 优化一下
,你可以在 Chat 中输入提示词(prompt)来指定需要优化的方法或函数。
Copilot 还可以做更多重构的事,你只要选中代码或者将鼠标放到方法/函数名称中,然后在 Chat 中输入提示词(prompt):
- 清理重复的代码:
将重复计算的过程移到一个方法内
。 - 拆分复杂的方法或者函数:
拆分到2个方法,一个用于清洗数据,另外一个用于打印
。 - 重写条件分支代码以提高可读性:
使用switch来重写
。 - 重新格式化代码:
闭包中使用箭头符号和重新命名可读性更好的参数名
。
迁移
有时候你可能需要换语言栈,比如要将某个项目从 PHP 语言转换成 JAVA 语言,Copilot 也能轻松帮我们实现。
但是你也要先充分了解原代码的用途和其工作原理,并且还要先生成单元测试,用于检查转换后代码执行的正确性,最后只需要打开你要转换的代码文件,或者你用鼠标选中代码,在 Chat 中输入提示词 转成JAVA来实现
,不用喝口水的时间就能完成代码生成,但是用单元测试来验证转换后代码的正确性。
快捷键
作用 | 操作系统 | 快捷键 |
---|---|---|
触发代码补全 | macOS | Option + \ |
Windows 或 Linux | Alt + \ | |
查看下一个建议 | macOS | Option + ] |
Windows 或 Linux | Alt + ] | |
查看上一个建议 | macOS | Option + [ |
Windows 或 Linux | Alt + [ | |
打开多个建议 | macOS | Command + Shift + A |
Windows 或 Linux | Ctrl + Enter | |
接受下一个字词 | macOS | Command + → |
Windows 或 Linux | Control + → | |
接受下一行 | macOS | Command + Control + → |
Windows 或 Linux | Control + Alt + → |
评论区