Vibe Coding实战:让AI成为你的Java结对编程搭档

引言

这段时间看到网上很多关于传统编程和氛围编程的争议,从笔者的角度出发,关掉ai基于原有模糊关键字的匹配的开发模式让开发者更有沉浸感,同时对于年轻的开发者来说更能够实际的去感知传统编程范式从而去提升自我编程的认知。由此基础之上采用vibe coding的方式解放自己的生产力,并基于自己更深层次的设计理念和ai对齐,从而保质保量的完成一次功能迭代。

当然,本文也并不是针对这个争议进行补充,而是针对vibe coding这一理念和技巧的落地方式以我个人即一个开发者的角度出发,展开深入的剖析,同时也会以一个传统开发者的角度介绍一些实践,希望对你有所帮助。

我是 SharkChili ,Java 开发者,Java Guide 开源项目维护者。欢迎关注我的公众号:写代码的SharkChili ,也欢迎您了解我的开源项目 mini-redis:github.com/shark-ctrl/...

为方便与读者交流,现已创建读者群。关注上方公众号获取我的联系方式,添加时备注加群即可加入。

详解vibe coding基本概念

vibe coding的基本概念

2025.2.3,前Open AI创始人、前特斯拉人工智能主管Andrej Karpathy在社交平台X上发布了一条看似平常却引发网友争议:

"There's a new kind of coding I call 'vibe coding', where you fully give in to the vibes, embrace exponentials, and forget that the code even exists. It's possible because the LLMs (e.g. Cursor Composer w Sonnet) are getting too good."

大意是说ai时代下,一种基于与ai交互模式的编程方式使得他有着更沉浸式的体验,这种编程方式与传统的编程方式完全不同,只是简单的说说、看看、运行即可完成一次完整的程序开发了。自此,vibe coding迅速的冲上了全球热搜。

vibe coding将编程模式由原有的写代码变为设计代码,这种方式带来的不仅是效率上的提升,更是创造力的提升,开发者可以专注于功能实现上,而非拘泥于编程语言的语法技巧和调试上的困扰。就像一位使用vibe coding的设计师所说:就像从手工绘图转向了使用PS,发现自己可以实现以前不敢想象的创意。

vibe coding使得开发者在同样的时间内可以做更多的事,从笔者的理解来看,这种编程的理念更像是一位资深的开发者指导一位有着良好基本的编程新手,即通过导师的设计让新人按照准确的设计理念用最准确的编码方式完成功能落地,并自主调试完成一次相对完美的功能开发。也就说通过vibe coding更专注于用户对于软件的认知而不是过分的去拘泥于代码实现和细节。

vibe coding的基本工作原理

归功于混合推理能力的出现,现代的模型已经能够在快速回答和深度思考两种模式中切换,这使得AI即能够快成简单的编码功能,还能够完成一次复杂的系统设计。甚至现在AI编程体已经能够理解复杂的业务逻辑并完成代码的调试,甚至也能够承载一部分架构的设计工作。这意味着vibe coding已经从实验性质工具变成开发者可以真正去依赖的生产力工具了。

整体来说,vibe coding下的编程方式也和原有的开发模式差不多,但在开发和调测阶段还是有一些区别,整体来说vibe coding分为如下几个阶段:

  1. 设计阶段:按照个人需求,明确规范自己的当前的任务和设计理解,以及需求边界和最终要求的效果,并拆解出细致的落地步骤,让ai能够更加准确的完成。
  2. 提示阶段:通过步骤1得到的想法,按照提示词规范,告诉AI本地落地功能的要求,这里笔者以一个开发者而非产品经理的角度给出一个示例:我是一名java开发,我现在要编写一个缓存工具记录活跃的用户信息,请你帮我用线程安全的Map写一个缓存工具,要求key为用户的id,value为User对象,同时保证并发读写安全。
  3. 生成阶段:由ai根据提示思考构建出相应的文件结构和代码,有时甚至会生成相应的测试用例
  4. 测试阶段:基于ai给定认为合格的代码进行单元调测,进行验收
  5. 优化阶段:步骤4可能会遇到或多或少的问题,此时我们就需要采用合适的提示词告知ai需要优化的地方,还是以笔者的并发缓存为例:这段代码锁的粒度太大,在并发度较高的场景下,程序吞吐量会下降,请你将关键字设计在map写操作的位置。

