实训第一周,我们小组五个人坐在教室里对着题目列表发呆。有人觉得这个太难,有人觉得那个太简单,吵了一下午最终定下来一个“校园二手交易系统”的方向——听起来平平无奇,整个学校估计有七八个组在做同类的东西,但它是我们当时能找到的最稳妥的选项。选完之后我才发现,真正决定这个项目能不能顺利走下去的,根本不是后面写代码的那些日子,而恰恰是那个下午和接下来一周里我们做的每一件“小事”。现在把这段前期准备的全过程整理成文字,写给正在参加项目实训、或者即将开始实训的同学参考,尤其是那些还停在“先装个环境”阶段的队伍。
这篇记录里没有高深的技术,讲的都是前期准备阶段那些具体、琐碎、却能决定后面几周运行节奏的事情:怎么判断题目合适、怎么给五个人分工、怎么统一技术栈、怎么定仓库和接口规范、怎么把模糊想法拆成能干完的任务。如果你恰好也在准备一次项目实训,照着这个思路走,能少走很多弯路。
1. 题目没选好,后面全是坑——实训选题阶段的真实判断标准
项目实训的第一步不是写代码,是选题。但很多小组在这个环节都把它做成了“头脑风暴大会”,想起什么就说什么,最后投票选一个“听起来最厉害”的。我们一开始也是这样,有人说做AI识别自习室占座,有人说做校园跑腿平台,还有人提议做一个全自动排课系统,最后被导师一句“你们打算用几周做完”问得全体沉默。
1.1 先回答三个问题:给谁用、解决什么、凭什么
判断一个题目能不能做,不能只凭“感兴趣”或者“看起来有技术含量”。我后来总结出一个筛选逻辑,在写任何一行代码之前,小组必须能把三句话说清楚:
一是这个系统给谁用。用户是学生、老师、管理员还是游客,决定了整个系统的权限设计和页面结构。拿我们当时差点选的“校园跑腿平台”来说,用户就分发布者、接单者、管理员三类,需要考虑的边界情况立刻翻倍,比如订单超时、跑腿员临时取消、费用结算,这些都需要设计合理的业务流程。
二是它到底解决什么实际问题。项目实训和商业项目不一样,不需要你做出多惊艳的商业模式,但你必须能说出某个真实存在的痛点。比如“校园二手交易系统”要解决的问题很简单:学校里的二手物品信息太散,都发在年级群和朋友圈里,需要有一个统一发布和检索的地方。越具体、越小的问题,反而越容易做出来。
三是你的小组凭什么能做完。这个“凭什么”是指组里有没有人会相关技术,有没有时间在实训周期内完成。我见过不少组选了一个看起来很前沿的方向,比如自然语言处理加知识图谱,结果组里没有一个人真正写过这类模型,前两周全在补基础,后期只能用别人的现成接口应付,答辩时提问一深就露馅。
1.2 好题目的三个硬指标
把上面的问题想清楚之后,还要用三个硬指标筛一遍:
- 工程量大不大。一个合格的实训题目,应该是在现有技术基础上稍微拓宽一点,不能是纯入门CRUD,也不能是一个大作业级别的“玩具”。像“二手交易系统”如果只是简单的发布列表加详情,工作量确实偏小,所以我们在里面加了搜索过滤、消息留言、收藏关注这些相对复杂一点的功能,保证模块够多,每个人都能分到活。
- 边界清不清楚。所谓边界,就是这个系统做到什么程度算完成。没有边界的题目最可怕,比如“做一个智能推荐系统”,推荐什么?给谁推?数据从哪里来?这些问题不定义清楚,做到最后你会发现永远有做不完的功能。
- 演示效果好不好的可能性高不高。实训最后要验收,一个能跑起来的、有完整页面流转的系统,和一堆写了一半的后端接口,观感完全不同。选题时就要想象一下,答辩现场你能为评委演示什么。
1.3 选题阶段的三个坑
第一个坑是“换题”。有的小组开始一两周后又觉得题目不合适,想换方向,结果之前积累的资料、数据库设计、代码骨架全废了。我们的做法是花时间做了一份简单的选择对比表,把候选题目的工程难度、兴趣程度、组员能力匹配度都列出来,确定后就再也没动过。
第二个坑是“照搬”。网上随便找一个开源项目改改名字就当作自己的题目,这种做法既学不到东西,也容易在演示时被问到答不上来。更实际一点说,实训的目的本来就是让你熟悉完整的项目流程,抄来的东西根本过不了自己心里那关。
第三个坑是“一个题目养活八个小组”。同一个方向做的人太多,最后的验收对比会很残酷,如果你没有比别人做得更多,就会显得很差。选题时可以稍微打听一下其他组的意向,尽量避开扎堆的方向,或者在同一方向里找一个不同的切入角度,比如别人做的是全场通用平台,你做一个针对特定场景的小工具,反而容易出彩。
2. 人员不在多,分工要对齐——团队配合里最容易被忽视的暗坑
我们组五个人,来自同一个专业方向,但各自的强项差异很大。有人前端写得多,有人后端逻辑熟,还有人对数据库比较敏感。按理说分工是件很自然的事,但真干起来才发现,把“谁擅长什么”和“项目需要什么”对齐,比想象中麻烦得多。
2.1 角色划分:别让四个人写同一个模块
最典型的错误是“大家一起写后端”。因为我们几个人都有点Java基础,一开始都想往后端挤,前端反而没人愿意碰。直到我们用一整个晚上把功能列表拆开数了一遍才发现,前端页面有十几个,工作量远比想象中大,而真正的核心业务逻辑,比如订单状态流转、商品搜索,集中在两三个关键模块里,根本不需要那么多人一起写。
最后的分工是:两个人负责前端页面和交互,两个人负责后端接口和业务逻辑,剩下一人同时承担数据库设计、接口文档维护和进度跟踪。说白了就是给他的前后端之间搭桥,还要盯住整个项目的节奏。这个分工方式不一定适合所有团队,但有一个原则是通用的——一定要有一个明确的“协调者”,不然各写各的,联调的时候就是灾难。
2.2 技术预研和风险认领
分工确定后,每个人对自己负责的部分做一次技术预研,也就是查资料弄清楚这部分要用的关键技术点、可能存在什么难点、需要多少时间去攻克。这一步能提前暴露问题,而不是等到开发中途才发现某个功能根本实现不了。
举个我们组的例子:负责前端的一个同学预研后发现,二手交易系统最核心的交互“商品分类筛选和关键词搜索”,如果全部自己写,排序和分页逻辑会很麻烦,但如果用现成的组件库加后端接口配合,难度能下降一大截。他把这个发现带回来,大家一看有道理,就把这个功能的技术方案定了下来,顺带还定了一个规则——每个人在预研阶段发现的难点,就由他本人负责在开发阶段解决,算是“风险认领”机制。这个机制的好处是,没有人能说自己那块做不了,因为难点的解决方案在开工前就已经想过了。
2.3 团队协作规矩要提前说清楚
这个东西看起来虚,后面却帮我们省了很多事。比如:
- 站会制度。每天上课前花十分钟过一遍,每个人说三件事:昨天做了什么、今天打算做什么、有没有卡住的地方。不需要软件工具,群里发消息都行,但必须固定时间说,防止有人闷头做了一周方向全错。
- 问题上报时限。任何一个人遇到超过两个小时的难题,必须立刻发到群里求助,不准自己死磕。这个规则执行起来刚开始挺尴尬,但时间长了大家反而更愿意暴露问题,因为越早说,解决的代价越小。
- 代码归属清晰。每个模块指定唯一负责人,其他人可以提建议,但不能擅自改别人的模块。改代码前必须在群里说一声,避免冲突。
这些规矩说不上高大上,但确实让我们避免了后期因为接二连三的代码冲突而心态爆炸。
3. 技术栈别追新,稳定才是第一位——选型、版本与开发环境的落地细节
技术选型是我们前期准备里吵得最激烈的一个环节,因为每个人都有自己偏好的技术。有人想用最新的前端框架版本,有人觉得后端必须上微服务,还有人提出要用一个大家都不熟的数据库。最后被导师一句话点醒:“实训就几周时间,你们是要做完项目,不是要做技术调研报告。”
3.1 为什么我们选了这套组合
最终我们定下的组合是:前端用当前主流框架加UI组件库,后端用熟悉的Java框架,数据库用最普遍的MySQL,服务器部署在一个云主机上。这个组合里没有一样是“前沿技术”,但每一环都满足三个前提:组内有人用过、网上资料足够多、出现问题能找到人问。
在选型问题上,我建议每个小组先回答一个问题:如果实训周期缩短三分之一,你的技术栈还能不能支撑你按时做完?如果答案是否定的,就说明这个技术栈对你来说太复杂了,应该换一个更稳的。我们当时放弃微服务转向单应用就是这个道理——微服务的注册中心、配置中心、网关这些概念本身就够学两周了,而我们的项目规模根本用不上这些。
3.2 版本统一和开发环境搭建
技术选型定下来之后,最折磨人的是版本统一。前后端工具链、依赖包版本、数据库版本,任何一个人的环境和别人不一样,就会出现“我本地跑得好好的,你那里怎么报错”的诡异问题。我们的解决办法是写一份环境配置说明,把每个人的开发环境强制拉到同一套版本上。
以我们当时的配置为例(版本号不必照抄,以实训当期可用稳定版本为准):
- 前端框架使用主流的稳定版本,Node.js 版本不低于 18,包管理工具统一使用 npm。
- 后端开发工具包使用至少长期维护的版本,例如 JDK 17 或 21,编译级别和项目语言级别保持一致。
- 数据库统一使用 8.0 以上版本,字符集统一为 utf8mb4,数据库连接时区信息显式配置。
这些内容单独看都很细碎,不统一带来的后果却是最严重的。我们组曾经因为一个人用了旧版的数据库驱动,导致他本地插入中文数据全是问号,排查了一整个晚上才发现是版本问题。
3.3 一个典型的环境配置检查清单
再提供一个通用的配置检查清单,实训小组可以直接照抄到自己的文档里:
- 是否所有人使用同一个 IDE 版本,或者至少统一编译器版本。
- 是否导出并共享了统一的代码格式化配置,避免合并代码时出现大量格式差异。
- 是否统一了命名规范(包名、类名、接口路径、数据库表名)。
- 是否统一了开发环境的最低硬件要求(这个对小组内有人电脑比较旧的情况很重要)。
- 是否在项目根目录放了一份 README,写清楚克隆代码后要怎么把环境跑起来。
这份清单不是给人看的,是给“遇到问题时”的自己看的。花一天时间把这些定好,胜过开发中花三天找出环境差异。
4. 仓库、数据库、接口规范,前期定死比后期救火省一万倍力气
实训进入第二周,我们开始搭项目骨架。这个时候我意识到一个很隐蔽的问题:如果没有一套约定俗成的规范,大家按自己的习惯写代码,项目很快会变成一个没法维护的毛线团。前期把仓库、数据库、接口三块规范定清楚,是我们做得最值的一件事。
4.1 Git仓库:分支策略和提交规范
Git的使用策略是前期准备的重点。我们的分支策略很简单:一个大分支作为主分支,负责维护可运行版本,平时所有人开发都在自己的功能分支上进行,做完一个功能合并回主分支之前,先让另一个人帮忙检查一遍合并。听起来很简单,但合理就好,不要一上来就搞多环境多分支的复杂策略,实训团队驾驭不住的。
合并代码的冲突问题我们采取了一个笨办法:每个人开发的时候都从最新的主分支拉一个分支出来,尽量保证自己负责的模块和其他人不交叉。一旦出现两个人同时改同一个文件,就说明分工有问题,需要重新划分边界。这个策略执行下来,我们的冲突次数一只手数得过来。
提交规范同样重要。我们的约定是每次提交必须写清楚“做了什么、为什么这样做”,禁止出现“update”“修改bug”这样的提交记录。这个习惯让后期回溯问题时省了很多时间——只要看一眼提交历史,就知道某个功能是哪次提交引入的,负责人是谁。
4.2 数据库建模与迁移脚本
数据库设计是我们前期投入时间最多的地方,也最值得。我们花了两天把表结构、字段类型、主外键关系全部画了出来,包括商品分类表、商品信息表、用户表、收藏表、留言表等。所有字段的类型、长度、默认值、是否为空都明文定好,写在一份数据库设计文档里。
这里有个关键经验:一定要用带版本控制的迁移脚本建表,而不是让每个人在自己的客户端里手工建表。手工建表最大的问题是想起来加一个字段就加一个,最后每个人的表结构都不一致,联调时接口报错也不知道是代码问题还是数据问题。我们的做法是写一个初始化建表脚本放进项目代码,任何人拉下来代码后执行一遍脚本就能得到完全一致的数据库结构。
字段命名也是一开始就要统一的。我们约定所有表中创建时间叫 create_time,更新时间叫 update_time,逻辑删除标记叫 is_deleted,关联字段统一用对应表名加主键字段名来命名。这套约定看上去简单,却能让后端代码里的实体映射变得非常整洁,不用每个字段都写注释都能看懂。
4.3 接口文档先行
我们要求每个后端接口在写代码之前,先把接口文档写出来。文档包含接口路径、请求方式、请求参数、返回数据结构、错误码含义五部分。前端按照文档先写页面调用逻辑,后端按文档实现,两边不互相等待。
实际上这个要求刚开始很难坚持,因为写文档比写代码枯燥多了。但坚持到联调阶段,好处就体现出来了:前端同学不再需要反复追着问“这个接口返回的是名字还是ID”“那个字段到底叫啥”,直接查文档就能自己解决。文档还会随代码一起放入仓库,改了接口必须同步更新,这个约定需要组员之间互相提醒才能形成惯性。
接口返回的数据结构也要统一。我们约定所有接口返回固定格式,包含状态码、消息和数据三个部分,成功是 200,业务错误用 400 系列,系统错误用 500。这个格式帮我们省了很多判断分歧,前端也只需要写一套处理逻辑就能应对所有接口。
5. 把“想做的东西”拆成“能干完的任务”——需求清单与里程碑规划
最后一步,也是被很多小组忽视的一步,是把脑子里的模糊需求落成一张可以执行的任务清单。我见过有的小组开工第一天就开始写代码,写到一半发现功能做多了,时间不够,只能砍需求,砍来砍去项目变得七零八落。我们不希望这样,所以在写代码前认认真真做了一版需求拆解。
5.1 一句话需求和三档优先级
第一件事是把整个项目用一句话说清楚。我们的说法是:做一个面向在校学生的二手物品信息发布与检索平台,支持用户注册登录、发布商品、浏览搜索、收藏留言和订单管理。这一句话虽然朴素,却保证了所有人对项目的理解一致,不会出现前端以为要做成一个购物网站、后端以为只是一个信息墙的乌龙。
这句话确认之后,把所有功能列成清单,分三个优先级:
- 必须做:注册登录、发布商品、商品列表、详情、搜索筛选、后台权限管理、数据初始化。这些功能缺失任何一个,整个项目立不住。
- 尽量做:收藏关注、留言互动、个人中心、订单状态流转、数据统计。这些功能是项目的亮点,也是答辩时可以展开讲的部分。
- 有余力做:图片压缩上传、邮件通知、消息提醒、更精细的权限控制。这些属于锦上添花,做完更好,做不完也不影响基本盘。
这个优先级列表帮我们很多次避免临时加需求。小组里一旦有人提出“我们再加个XX功能吧”,先问一句它在哪个优先级里,不是这档次的,留到后期看时间再说,不要中途改道。
5.2 里程碑拆解:第一周到验收周的节奏安排
技术预研完成之后,我们按周排了一个节奏表:
第一周完成选题和组队,定好技术栈、仓库和规范。第二周完成详细设计文档,包括数据库设计、接口文档和页面草图。第三至第四周开发核心模块,优先做必须做的功能。第五至第六周做尽量做级别的功能,逐步填充亮点。第七周进入联调阶段,前后端统一跑通所有流程,修复暴露出来的问题。最后一周整理演示用的数据、写README、准备答辩材料和总结。
这个分工里最重要的一点是给最后两周留了弹性时间。几乎每个实训项目都会在联调阶段发现一堆之前没考虑到的兼容问题,如果前面把时间排得满满当当,最后就会陷入越慌越乱的局面。宁可前面加班,也要保住最后两周的缓冲。
5.3 提前把演示材料准备起来
这个建议说出来可能显得很“应试”,但对实训来说特别有用。从第二周开始,我们就把每周做了什么、解决了什么问题、有什么收获记录在共享文档里,这些记录最后变成了答辩总结的现成素材。演示用的示例数据我们也是提前准备好的,包括几十条真实的二手物品信息、几个不同类型的用户账号,确保答辩现场不需要临场注册账号、现场发布商品,省去很多不必要的紧张和尴尬。
更重要的是,这些记录可以倒逼自己思考“我这周到底做出了什么”,及时发现看似忙了一周、实际上什么都没沉淀下来的危险信号。
回头看前期准备这一周,我们做的事情全都很琐碎:判断一个合适的题目、分清楚每个人的边界、统一技术栈版本、定好仓库和接口规范、把需求拆成任务清单。没有一项是直接产出代码的,但每一项都在为后面的开发减少不确定性和返工成本。尤其是接口文档先行和数据库迁移脚本这两件事,让我们在联调阶段几乎没有因为接口字段对不上、表结构不一致而扯皮。
如果非要说一个最重要的心得,那就是“前期慢就是后期快”。实训最怕的不是做得慢,而是方向错了、规范没定好导致的反复重来。前期多花一点时间把这些问题想清楚,后面每一天的代码都是在给这个决定添砖加瓦。我在这次实训里踩过的这些坑,希望正在读这篇记录的你不用再踩一遍。下一次更新,我会继续写核心功能开发阶段遇到的实际问题和解决办法。