2022年NFT市场冷却,我们的NFT交易平台业务量下降,终于有时间重构之前赶进度写的烂代码。
本文分享这次代码重构的经历,包括重构前的问题、重构的原则、具体的重构步骤、遇到的坑,以及重构后的效果。
一、为什么要重构
1. 业务背景
我们做了一个NFT交易平台。
2021年NFT火热的时候,我们赶进度,快速上线了平台。那时候,业务优先,代码质量只能往后放。
结果就是:
- 代码写得很仓促
- 很多地方是临时方案
- 技术债越积越多
- 维护越来越困难
2022年,NFT市场冷却了,业务量下降。我们终于有时间,回过头来重构代码。
2. 重构前的问题
重构前,代码有很多问题。
代码混乱:
- 一个文件几千行
- 函数又长又复杂
- 命名不规范
- 注释很少
架构不合理:
- 业务逻辑和数据操作混在一起
- 没有分层
- 模块之间耦合严重
- 改一个地方,影响很多地方
性能问题:
- 数据库查询慢
- 有N+1查询
- 没有缓存
- 接口响应慢
安全隐患:
- 输入没有校验
- SQL拼接(虽然用了ORM,但有些地方还是拼接)
- 权限控制不严
- 错误处理不完善
测试缺失:
- 几乎没有单元测试
- 没有集成测试
- 改代码全靠手动测试
- 经常改出bug
这些问题,让开发效率越来越低,bug越来越多。
3. 为什么现在重构
为什么选择现在重构?
- 业务量下降,有时间了
- 新人接手,看不懂代码
- bug越来越多,维护成本高
- 性能问题开始显现
- 再不重构,项目就没法维护了
市场冷却,反而给了我们重构的机会。
二、重构的原则
重构不是重写,我们制定了一些原则。
1. 渐进式重构
不要一次性全部重写。
- 一个模块一个模块地重构
- 每次重构一小部分
- 重构完就测试、上线
- 风险可控
一次性重写,风险太大,容易出问题。渐进式重构,更稳妥。
2. 保持功能不变
重构的目标是改善代码结构,不是改变功能。
- 重构前后,功能要一样
- 用户感知不到变化
- 接口不变
- 数据不变
如果重构改变了功能,那就不是重构,而是重写了。
3. 有测试保护
重构前,先补测试。
- 给要重构的代码写测试
- 确保测试通过
- 重构后,测试依然通过
- 测试是重构的安全网
没有测试的重构,就是赌博。
4. 小步提交
每次重构,小步提交。
- 一个改动一个commit
- commit信息清晰
- 方便回滚
- 方便code review
小步提交,出了问题容易定位和回滚。
5. 持续集成
重构过程中,持续集成。
- 每次提交,自动跑测试
- 测试不通过,不能合并
- 代码质量检查
- 确保主干一直可用
三、重构的步骤
我们的重构,分了几个步骤。
1. 第一步:梳理代码
先梳理代码,了解现状。
- 画架构图
- 梳理模块关系
- 找出最乱的地方
- 列出技术债清单
- 评估重构的优先级
梳理完,我们对代码的整体情况有了了解,也知道了从哪里开始。
2. 第二步:补测试
重构前,先补测试。
- 给核心业务逻辑写单元测试
- 给关键接口写集成测试
- 测试覆盖率尽量高
- 确保测试能覆盖主要场景
补测试的过程,也是理解代码的过程。很多bug,就是在补测试的时候发现的。
3. 第三步:分层架构
我们首先做的,是分层。
重构前,代码是这样的:
controllers/
UserController.php // 包含了业务逻辑、数据操作、甚至HTML
models/
User.php // 只有数据库映射重构后,变成了:
controllers/
UserController.php // 只处理HTTP请求和响应
services/
UserService.php // 业务逻辑
repositories/
UserRepository.php // 数据操作
models/
User.php // 数据模型每一层职责清晰:
- Controller:处理HTTP,参数校验,调用Service
- Service:业务逻辑,事务控制
- Repository:数据操作,查询封装
- Model:数据结构
分层之后,代码清晰了很多。
4. 第四步:拆分大文件
然后,拆分大文件。
- 一个文件不超过300行
- 一个函数不超过50行
- 按职责拆分
- 命名清晰
比如,原来的OrderController.php有2000行,拆成了:
- OrderController:订单相关接口
- OrderService:订单业务逻辑
- OrderRepository:订单数据操作
- OrderValidator:订单参数校验
拆分之后,每个文件都很小,职责清晰,容易维护。
5. 第五步:优化数据库查询
接下来,优化数据库查询。
解决N+1查询:
// 不好:N+1查询
$orders = Order::all();
foreach ($orders as $order) {
echo $order->user->name; // 每次都查数据库
}
// 好:预加载
$orders = Order::with('user')->get();
foreach ($orders as $order) {
echo $order->user->name; // 不查数据库
}加索引:
- 给常用查询字段加索引
- 复合索引
- 定期检查慢查询
加缓存:
- 热点数据加缓存
- 用Redis缓存
- 设置合理的过期时间
- 注意缓存更新
优化之后,接口响应速度提升了很多。
6. 第六步:统一错误处理
重构前,错误处理很乱。
- 有的地方返回错误数组
- 有的地方抛异常
- 有的地方直接die
- 错误信息不统一
重构后,统一了错误处理:
// 统一异常处理
class ApiException extends Exception {
public function __construct($code, $message) {
parent::__construct($message, $code);
}
}
// 全局异常处理器
class ExceptionHandler {
public function render($request, Exception $e) {
if ($e instanceof ApiException) {
return response()->json([
'code' => $e->getCode(),
'message' => $e->getMessage(),
], 400);
}
// 其他异常
return response()->json([
'code' => 500,
'message' => '服务器错误',
], 500);
}
}统一之后,错误处理清晰了,前端也好处理。
7. 第七步:参数校验
重构前,参数校验散落在各处。
重构后,用了统一的校验层:
class CreateOrderRequest {
public function rules() {
return [
'nft_id' => 'required|integer|exists:nfts,id',
'price' => 'required|numeric|min:0',
'payment_method' => 'required|in:eth,usdt',
];
}
}Controller里直接用:
public function create(CreateOrderRequest $request) {
$data = $request->validated();
// 业务逻辑
}参数校验统一了,代码更简洁,也更安全。
8. 第八步:代码规范
最后,统一代码规范。
- 用PSR规范
- 统一命名
- 统一注释风格
- 用代码格式化工具
- 用静态分析工具
代码规范统一了,团队协作更顺畅。
四、遇到的坑
重构过程中,我们踩了不少坑。
1. 坑一:没有测试就重构
最开始,我们有些地方没写测试就重构了。
结果:
- 重构完,发现功能变了
- 找了半天才找到问题
- 不得不回滚
教训:重构前,一定要先写测试。没有测试保护的重构,太危险了。
2. 坑二:一次性改太多
有一次,我们一个模块改了太多东西。
结果:
- 出了bug,不知道是哪里改的
- 回滚也麻烦
- 花了很多时间排查
教训:小步提交,每次只改一点。改完测试,没问题再继续。
3. 坑三:改了接口
重构的时候,不小心改了接口的返回格式。
结果:
- 前端报错
- 用户投诉
- 紧急回滚
教训:重构要保持接口不变。如果必须改,要和前端协调,做好兼容。
4. 坑四:忽略了边界情况
重构的时候,只考虑了正常流程,忽略了边界情况。
结果:
- 一些异常情况出了bug
- 比如订单取消、退款等场景
- 测试没覆盖到
教训:重构前,要把所有场景都列出来,测试要覆盖边界情况。
5. 坑五:性能下降
有一次重构,代码结构变好了,但性能下降了。
原因:
- 分层多了,多了几层调用
- 有些查询没优化
- 缓存策略变了
教训:重构后要做性能测试,确保性能不下降。结构好,性能也要好。
五、重构后的效果
经过几个月的重构,效果很明显。
1. 代码质量提升
- 代码结构清晰
- 命名规范
- 注释完善
- 职责分明
新人接手,能快速看懂代码。
2. 开发效率提升
- 改bug更快了
- 加新功能更容易了
- 代码复用率高了
- 团队协作更顺畅
开发效率,比重构前提升了很多。
3. bug减少
- 测试覆盖了主要场景
- 代码结构清晰,不容易写错
- 静态检查发现了很多潜在问题
- 线上bug明显减少
4. 性能提升
- 数据库查询优化了
- 加了缓存
- 接口响应速度提升了
- 服务器负载降低了
5. 团队信心提升
- 代码不再是"烂代码"
- 大家愿意维护了
- 加新功能不再恐惧
- 团队士气提升了
六、重构的经验总结
这次重构,我们总结了一些经验。
1. 技术债要及时还
- 不要让技术债越积越多
- 有时间就还一点
- 不要等还不起了才想起来
- 技术债是有利息的,越晚还,成本越高
2. 重构是常态
- 重构不是一次性的
- 是持续的过程
- 每次写代码,都可以顺手重构
- "童子军规则":离开时比来时更干净
3. 测试是基础
- 没有测试,不要重构
- 测试是重构的安全网
- 测试覆盖率要够
- 测试也要维护
4. 渐进式重构
- 不要一次性重写
- 小步快跑
- 每次重构一点
- 风险可控
5. 保持功能不变
- 重构不是重写
- 功能要保持不变
- 接口要保持不变
- 用户感知不到变化
6. 性能不能降
- 重构后要做性能测试
- 结构好,性能也要好
- 不要为了结构牺牲性能
- 找到结构和性能的平衡点
七、给想重构的人的建议
如果你也想重构代码,我的建议:
1. 先评估
- 评估代码的现状
- 列出技术债
- 评估重构的成本和收益
- 确定重构的优先级
2. 从最痛的地方开始
- 从bug最多的地方开始
- 从最难维护的地方开始
- 从最影响效率的地方开始
- 不要从无关紧要的地方开始
3. 先补测试
- 重构前,先补测试
- 确保测试覆盖主要场景
- 测试通过了,再开始重构
4. 小步重构
- 每次只重构一个模块
- 每次只改一点
- 改完就测试
- 测试通过就上线
5. 持续监控
- 重构后,监控线上情况
- 看有没有bug
- 看性能有没有下降
- 有问题及时回滚
八、写在最后
NFT市场冷却了,但我们的代码质量提升了。
这次重构,从烂代码到优雅代码,花了几个月的时间。过程很辛苦,但结果很值得。代码质量提升了,开发效率提升了,bug减少了,团队也更有信心了。
市场有周期,冷的时候,正好可以修炼内功。把代码写好,把基础打牢,等市场回暖的时候,才能快速出击。
2022年了,很多行业都在经历寒冬。寒冬不可怕,可怕的是在寒冬里什么都不做。利用这段时间,提升自己,优化代码,修炼内功,等春天来了,才能更好地出发。
最后,用一句话总结:"代码重构,不是负担,而是投资。从烂代码到优雅代码,提升的不只是代码质量,还有团队的效率和信心。"
愿你的代码,优雅而高效。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录