vibe coding与传统编程的区别

vibe coding和传统编程上的区别,笔者认为可以从以下几个角度考虑:

  1. 交互模式:传统编程模式更讲究的术,这种方式要求开发者完成对于需求的梳理,模型的构建,真正进行编码时,需要从应用层次完成抽象设计,然后在针对微观细节的内存、性能角度进行各种思考,同时在完成编码后还需要针对自己的成果建立测试用例完成功能验收和性能压测,而vibe coding讲究的更多是一种道,即设计之道,开发者可直接从用户角度出发,告知ai自己的需求,注意笔者强调的说法,是一种直接表达用户需要的需求,开发者可以化身为产品经理一样去思考问题,告知ai软件需要的功能、交互逻辑、体验细节上的说明,让ai直接生成对应的功能代码,然后在必要的场合,再以开发者的身份介入完成功能开发。
  2. 学习成本:传统编程模式需要开发者学习编程语言具备功能落地的能力,同时也要学习数据结构和算法独立完成功能上的优化,与此同时还需要对计算机网络、编译原理、数据库等几个重要的学科深入理解掌握,学习成本以年为单位,而vibe coding则不同,即使你是一个没有任何编程经验的开发小白,只要能够合理完成软件设计的任务拆解并严格按照提示词规范和ai时刻保持沟通上的对齐,也能够完成功能的开发。
  3. 认知负荷:传统编程模式要求开发者针对每个变量、函数、代码结构都了然于心,相对于vibe coding前者需要更强大的专注力和脑力,必须让自己保持在一个心流的模式下完成一个完整的功能开发。而vibe coding则不一样,开发者只需准确的提出自己的想法,让ai完成功能落地,在此期间还可以随心所欲的增加各种创意,极大了解放人知的生产力。
  4. 社会发展:从长远来看,vibe coding模式和之前几次人类的革命差不多,本质上都是传统维度解放和新模式的演进,有学者说法到了2030年,将会有80%的开发者将设计ai的协作,而vibe coding正式这种模式的直接形式。

但这并不意味着vibe coding会取代传统编程,两者的关系应该是相辅相成的:

  1. vibe coding在现阶段更多是一种实用、够用主义,即功能更多时是保证能够符合用户的需求即可
  2. 传统编程在开发者这里,因为人的因素,编码阶段会考虑代码的优雅、性能的压榨、架构的通用性,更多是一种术的维度上的人知

也就是说明对于一些相对基础的原型开发、固定模式功能开发工作可以交由ai实现,而对于涉及架构设计、高性能运算、安全一些领域,AI更多是一种辅助,即在传统编程开发者的人知维度下辅助生成相对最优解,然后采用传统编程或者ai完成试验。

vibe coding基本提示词的核心理论

想法到方案落地的标准流程

vibe coding有许多和ai交互的地方,这其中有很多不错的理念,对于开发者亦或者产品经理来说都是很实用的,笔者也将其抽取为几个重要的观点分享:

第一个重要的发现问题,对于面向ai编程的vibe coding,这都是非常重要的,即无论是产品经理还是开发者都需要抓住真正的问题本质,才能够更好的和ai沟通并解决问题。举个例子,产品经理想利用vibe coding模式生成一款好用的笔记,如果只是告知ai:我想要一款好用的软件。那么得到的结果肯定是和预期有所区别的,因为我们并没有明确告知ai自己清晰的需求。

如果这位产品经理能够清晰的明确自己的需求,例如:一款能够快速完成知识点笔记整理和快速回顾完成知识点复盘,即vibe coding要求用户必须明确核心问题,才能更进一步完成迭代。

面向开发向也是同理,针对开发功能数据查询效率低下,如果你仅仅是给定代码后告知帮我提升这段代码的读取性能,那么得到的结果可能也是不符合预期,如果你明确知晓这个功能点的场景是读多写少,且是高并发的热点数据查询,那么开发者就应该抓住问题的本质,告知ai:这段代码是高并发读多写少的数据查询,请采用写时复制的思想帮我写一款并发安全的缓存工具:

vibe-coding.drawio

