资讯动态

MVP矩阵+Cola架构:AI编程代码不再越写越乱

发布时间:2026/8/27 21:13:59 来源:尧图企业网站定制
1. 背景与核心概念AI编程为什么“越写越乱”先聊一个很常见的场景拿到 Claude Code、Cursor 这类 AI 编程工具后很多人第一句话就是“帮我写一个订单系统”“帮我写一个用户管理模块”然后直接把需求糊给 AI。结果是什么AI 确实生成了几百行代码结构看着像模像样但一跑就报错或者压根不是你想要的逻辑。我也经历过这个阶段。Claude Code 的能力确实强但它的强是“在明确约束下快速产出高质量代码”而不是“帮你想清楚需求”。如果需求本身是模糊的、边界的残缺的AI 生成的代码就很容易出现结构混乱、逻辑前后矛盾、反复重构浪费 token 的情况。本篇文章想解决的核心问题就是在用 Claude Code 这类 AI 编程工具之前我们应该先做什么答案不是写更多的提示词而是先把需求拆成一个可运行的最小产品也就是 MVP再用合适的架构去约束代码结构。这里我会引入一个很实用的方法框架——MVP 矩阵MVP Matrix以及一套用于落地代码结构的应用架构思想——Cola。你可能听说过 Cola它是阿里开源的一套应用架构核心思想是分层清晰、业务与基础设施解耦。我在 AI 编程实战中越来越多地使用 Cola 的思路不是为了追框架时髦而是为了让 AI 生成的代码有“骨架”可依。AI 写代码如果没有骨架每个文件都在自由发挥一旦有了分层约束AI 生成代码的质量会稳定很多。这篇文章主要面向两类读者已经在用 Claude Code、Cursor 等 AI 编程工具但发现代码越改越乱的开发者。想尝试 AI 编程但不知道如何描述需求、如何组织代码结构的新手。读完本文你将掌握三件事第一用 MVP 矩阵把需求拆成最小可交付范围第二用 Cola 的分层思路约束 AI 生成的代码结构第三用 Claude Code 完成一次完整的开发闭环从描述需求到代码落地。2. 环境准备与版本说明在真正开始之前先来看一下本文使用的开发环境。需要说明的是AI 编程工具链迭代非常快。Claude Code 的安装方式、命令参数、模型名称都会不断变化所以本文不会写死某个具体版本号而是给出通用安装和验证思路。如果你的运行环境与本文不一致优先以官方文档为准。2.1 开发环境清单以下是我在本文示例中使用的环境操作系统macOS / LinuxWindows 可使用 WSL 或 PowerShell命令基本一致Node.js18 及以上版本建议 LTSnpm随 Node.js 安装AI 编程工具Claude Code当前建议安装最新稳定版模型以 Claude 系列模型为例具体模型名称以你账号下实际可用的模型为准项目类型一个最小可运行的 Java 或 TypeScript 项目本文以后端接口为例2.2 安装 Claude CodeClaude Code 是 Anthropic 官方推出的命令行 AI 编程工具可以在终端里直接与代码仓库交互。安装方式比较简单前提是你有对应的账号或组织访问权限。npm install -g anthropic-ai/claude-code安装完成后可以先查看版本确认安装成功。claude --version登录时在终端直接执行claude首次运行会引导你完成登录。如果遇到your organization has disabled claude subscription access for claude code这类提示说明当前组织账号没有开通 Claude Code 权限需要联系管理员开通或者换用个人订阅账号。需要注意的是Claude Code 的可用性受地区和服务政策影响。如果执行时提示claude code might not be available in your country说明当前网络或账号区域不受支持这不是代码问题不要浪费时间排查。除了 Claude CodeCursor 也是当前比较主流的 AI 编程工具。两者的关系简单来说Claude Code 是命令行工具适合与 Git 仓库和脚本流程深度集成Cursor 是编辑器适合在 IDE 内完成 AI 辅助编码。本文以 Claude Code 为主因为它更接近“开发流程自动化”的定位。2.3 验证 AI 编程工具的上下文能力在开始正式开发前我强烈建议先做一个“最小验证”让 Claude Code 读取当前项目结构并描述目录内容。这一步可以确认工具能否正常工作。在项目根目录执行claude 请列出当前项目目录的结构并说明每个目录的用途如果 Claude Code 能正确输出目录结构说明工具链基本就绪。接下来我们进入本文的核心内容如何在写代码之前先用 MVP 矩阵想清楚需求。3. 核心概念MVP 矩阵与 Cola 架构这一节是全文的理论基础但我会尽量用大白话讲清楚不堆术语。3.1 什么是 MVP为什么 AI 编程要先做 MVPMVP 全称是 Minimum Viable Product最小可行产品。这个概念在创业领域很常见意思是用最低成本做出一个能验证核心想法的产品版本不追求功能齐全只追求“跑得通、有人用、能验证”。在 AI 编程里MVP 同样重要而且我认为它的优先级比提示词技巧更高。原因很简单AI 生成的代码是基于你的输入推测出来的。如果你的输入包含 10 个功能点AI 会努力把这 10 个功能点全部实现。但问题是这 10 个功能点本身可能互相冲突优先级不明边界不清。AI 一旦陷入这种复杂需求里最容易出现两种结果一是生成大量无用代码二是关键逻辑被淹没在次要功能里。如果先把需求收敛成一个 MVP只保留核心链路AI 的产出质量会明显提高。举个例子。你让 Claude Code“写一个用户系统”。这个需求的边界很模糊AI 可能会生成注册、登录、找回密码、个人中心、头像上传、权限管理……一大堆代码。但如果你把需求改成“写一个支持邮箱密码登录、登录后返回用户 ID 和用户名的用户系统”AI 的生成范围就清晰多了代码质量也会高很多。3.2 MVP 矩阵四个空间拆解需求这里就要引出 MVP 矩阵了。简单来说MVP 矩阵是一个把需求拆成四个空间的分析框架分别回答四个问题业务空间Business Space核心业务目标是什么要解决谁的什么问题方案空间Solution Space为了实现业务目标产品需要具备哪些能力系统空间System Space这些能力由哪些系统/模块承接系统之间的交互关系是什么工程空间Engineering Space在代码层面模块如何组织数据结构如何设计接口如何定义我再尽量用一套更具体的表述来帮你理解。你可以把 MVP 矩阵想成“从需求到代码的四个翻译层”空间核心问题输出产物对应开发阶段业务空间要解决什么问题用户故事、业务目标需求分析方案空间产品应该做什么功能清单、页面/接口列表产品设计系统空间系统如何组织模块划分、系统交互、API 设计架构设计工程空间代码如何落地代码结构、类设计、数据表编码实现在 AI 编程的语境下这四个空间的顺序非常重要必须从上到下依次完成不能跳级。大多数人的习惯是直接从工程空间开始让 AI 写代码。但这样做等于跳过了前三层AI 没有业务上下文没有方案约束没有系统边界写出来的代码只能靠“猜”。这也是很多人觉得 AI 写代码“不靠谱”的根本原因。3.3 什么是 Cola 架构它和 AI 编程有什么关系Cola 是 Clean Object Layered Architecture 的缩写中文可以理解为“整洁对象分层架构”。它由阿里技术团队开源核心理念是把应用代码按照职责分成清晰的层次让业务逻辑不依赖具体框架和基础设施。Cola 架构最核心的分层结构是适配层Adapter Layer处理外部请求的入站适配和出站适配比如 Controller、消息消费者。应用层Application Layer负责业务流程编排、事务管理、权限校验但不包含具体业务规则。领域层Domain Layer核心业务逻辑所在包含领域对象、领域服务、业务规则。基础设施层Infrastructure Layer提供数据持久化、外部接口调用、消息发送等技术能力。看到这里你可能会问这和我用 AI 编程有什么关系关系非常大。Claude Code 这类工具虽然能力很强但它没有“架构审美”你给它一堆自由度它就会自由发挥。而 Cola 恰好提供了一套稳定的代码组织框架你可以把分层规则直接写进 Claude Code 的上下文里让它严格遵守。比如你在项目说明里告诉 Claude CodeController 只负责参数接收和响应包装业务逻辑必须放在 ApplicationService 里领域规则必须放在 Domain 层。这样一来AI 生成的代码就有了约束再也不会出现 Controller 里写 SQL 的情况了。Claude Code 支持CLAUDE.md这类项目级指令文件你可以在其中声明 Cola 分层规则让 AI 在每次生成代码前自动参考这些规则。这个用法我后面会详细演示。4. 实战准备用 MVP 矩阵拆一个具体需求理论讲了不少接下来我们要用 MVP 矩阵来拆一个真实可落地的项目需求并最终用 Claude Code 把它写出来。这个需求是我精心挑选的因为它足够小、足够典型又包含了 AI 编程最容易出问题的地方。4.1 原始需求假设你现在接到了一个需求开发一个“用户状态查询”接口。原始描述非常简短甚至有点模糊“做一个接口能查用户状态。”如果直接把这句话丢给 Claude Code它大概率会困惑“用户状态”是什么状态有哪些需不需要查数据库用户从哪来接口用什么格式返回这些信息Claude Code 不知道它只能猜。很多人遇到这种情况会觉得 AI 能力不够其实是因为需求描述不够清晰。4.2 用 MVP 矩阵拆解需求现在我们用 MVP 矩阵把这个需求从业务空间到工程空间逐步拆解。业务空间要解决什么问题业务目标让业务方能够查询指定用户的账号状态用于判断用户是否可以登录系统。目标用户运营人员系统的调用方比如网关鉴权服务。业务规则用户状态包括正常ACTIVE、禁用DISABLED、已删除DELETED、待审核PENDING。查询返回结果用于后续业务判断因此必须准确、可追踪。方案空间产品应该做什么基于业务空间方案空间需要定义 MVP 的最小功能集。MVP 功能清单提供一个查询接口入参是用户 ID。根据用户 ID 查询用户状态。返回标准格式的结果用户 ID、用户名称、用户状态、查询时间。如果用户不存在返回明确的错误码。不做的事不做注册、登录、用户管理。不做权限管理。不做状态变更接口。不做批量查询。这个“明确不做什么”非常关键。AI 编程时最容易出现的浪费就是 AI 帮你“顺便”实现了太多无关功能。系统空间系统如何组织在系统层面这个功能涉及两个模块user-web提供 HTTP 接口负责参数校验和响应封装。user-service负责核心业务逻辑和数据访问。这两个模块的交互关系user-web接收请求调用user-serviceuser-service从数据库读取用户数据返回给user-web。API 设计GET /api/v1/users/{userId}/status 响应200 OK { userId: 1001, userName: alice, status: ACTIVE, queriedAt: 2025-07-04T12:00:00Z } 响应404 NOT_FOUND { errorCode: USER_NOT_FOUND, errorMessage: 用户不存在 }工程空间代码如何落地到了这一步我们已经可以开始考虑代码结构了。按照 Cola 的分层思路我们这样组织代码Controller适配层接收 HTTP 请求调用应用层服务。ApplicationService应用层编排业务流程处理异常。DomainService领域层封装用户状态相关的领域规则。Repository基础设施层访问数据库查询用户数据。到这里MVP 矩阵的四个空间就都拆完了。接下来我们会把这个结构描述给 Claude Code让它严格按照这个结构生成代码。5. 完整实战Claude Code 从需求到代码的落地准备工作完成现在进入真正的实战环节。5.1 项目初始化首先创建一个空的 Spring Boot 项目项目名称可以叫user-status-demo。这里我假设你已经可以创建 Spring Boot 项目不再展开骨架代码的生成步骤。项目基础结构如下user-status-demo/ ├── pom.xml ├── src │ └── main │ ├── java │ │ └── com/example/userstatus │ │ ├── UserStatusApplication.java │ │ ├── controller/ │ │ ├── application/ │ │ ├── domain/ │ │ └── infrastructure/ │ └── resources │ └── application.yml在项目根目录创建CLAUDE.md这是 Claude Code 的项目级指令文件。Claude Code 会优先读取该文件里的规则每次生成代码前都会参考。# User Status Demo - Claude Code 项目说明 ## 项目简介 这是一个用户状态查询的 MVP 示例项目使用 Spring Boot 实现。 项目遵循 Cola 架构分层思想所有代码必须严格按层组织。 ## 架构规则 - controller 包只负责 HTTP 参数接收和响应包装禁止写入业务逻辑。 - application 包负责业务流程编排、异常处理和事务管理。 - domain 包负责领域规则和核心业务逻辑禁止依赖 Spring 框架。 - infrastructure 包负责数据库访问和外部服务调用。 ## 接口规范 - 统一返回结构code/message/data - 成功时 code 为 0失败时返回业务错误码 - 错误场景必须使用自定义异常不允许返回 null ## 开发约束 - 所有类名、方法名使用英文。 - 需要写单元测试的类必须在同一模块内创建对应测试。 - 修改代码前先描述修改计划确认后再动手。5.2 编写上下文补充文件除了CLAUDE.md我建议再创建一个docs/task.md把 MVP 矩阵的拆解结果写进去。这样 Claude Code 在生成代码时不仅能看架构规则还能看到完整的业务约束。# 任务用户状态查询接口 ## MVP 范围 - 入参用户 ID路径参数 - 出参用户 ID、用户名称、用户状态、查询时间 - 用户状态ACTIVE正常、DISABLED禁用、DELETED已删除、PENDING待审核 - 用户不存在时返回错误码 USER_NOT_FOUND ## 不做的事 - 不做注册、登录、用户管理 - 不做权限管理 - 不做状态变更接口 - 不做批量查询 ## 技术栈 - Spring Boot 3.x - MyBatis-Plus 或 Spring Data JPA选型见实际配置 - H2 内存数据库演示用5.3 提示词的最佳写法现在我们可以向 Claude Code 提出第一个任务了。推荐在项目根目录执行让 Claude Code 有完整的代码上下文claude然后输入以下提示词请先阅读 CLAUDE.md 和 docs/task.md理解项目背景和 MVP 范围。 然后根据 MVP 矩阵的工程空间拆分实现“用户状态查询”接口。 要求 1. 按照 Cola 分层结构创建 controller、application、domain、infrastructure 四个包。 2. Controller 只接收 userId 参数并返回统一响应结构。 3. ApplicationService 负责调用领域服务并处理 USER_NOT_FOUND 异常。 4. Domain 层定义 User 对象和 UserStatus 枚举。 5. Infrastructure 层使用 Repository 访问 H2 数据库。 6. 创建一个 UserControllerTest 单元测试验证 success 和 user not found 两个场景。 请先列出你的实现计划确认后再写代码。这里有几个值得注意的点我在提示词里明确引用了CLAUDE.md和docs/task.md让 AI 先读规则再动手。我要求它“先列出实现计划确认后再写代码”这样 AI 不会一次性生成一大堆代码避免难以 review。我把异常场景明确写进去了避免 AI 只写 happy path。5.4 核心代码演进由于方案比较复杂我们分阶段来实现。这里我会展示关键代码并说明每一段代码在 Cola 分层中的位置。先看infrastructure层的数据库访问。// 文件路径src/main/java/com/example/userstatus/infrastructure/UserRepository.java package com.example.userstatus.infrastructure; import com.example.userstatus.domain.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; Repository public interface UserRepository extends JpaRepositoryUser, Long { }接着是domain层的领域模型。这里我刻意把枚举定义放在 domain 层因为它属于核心业务规则。// 文件路径src/main/java/com/example/userstatus/domain/UserStatus.java package com.example.userstatus.domain; public enum UserStatus { ACTIVE, DISABLED, DELETED, PENDING }// 文件路径src/main/java/com/example/userstatus/domain/User.java package com.example.userstatus.domain; import jakarta.persistence.*; Entity Table(name t_user) public class User { Id private Long id; Column(nullable false) private String userName; Enumerated(EnumType.STRING) Column(nullable false) private UserStatus status; // 默认构造函数、getter、setter 省略实际生成时补齐 public User() { } public User(Long id, String userName, UserStatus status) { this.id id; this.userName userName; this.status status; } public Long getId() { return id; } public String getUserName() { return userName; } public UserStatus getStatus() { return status; } }然后看application层。应用层的核心职责是流程编排不写业务规则。// 文件路径src/main/java/com/example/userstatus/application/UserQueryService.java package com.example.userstatus.application; import com.example.userstatus.application.api.UserProfileDTO; import com.example.userstatus.domain.User; import com.example.userstatus.domain.UserStatus; import com.example.userstatus.infrastructure.UserRepository; import com.example.userstatus.application.exception.UserNotFoundException; import org.springframework.stereotype.Service; import java.time.OffsetDateTime; Service public class UserQueryService { private final UserRepository userRepository; public UserQueryService(UserRepository userRepository) { this.userRepository userRepository; } public UserProfileDTO queryUserStatus(Long userId) { User user userRepository.findById(userId) .orElseThrow(() - new UserNotFoundException(用户不存在userId userId)); UserStatus status user.getStatus(); OffsetDateTime queriedAt OffsetDateTime.now(); return new UserProfileDTO(user.getId(), user.getUserName(), status, queriedAt); } }最后是controller层只做参数接收和响应包装。// 文件路径src/main/java/com/example/userstatus/controller/UserController.java package com.example.userstatus.controller; import com.example.userstatus.application.UserQueryService; import com.example.userstatus.application.api.UserProfileDTO; import com.example.userstatus.application.exception.UserNotFoundException; import com.example.userstatus.controller.response.ApiResponse; import org.springframework.web.bind.annotation.*; RestController RequestMapping(/api/v1/users) public class UserController { private final UserQueryService userQueryService; public UserController(UserQueryService userQueryService) { this.userQueryService userQueryService; } GetMapping(/{userId}/status) public ApiResponseUserProfileDTO getUserStatus(PathVariable Long userId) { UserProfileDTO result userQueryService.queryUserStatus(userId); return ApiResponse.success(result); } ExceptionHandler(UserNotFoundException.class) public ApiResponseVoid handleUserNotFound(UserNotFoundException ex) { return ApiResponse.error(USER_NOT_FOUND, ex.getMessage()); } }5.5 运行与验证当 Claude Code 生成的代码完成后我们需要在本地运行验证。首先确保application.yml配置了 H2 数据库和测试数据# 文件路径src/main/resources/application.yml spring: datasource: url: jdbc:h2:mem:userdb;DB_CLOSE_DELAY-1 driver-class-name: org.h2.Driver username: sa password: jpa: hibernate: ddl-auto: create-drop show-sql: true启动项目后用 curl 验证接口curl http://localhost:8080/api/v1/users/1001/status如果数据存在预期返回{ code: 0, data: { userId: 1001, userName: alice, status: ACTIVE, queriedAt: 2025-07-04T12:00:00Z }, message: success }如果用户不存在预期返回{ code: USER_NOT_FOUND, data: null, message: 用户不存在userId9999 }5.6 从 MVP 到迭代演进的思路MVP 跑通之后功能迭代就可以进入正循环了。每新增一个功能都走一遍 MVP 矩阵业务空间新增业务目标是什么方案空间产品要做什么、不做什么系统空间涉及哪些模块工程空间代码落在哪一层举个例子后续如果要做“用户状态修改”接口你只需要在方案空间明确“只支持管理员调用、只支持 ACTIVE 和 DISABLED 互转”然后让 Claude Code 在 application 层新增一个方法domain 层新增状态变更领域服务即可。6. 常见问题与排查思路用 Claude Code 配合 Cola 架构开发时有几个问题出现频率很高。我整理成表格方便你排查。问题现象常见原因解决思路Claude Code 生成的代码没有按包分层CLAUDE.md没有写清楚架构规则在项目根目录补充分层约束重新描述任务AI 写出的 Controller 里包含大量业务逻辑任务描述太宽泛没有明确职责边界显式要求“Controller 只做参数接收和响应包装”生成的代码不完整出现空方法MVP 范围不明确AI 不确定该不该实现在docs/task.md写明必须实现的功能点接口返回结构不统一没有声明统一返回值规范在CLAUDE.md中明确 ApiResponse 的结构ChatGPT/Claude 重复生成相似功能的代码上下文丢失或没有记录已完成功能在任务文档中维护功能清单标记已完成项安装后提示地区不可用Claude Code 服务未在当前地区开放检查账号区域或组织权限换用可用环境模型名称报错客户端与云端模型不匹配升级 Claude Code 到最新版本确认账号可用模型列表再展开一个常见问题Claude Code 新开会话丢失上下文记忆。这是很多人遇到的问题其实这不算 Bug而是使用方式问题。Claude Code 本身有项目文件作为“长期记忆”真正可靠的记忆载体是CLAUDE.md、docs/目录和 Git 提交记录。每次新开会话时只要让 AI 先读这些文件就能快速恢复上下文。建议的使用习惯是每个功能点完成后在docs/task.md中更新“已完成功能”清单。新开会话后输入“请先阅读 CLAUDE.md 和 docs/task.md”再开始新任务。7. 最佳实践与工程建议最后这一节我结合自己的使用经验整理了一些关键的工程建议。这些建议不一定适用于所有团队但适合从“个人用 AI 写小项目”过渡到“团队用 AI 协作开发”的场景。7.1 需求文档比提示词更重要很多开发者以为 AI 编程的核心是提示词技术其实对中大型任务来说需求文档的优先级远高于提示词。一个结构清晰的docs/task.md比一段堆砌关键词的提示词有效得多。建议你在docs/目录下维护一份“需求说明书”包含背景、范围、不做的事、验收标准。每开发一个功能点就让 AI 先读这份文档再开始写代码。7.2 用 CLAUDE.md 固化团队规范CLAUDE.md是你的 AI 编程“宪法”可以写入以下内容项目结构规则。代码风格约束。禁止事项比如禁止在 Controller 里写业务逻辑。接口规范。测试要求。团队协作时把CLAUDE.md纳入代码评审范围。不要让它成为摆设每次评审时检查 AI 生成的代码是否遵守了这些规则。7.3 分阶段交付不用一次性生成大模块AI 编程最大的效率来源不是“一口气生成整个系统”而是“小步快跑、按模块验证”。一次任务的目标越集中AI 的产出质量越高。推荐的任务切分粒度单个接口实现不超过 5 个文件。单个数据库表变更含迁移脚本和实体更新。单类测试补齐controller 测试或 service 测试。7.4 测试先行保护迭代安全AI 生成代码之后必须补齐测试。不要觉得测试是浪费时间AI 迭代速度很快如果没有测试保护重构时的风险会成倍增加。用 Claude Code 写测试也很简单直接在任务里声明请为 UserController 生成单元测试覆盖成功场景和用户不存在场景。 测试需要使用 MockMvc不连接真实数据库。AI 会基于已有的 Controller 和 Service 代码自动生成测试用例。你要做的就是 review 测试断言是否符合业务预期。7.5 避免让 AI 越权处理安全与敏感操作这一点值得单独强调。AI 生成的代码在安全性上不可盲信尤其是涉及用户登录、权限校验、数据库变更时必须人工审查。具体来说涉及删除和更新的接口必须加权限校验和操作审计。生成的 SQL 参数化严防 SQL 注入。不把数据库密码和密钥写进代码仓库使用环境变量或配置中心。涉及生产环境的数据变更必须先备份再执行并在测试环境验证。8. 总结与下一步学习方向这篇文章的内容量不低我们既聊了 AI 编程的方法论也落地了一个具体接口的开发。现在回头来看整条路径其实核心就一句话拿到需求先用 MVP 矩阵拆解再用 Cola 分层约束最后才交给 Claude Code 写代码。拆解下来是三步第一用 MVP 矩阵把需求分成业务、方案、系统、工程四个空间明确做什么和不做什么。这一步最大的价值是减少 AI 的自由发挥空间。第二用 Cola 架构思想做代码分层约束把规则固化在CLAUDE.md中让 AI 每次生成代码前自动参考。第三用 Claude Code 分阶段生成代码先列计划再动手每完成一个功能点就更新任务文档确保新开会话时上下文不丢。如果你对这套方法有兴趣可以继续深入几个方向一是学习 DDD 领域驱动设计它和 Cola 架构有不少相通之处结合起来能处理更复杂的业务二是研究 Claude Code 的 Skill 机制把常用的开发流程封装成可复用的技能三是多看一些 AI 编程的 token 消耗分析了解哪些任务浪费 token 最多避免无谓消耗。最后说一点实战体会AI 编程工具现在越来越强但它的上限取决于你描述问题的能力。先把需求想清楚把结构定好AI 就是你手里最顺手的工程助手。希望这篇文章能帮你在 AI 编程的路上少走一些弯路。如果觉得内容对你有帮助可以收藏备用项目里遇到类似的架构问题随时翻出来对照。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价