第二个理念的具像化的场景化构思,有了上述的基础之后,用户需要告知ai是什么人在什么情况下要用到这个功能,最终能够达成什么效果,通过这个一个具像化的场景化构思,才能够让ai准确的理解需求,并未后续的提示词提供良好的上下文。

例如:

  1. 35岁的中年营销经理小王,在每周一的例会前,需要用到这个工具快速生成上周的数据分析报告,让他能够专注于策略的讨论而不是数据归纳整理......
  2. 面向于全国用户的气象管理平台,每天12点都会有数以万计的查询调用本日天气情况,此时的qps差不多是10w,同时平台接口也会在前一天的11点,写入次日达气象信息,请你基于该场景和我给定的数据表ddl生成一个符合当前性能要求的保存接口和查询接口。

第三个理念则是精炼,采用MVP即最小可行性产品策略,抽检出问题的核心从而保证最快速的完成目的,这也就是vibe coding新手和高手之间的差异,善用ai的人,总是能够抓住问题的本质,去着手解决关键的功能点,大部分开发者初次接触ai都巴不得一次性完成所有的功能,这使得他的提示词的质量非常差,ai无法非常准确的把控需求亦或者在需求沟通、迭代、落地都非常的漫长。同样的这个道理也适用于传统开发者,对于某个功能点,也一定拆解出来并抓住最重要的点进行解决,然后再进行其他相对不重要的点的迭代。

最后一个理念就是实现了,对于与传统编程不同的是,vibe coding是支持边学边做的模式,即用户可以不懂某个技术的实现,善用沟通能力,掌握合适的迭代和优化技巧,通过多轮对话的方式让ai不断改进优化得到我们需要的产品。

vibe coding时的核心设计原则

这一点不仅仅是面向于vibe coding,对于我们的日常工作也是很受用的,即MVP原则:先求有,再求好。

很多初学者都会陷入完美主义的问题,即对于需要的功能总是巴不得一次性将其所有功能完成,这种方式不仅低效而且风险还高,低效是因为花费大量的时间沟通没有完成核心功能且耗费大量心里用在一些非必要的功能点上。风险高也是因为前者的心力上的耗费,可能在同等的工时内,开发者不能有效正确的完成功能验收。

所以正确的做法,是基于vibe coding快速做出一个能够符合要求的功能版本,然后推向市场收集反馈,再根据效果进入下一步的决策进行改进,这种方案不仅效率最高,而且能够保证产品永远按照正确的方向推进。

沿用一个成功的产品经理的经验,他现在所做的产品都是先花费两天时间做出MVP,然后完成功能迭代后直接推向市场接受考验并获得正确的的反馈并进行数据分析,然后再花费一段时间进行整理得出优化策略,得到下一个版本的功能改进策略。通过这种有效的循环迭代,保证产品的持续演进。

这也是就MVP原则的核心:

  1. 实用:先真正的去解决问题,在不是炫耀技术
  2. 简单:结果深思熟虑后的精炼而非功能的简陋,让vibe coding能够在最短的时间最大化的发挥
  3. 优雅:通过持续迭代和vibe coding的交互模式,我们不仅可以得到正确的功能,还能够更多时间专注于视觉上的优化、整体感官的和谐与统一

与AI沟通的通用模板

需求说明

上述更多是一些宏观的理念,而下来笔者将基于这些理念构建出一套通用且普适的vibe coding技巧,通过这套技巧我们最终将会得到一个优秀支持高并发缓存运算的缓存工具。

这里笔者先简单的介绍一下这个缓存工具的需求,该缓存工具主要是缓存某个社区网站的活跃用户信息,这个网站面向群体是程序员,因为网站要针对一些活跃的用户信息指标统计分析,所以我们希望构建一款缓存工具将活跃的用户信息加载到缓存中,便于后续更好的汇聚统计。

而对应用户实体类如下:

arduino 复制代码
@Data
public class User {
    // 用户ID号
    private long id;
    
    // 用户姓名
    private String name;
    
    // 用户出生日期
    private DateTime birthday;
   
    // 用户主编程语言
    private int mainLanguage;
}

笔者希望这款缓存工具可以基于我们外部传入的用户id、出生日期、性别的整数标识、以及编程语言标识生成用户的年龄、性别字符串、主编程语言字符串这个直观的实体信息,对应的实体定义如下:

arduino 复制代码
@Data
public class HotUser {
    // 用户ID号
    private long id;
    
    // 用户姓名
    private String name;
    
    // 用户年龄
    private int age;
    
    // 用户性别
    private String sex;
    
    // 用户主编程语言
    private String mainLanguage;
}

完成转换之后,再将热点用户实体信息写入到缓存中:

vibe-coding-2.drawio

学会具体化描述

提示词的第一个技巧就是避免过多的修饰,尽可能去具体描述你的需求,因为具体化描述会带来如下的收益:

  1. 累积细节:每个具体的描述都会引导AI往正确的方向推进,如果你说要一个好看的网页,ai就很很容易泛化这个概念,取而代之的,如果你说我一个蓝白色调的网页,ai就会朝着正确的目标去构建html,简而言之就是AI比人类需要更详细的说明书。
  2. 保证用户体验的连贯性:通过具体化描述让AI更加明确你的需求,从而能够设计出更加统一,逻辑一致的功能,确保需求和产出的一致性以及沟通的连贯性。
  3. 辅助需求的梳理:这点就像费曼学习法一样,主动的去完整的输出自己的想要描述的东西,通常会发现原本模糊的概念变得更加清晰,甚至会意识到之前遗漏的一些细节。
  4. 减少修改次数:准确的需求描述减少非必要的沟通次数,避免耗费过多的心力,按照一位资深的vibe coding创业者的分享的技巧,在实用AI初期,他总是会草草花费5分钟去描述需求,然后耗费一天的时间进行反复修改,而他现在总是会花费半小时去准备产品的描述,然后再和AI进行沟通,只需两个小时就可以得到一个满意的模型。

对此,笔者也给出一套比较万能的提示词技巧,即背景信息+具体任务+输出要求+约束条件,下面笔者还是以一个缓存工具为例介绍一下如何基于这套模板构建出合适的的提示词:

  1. 背景信息:这是告诉ai要站在什么角度理解和处理需求,例如:我是一个从事多年java开发的软件工程师。这就是让ai明确知晓当前的问题要从一个java程序员的角度来思考,而非那些非计算机专业的产品经理或者普通用户角度。
  2. 具体任务:这点描述的目的是告诉ai当前我们希望ai完成什么任务,也就是整个指令的核心,例如:我现在希望你基于我给的实体类编写相应的缓存工具,要求.......,即通过具体化的描述让AI尽可能的一次性知晓本次需求。
  3. 输出要求:即更进步一步针对需求的特性或者说功能以外的技术说明,还是以缓存工具为例:我要求这个缓存工具要保证线程安全,锁的粒度要尽可能小,尽可能的让并发线程减少阻塞的时间
  4. 约束条件:这点就是要告诉AI不能做什么,要在什么限定条件下编程,例如:这个类不需要考虑通用性,只需要基于我给你的实体类信息生成相应的缓存工具,同时考虑到jdk8之后的锁升级机制和代码的简洁性我希望你实用监视锁而非java内置的lock接口衍生的锁。

所以综合以下,笔者本次的提示词为:

markdown 复制代码
背景信息:我是一个5年开发经验的java开发工程师,最近要针对一个记录程序员信息的实体类编写一个缓存工具,这个实体类信息为:
......

具体任务:我希望这个缓存工具能够对外提供一个方法,要求如下:
0. 专门创建一个静态工具类作为缓存工具,这个缓存工具内部聚合一个map作为实际缓存信息的数据结构,其中key为user类的id,value为HotUser
1. 生成一个将信息写入缓存的方法,基于我给你的用户实体类User将其转为HotUser,要求id和姓名不变、基于传入的日期生成用户的年龄并写入到age字段中,传入的整型mainLanguage对应0是golang,对应1是java
2. 基于上述步骤将转换后得到的HotUser写入到缓存中
3. 生成一个读缓存的方法,要求基于我们给定的用户id,返回指定HotUser的信息

输出要求:我要求编写缓存工具,代码要清晰简洁易懂,同时必要的部分附上注释说明,方便我后续查阅和迭代
约束条件:这个缓存工具是允许多线程并发写入的,所以我希望你考虑到多线程并发安全问题,考虑到jdk8后的synchronized的锁升级机制,我希望你用这个关键字而非lock接口的衍生类

到此为止,我们已基于背景信息、目标任务、输出要求、约束条件这个框架编写出相对完整的提示词,下一步我们将基于几个技巧再次尝试将提示词精确化。

如何指令精确化

指令精确化是对于提示词技巧优化的关键步骤,整体来说它要求用户学会:

  1. 场景具像化:很多人在描述需求的时候,总是会实用抽象的形容词去描绘,例如:好看的界面、高性能的缓存工具,以笔者上述提示词为例,在具体任务的描述时对应的场景可以再次具像一点,即:我希望你编写一个基于给定用户信息完成并发线程安全的读写缓存工具
  2. 对标参考法:这一点是针对很难描述需求效果时的一个技巧,此时我们就可以提示AI一个参考对象,例如笔者当前项目工程中ComputeCache实现思路就很不错,我们就可以提示ai参考这个工具类
  3. 限制条件明确化:这一点上文已经提及,如果没有相对明确的说明,ai可能会开发出偏离目标需求的功能代码段,所以对应的限制条件一定要明确
  4. 分步骤描述:一次灌过多需求会造成ai混乱,所以我们要学会拆解任务,以笔者本次编写的缓存工具为例,笔者的提示词更多是要求ai开发出线程安全的缓存工具,后续的一些健壮性判断、性能优化都会在后续迭代中完成,这也是严格遵循了之前提及的理念:东西先出来,再不断迭代去优化
  5. 迭代反馈:很少人能够一次性给出完美的提示词,所以要学会多轮沟通和有效反馈让ai在每次对话后都有所推进

于是我们最终的提示词如下,此时就可以将其喂给AI了:

markdown 复制代码
背景信息:我是一个5年开发经验的java开发工程师,最近要针对一个记录程序员信息的实体类User编写一个并发安全的缓存工具,这个实体类信息查看com.sharkchili.vibecoding.User

具体任务:我希望这个缓存工具能够对外提供两个方法分别是提供用户信息写入和读取,要求如下:
0. 专门创建一个静态工具类作为缓存工具,这个缓存工具内部聚合一个map作为实际缓存信息的数据结构,其中key为user类的id,value为HotUser,对应HotUser代码位于com.sharkchili.vibecoding.HotUser
1. 生成一个将信息写入缓存的方法,基于我给你的用户实体类User将其转为HotUser,要求id和姓名不变、基于传入的日期生成用户的年龄并写入到age字段中,传入的整型mainLanguage对应0是golang,对应1是java
2. 基于上述步骤将转换后得到的HotUser写入到缓存中
3. 生成一个读缓存的方法,要求基于我们给定的用户id,返回指定HotUser的信息

整体的并发安全编写思路可参考我写的com.sharkchili.juc.cache.ComputeCache

输出要求:我要求编写缓存工具,代码要清晰简洁易懂,同时必要的部分附上注释说明,方便我后续查阅和迭代
约束条件:这个缓存工具是允许多线程并发写入的,所以我希望你考虑到多线程并发安全问题,考虑到jdk8后的synchronized的锁升级机制,我希望你用这个关键字而非lock接口的衍生类

于是我们就得到了这样一段代码,可以看到错误还是蛮多的,所以我们就需要引出下一个话题------持续迭代的艺术

java 复制代码
/**
 * 用户信息缓存工具类
 * 提供用户信息的并发安全读写操作
 */
public class UserCache {

    /**
     * 实际存储用户信息的缓存容器
     * key: 用户ID
     * value: 热点用户信息
     */
    private static final Map<Long, HotUser> cache = new ConcurrentHashMap<>();

    /**
     * 将用户信息写入缓存
     * 
     * @param user 用户信息对象
     * @param currentDate 当前日期,用于计算年龄
     */
    public static synchronized void put(User user, Date currentDate) {
        // 将User对象转换为HotUser对象
        HotUser hotUser = new HotUser();
        hotUser.setId(user.getId());
        hotUser.setName(user.getName());
        
        // 根据出生日期计算年龄
        Date birthDate = user.getBirthday();
        long betweenYear = DateUtil.betweenYear(birthDate, currentDate, false);
        hotUser.setAge((int) betweenYear);
        
        // 设置性别(整型转字符串)
        hotUser.setSex(String.valueOf(user.getSex()));
        
        // 设置主编程语言(0表示golang,1表示java)
        int mainLanguage = user.getMainLanguage();
        if (mainLanguage == 0) {
            hotUser.setMainLanguage("golang");
        } else if (mainLanguage == 1) {
            hotUser.setMainLanguage("java");
        }
        
        // 将转换后的HotUser对象存入缓存
        cache.put(user.getId(), hotUser);
    }

    /**
     * 根据用户ID从缓存中读取用户信息
     * 
     * @param id 用户ID
     * @return 对应的热点用户信息,如果不存在则返回null
     */
    public static HotUser get(Long id) {
        return cache.get(id);
    }
}

学会持续优化

如何正确的指出不完美的地方

迭代优化的核心是学会精确的表达不满意,即要求用户学会:

  1. 不要提出过于模糊的要求,例如:你这个代码写的bug太多了,再改改
  2. 不要一次性灌入太多需求,让ai无所适从,最终迭代出与预期相反的效果

持续迭代必须要求我们学会:

  1. 和AI建立对话历史:让工作持续推进,而非每次从0开始
  2. 一次只改一个问题:虽然我们会同时发现很多的问题,但我还是要学会一次只修改一个问题,通过分步解决,确保清晰的看到每次改动的效果明确ai改动是否正确,从而决定是推进迭代还是回滚
  3. 肯定正确的部分,明确指出需要改进的地方:这样的反馈可以让ai精确的理解需求,保证不破坏已有的正确的代码情况下,针对不满意的地方进行进一步修改

以上述缓存工具为例,笔者在提出需求的时候忘记告知ai对应的性别转换规则,导致它进行转换时是直接将数字专为性别字符串,所以笔者迭代的提示词为:

makefile 复制代码
整体逻辑是符合我的预期,但是设置性别(整型转字符串)这一步我没有交代清楚,我传入的性别整数的含义为:
0:代表性别女
1:代表性别难
所以请你保持当前代码逻辑结构不变,将性别转换这块逻辑改一下,当你看到sex对应整数0,对应的字符串就是女,反之1就是男

可以看到,对应diff比对时,ai准确在保持当前逻辑结构不变的情况下,将性别转换问题修复:

image-20250810115831626

为了让读者有更直观的体验,笔者这里再给出一个问题的修复,从代码上来看,其入参的都currentDate是非必要的,因为计算年龄这个事情本身就是方法的职责,调用者更应该关注的是缓存读写的结果,所以我们应要求ai在方法内自生成currentDate而非要求用户主动传值暴露方法的细节:

arduino 复制代码
逻辑调整的可以,当前逻辑正确清晰,请保持不变,我要求put传参的currentDate在方法内生成而非调用者传入,避免暴露过多细节,同时这个currentDate的值生成的也是执行时刻的值

从diff上可以看到,准确的提示词,确实给到了

image-20250810154159520

直接复制错误比描述信息更加有效

完成第一个版本的缓存编写之后,我们或多或少会自己进行调试和功能验收,这时候难免会有一些报错,这时候就需要学会告知ai错误,让ai清晰明白当前的错误是什么,这其中就有一个黄金法则:

错误本省就是最准确的问题描述

举个例子,例如我们在调用上述方法时传入一个空的user,导致后续转换逻辑一场,我们要告知ai的不是:这个代码运行会空指针,请帮我修复,取而代之的是:

  1. 如何复现问题
  2. 问题的原因,即错误的栈帧
  3. 要求ai修复问题,并明确告知原因以判断ai的决策是否准确

对应提示词如下:

php 复制代码
基于给定的UserCache我尝试通过UserCache.put(null);这种方式发起调用,结果栈帧中抛出如下错误:

Exception in thread "main" java.lang.NullPointerException
 at com.sharkchili.vibecoding.UserCache.put(UserCache.java:34)
 at com.sharkchili.vibecoding.UserCache.main(UserCache.java:75)
 
请给我详细的说明原因,并尝试修复这个问题 

于是,ai就给了我如下一段的解释:

根据您提供的错误信息和代码分析,出现 NullPointerException 的原因是您在调用 UserCache.put(null) 时传入了一个 null 值,而在 put 方法内部直接使用了这个 null 值而没有进行空值检查。 具体来说,错误发生在 UserCache.java 的第34行,即 hotUser.setId(user.getId()); 这一行代码。当 user 参数为 null 时,调用 user.getId() 就会抛出 NullPointerException。

对应的也在user入参这块做了健壮性的判断:

image-20250810154219248

渐进式迭代而非一蹴而就

基于上述基础,对应缓存工具已经是具备能用的水平,此时我们就需要考虑好用,也就是优化工作这一理念也是笔者vibe coding一直坚持的,即优先保证功能实现,保证不破坏原有好东西的情况下,明确指出优化的点。同时也要严格遵循小跑原则,也就是一次尽可能改动一到两个问题,保证问题能够验证的同时,保证设计者能够明确跟进问题进行进一步的决策。

整体来说提示词的标准大体就是上述的步骤,笔者这里就不多做演示,这里就讲讲笔者后续几次和ai沟通的迭代步骤,在上述的对话下,这个缓存工具,已经具备的非常初步的可用性,后续就是一些见仁见智的优化步骤,按照笔者方式即从一个开发者角度的优化步骤为:

  1. 封装user转换逻辑,为锁粒度缩小做好铺垫:
arduino 复制代码
当前这段逻辑已经完整可用,将put方法内部将user转换为hotuser的逻辑封装成一个方法,保证代码的简洁,为后续性能优化做铺垫。
  1. 引入futureTask配合ConcurrentHashMap原子putIfAbsent避免运算阻塞其他线程,提高性能同时,保证线程安全
arduino 复制代码
当前代码基本可以保证可用,但是synchronized关键字锁的粒度太大了,在高并发的表现下性能上不去,我希望你帮我优化一下,这里我给出的思路是:
0. 缓存的map保持key为String的用户id,但是value是FutureTask的HotUser泛型对象,后续该缓存记录的都是当前需要缓存用户的FutureTask对象
1. 将convertToHotUser方法封装为FutureTask对象返回
2. 尝试通过ConcurrentHashMap的putIfAbsent方法,以user的id作为key,convertToHotUser返回值作为value尝试进行存操作,如果返回null则说明这个hotuser的任务是第一次添加,则调用FutureTask的run方法将该任务启动
3. get方法逻辑也调整一下,直接从缓存中拿到FutureTask并调用get方法返回出去
  1. HotUser所有字段该用final修饰,保证安全发布不被外部调用者修改:
sql 复制代码
上述操作保证缓存并发的性能表现,基本可用,请保持不变,最后我想让你做的是,将hotuser对象所有字段采用final进行修饰,让其具备不可变形,确保外部在调用get获取到对象时无法修改缓存内部的user对象

最终HotUser被修改为具备安全发布的实体类型:

arduino 复制代码
@Getter
public class HotUser {
    // 用户ID号
    private final long id;
    
    // 用户姓名
    private final String name;
    
    // 用户年龄
    private final int age;
    
    // 用户性别
    private final String sex;
    
    // 用户主编程语言
    private final String mainLanguage;
    
    public HotUser(long id, String name, int age, String sex, String mainLanguage) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.mainLanguage = mainLanguage;
    }

    
}

而对应的缓存,也注入的笔者个人的设计理念,缓存的可用性和性能都得到了保证:

ini 复制代码
/**
 * 用户信息缓存工具类
 * 提供用户信息的并发安全读写操作
 */
public class UserCache {

    /**
     * 实际存储用户信息的缓存容器
     * key: 用户ID
     * value: 用户信息的FutureTask包装对象
     */
    private static final Map<Long, FutureTask<HotUser>> cache = new ConcurrentHashMap<>();

    /**
     * 将用户信息写入缓存
     *
     * @param user 用户信息对象
     */
    public static void put(User user) {
        // 检查参数是否为空
        if (user == null) {
            throw new IllegalArgumentException("用户信息不能为空");
        }

        // 创建FutureTask包装用户转换任务
        FutureTask<HotUser> newUserTask = new FutureTask<>(() -> convertToHotUser(user));

        // 使用putIfAbsent原子操作尝试添加任务
        FutureTask<HotUser> existingUserTask = cache.putIfAbsent(user.getId(), newUserTask);

        // 如果返回null说明是第一次添加该用户任务
        if (existingUserTask == null) {
            // 启动任务
            newUserTask.run();
        }
    }

    /**
     * 将User对象转换为HotUser对象
     *
     * @param user 用户信息对象
     * @return 转换后的HotUser对象
     */
    private static HotUser convertToHotUser(User user) {
        // 根据出生日期和当前日期计算年龄
        Date birthDate = user.getBirthday();
        Date currentDate = new Date(); // 在方法内生成当前日期
        long betweenYear = DateUtil.betweenYear(birthDate, currentDate, false);
        int age = (int) betweenYear;

        // 设置性别(0表示女,1表示男)
        String sex;
        int userSex = user.getSex();
        if (userSex == 0) {
            sex = "女";
        } else if (userSex == 1) {
            sex = "男";
        } else {
            sex = "未知";
        }

        // 设置主编程语言(0表示golang,1表示java)
        String mainLanguage;
        int userMainLanguage = user.getMainLanguage();
        if (userMainLanguage == 0) {
            mainLanguage = "golang";
        } else if (userMainLanguage == 1) {
            mainLanguage = "java";
        } else {
            mainLanguage = "未知";
        }

        // 使用构造函数创建不可变的HotUser对象
        return new HotUser(user.getId(), user.getName(), age, sex, mainLanguage);
    }

    /**
     * 根据用户ID从缓存中读取用户信息
     *
     * @param id 用户ID
     * @return 对应的热点用户信息,如果不存在则返回null
     */
    public static HotUser get(Long id) throws Exception {
        FutureTask<HotUser> userTask = cache.get(id);
        if (userTask != null) {
            return userTask.get();
        }
        return null;
    }

}

小结

笔者简单的花了一个周末尝试注入自己的理念完成了一次vibe coding,总的来说,它并没有网上说的那么神奇,从个人使用角度来看,vibe coding更多是处于一个实用、可用的质量,要想编写出高质量的代码,传统编程开发者的认知还是很重要的,要知道ai只是一个优秀的协作者而非一个能够独立思考并替代你编程工作的设计者,所以即使AI在飞速的发展,我们也需要学会跟随时代的推进,提高自己的认知,找到一种能够把控软件设计的核心思想和认知。

针对vibe coding,笔者想说的是,在努力提升自我认知的基础上,要做到以下几点:

  1. 学会梳理需求和拆解问题,得到一个清晰具体的要求
  2. 基于提示词模板和ai进行沟通
  3. 避免完美不足,学会逐步迭代
  4. 学会报错提问的技巧

我是 SharkChili ,Java 开发者,Java Guide 开源项目维护者。欢迎关注我的公众号:写代码的SharkChili ,也欢迎您了解我的开源项目 mini-redis:github.com/shark-ctrl/...

为方便与读者交流,现已创建读者群。关注上方公众号获取我的联系方式,添加时备注加群即可加入。

参考

《人人皆可vibe 编程 玩转氛围编程》

本文使用 markdown.com.cn 排版

相关推荐
天天摸鱼的java工程师5 分钟前
JDK 25 到底更新了什么?这篇全景式解读带你全面掌握
java·后端
非鱼feiyu10 分钟前
自关联数据表查询优化实践:以 Django + 递归 CTE 构建树结构为例
数据库·后端·django
零日失眠者16 分钟前
这5个Python库一旦掌握就离不开
后端·python
幌才_loong20 分钟前
.NET8 × Redis 实战宝典:从配置到落地,搞定高并发缓存就这篇!
后端·.net
用户83562907805123 分钟前
如何使用 Python 从 Word 文档中批量提取表格数据
后端·python
l***370938 分钟前
spring 跨域CORS Filter
java·后端·spring
aiopencode1 小时前
APP 公钥与 MD5 信息在工程中的价值 一次签名排查过程带来的经验总结
后端
ServBay1 小时前
Django 6.0 发布,新增原生任务队列与 CSP 支持
后端·python·django
用户2190326527352 小时前
Spring Boot 4.0 整合 RabbitMQ 注解方式使用指南
后端
PPPPickup2 小时前
easychat---创建,获取,获取详细,退群,解散,添加与移除群组
java·开发语言·后端·maven