在创业公司卷了三年,聊聊我折腾过的那些技术探索
一个啥都干的全栈狗,准备换个环境前,把这几年的折腾记录整理一下。
先说点掏心窝子的话
兄弟们好,我是老K,在一家做SaaS的创业公司待了三年零两个月了。说是全栈开发,其实就是"啥都干"的代名词——前端Vue、React写过,后端Go、Java撸过,数据库MySQL、MongoDB调过,甚至连运维的K8s、Docker也得自己搞。老板常说的一句话就是:"咱们是小公司,能者多劳嘛。"翻译一下就是:你一个人干三个人的活,拿一个人的工资。
最近开始看机会了,准备更新一下简历,顺便把这几年踩过的坑、做过的一些技术探索整理出来。毕竟在创业公司最大的好处就是——你能接触到各种各样的业务场景,从0到1搭系统、从1到100优化性能,什么脏活累活都干过。
今天这篇文章,就聊聊我在这几年里做的一些技术探索和实践,有成功的经验,也有血泪的教训,希望能给同样在创业公司摸爬滚打的兄弟们一些参考。
那次差点把我逼疯的架构重构
去年Q3的时候,公司的核心业务系统已经跑了快两年了。说实话,最开始为了快速上线,代码写得那叫一个"狂野"——三层架构?不存在的,Controller直接调DAO,业务逻辑全堆在Service里,一个类能写三千行。
当时的场景是这样的:产品经理小王跑过来说,"K哥,客户提了个新需求,要在订单模块加个审批流。"我一看代码,好家伙,订单模块的代码已经是个"屎山"了,改一处动全身。
// 当时的订单Service,大概长这样
@Service
public class OrderService {
// 3000+ 行代码...
// 下单、支付、退款、审批、导出全在这里面
public void createOrder(OrderDTO dto) {
// 直接操作数据库,没有任何分层
orderDao.insert(dto);
// 发短信通知
smsService.send(dto.getPhone(), "您的订单已创建");
// 扣库存
inventoryDao.deduct(dto.getSkuId(), dto.getQuantity());
// 计算佣金
commissionService.calculate(dto);
// ... 还有几十个这样的调用
}
}
当时真的想砸电脑。但没办法,活还得干。我花了两周时间,拉着后端另一个兄弟一起做了次大重构:
- 引入DDD领域驱动设计的思想,把订单、支付、库存拆成独立的领域
- 使用事件驱动架构,模块间通过消息队列解耦
- 引入CQRS模式,读写分离,查询走ES
重构后的效果是显著的:
| 指标 | 重构前 | 重构后 | 提升 |
|---|---|---|---|
| 代码行数(核心模块) | 12000+ | 6500 | -45% |
| 单次需求开发周期 | 5-7天 | 2-3天 | -50% |
| 线上Bug率 | 8个/月 | 2个/月 | -75% |
| 接口平均响应时间 | 850ms | 320ms | -62% |
说实话,这次重构让我对架构设计有了更深的理解。在创业公司,很多时候为了赶进度会写很多"临时方案",但这些临时方案如果不及时还债,迟早会变成压垮团队的最后一根稻草。
聊聊代码质量这件事
在创业公司谈代码质量,很多人会觉得"都什么时候了,还搞这些虚的"。但我这几年的经验告诉我,代码质量不是虚的,它是实打实的生产力。
我给自己定了几条规矩,也慢慢在团队里推行开来:
1. Code Review是必须的
刚开始团队就三个人,大家觉得CR浪费时间。但我坚持每次PR都要至少一个人review。刚开始确实慢,但一个月后效果就出来了——很多低级Bug在CR阶段就被发现了,测试那边的Bug反馈量直接降了30%。
2. 单元测试不能偷懒
我知道很多兄弟觉得写单测是浪费时间,但在我们这种小团队,没有QA专门给你写测试用例,单测就是你的最后一道防线。我现在的要求是核心业务逻辑覆盖率至少80%。
// 用Go写的单测示例
func TestCalculateDiscount(t *testing.T) {
tests := []struct {
name string
input Order
expected float64
}{
{
name: "普通用户无折扣",
input: Order{UserID: 1, Amount: 100, UserLevel: "normal"},
expected: 100,
},
{
name: "VIP用户9折",
input: Order{UserID: 2, Amount: 100, UserLevel: "vip"},
expected: 90,
},
{
name: "SVIP用户8折",
input: Order{UserID: 3, Amount: 100, UserLevel: "svip"},
expected: 80,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := CalculateDiscount(tt.input)
if result != tt.expected {
t.Errorf("expected %f, got %f", tt.expected, result)
}
})
}
}
3. 文档要跟上
这个真的是血泪教训。去年有个兄弟离职了,他负责的那个模块没有任何文档,我接手的时候看得一头雾水,花了整整一周才理清逻辑。从那以后,我要求每个核心模块必须有README,每个接口必须有Swagger文档。
开源项目,我的技术成长加速器
说到技术成长,不得不提开源项目。我这人有个习惯,遇到技术问题喜欢去翻源码。不是什么高大上的原因,就是觉得看大佬写的代码能学到很多东西。
这几年我主要研究了这几个项目:
- Gin:学它的中间件设计和路由树实现
- Zap:学它的日志库设计,特别是零分配的理念
- Etcd:学它的Raft协议实现和分布式一致性
举个例子,之前我们的系统有个性能瓶颈,接口响应时间老是下不来。我去翻了Gin的源码,发现我们在中间件里做了一些不必要的操作,每次请求都会重新解析一些配置。改成预加载之后,性能直接提升了20%。
// 优化前:每次请求都解析配置
func ConfigMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
config := parseConfig() // 每次请求都解析,慢!
c.Set("config", config)
c.Next()
}
}
// 优化后:启动时预加载
var globalConfig *Config
func init() {
globalConfig = parseConfig() // 启动时解析一次
}
func ConfigMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("config", globalConfig)
c.Next()
}
}
这种从源码里学到的东西,比看十篇博客都管用。
关于AI工具的一些思考
说到技术探索,今年最火的肯定是AI了。说实话,刚开始我对这些AI工具是持怀疑态度的——"这玩意能写代码?别逗了。"
但自从试了文心一言之后,我的想法变了。不是说它能完全替代程序员,但在很多场景下确实能提高效率:
- 写单测:给它一段业务代码,让它生成测试用例,基本能覆盖80%的场景
- 写文档:接口文档、README这些重复性的工作,让AI先写个初稿,再人工润色
- Code Review辅助:让它帮忙检查一些常见的代码问题,比如空指针、资源泄露等
- 技术方案调研:需要调研某个技术方案的时候,让它先给个概览,再深入去看
当然,AI生成的代码不能直接用,还是要人工review的。但作为一个辅助工具,确实能省下不少时间。
我们团队现在的工作流大概是这样的:
需求分析 → 技术方案(AI辅助调研)→ 编码(AI辅助生成基础代码)
→ Code Review(AI辅助检查 + 人工review)→ 测试(AI辅助生成用例)
→ 上线
效率大概提升了30%左右,最重要的是,大家可以把更多精力放在业务逻辑和架构设计上,而不是那些重复性的工作。
给创业公司技术人的几点建议
最后,结合这几年的经验,给同样在创业公司奋斗的兄弟们几点建议:
- 不要只做CRUD boy/girl:创业公司机会多,主动去承担一些有挑战的任务,哪怕一开始做不好
- 重视基础:数据结构、算法、网络协议这些基础东西,关键时刻能救命
- 保持学习:技术更新太快了,每天抽半小时看看技术文章、翻翻源码
- 多总结:做完一个项目,花点时间写个总结,不一定要发博客,自己看看也行
- 关注业务:技术最终是为业务服务的,理解业务才能做出好的技术方案
写在最后
在创业公司待了三年,虽然累,但成长也是真的快。从最开始只会写CRUD,到现在能独立负责一个系统的设计和开发,中间踩了无数的坑,也收获了很多。
现在准备换个环境,不是对现在公司有什么不满,就是觉得自己到了一个瓶颈期,想看看外面的世界。面试的时候,把这些年的技术探索和实践整理出来,也算是对自己这三年一个交代。
希望这篇文章能对你们有些帮助。如果有什么问题,欢迎在评论区交流。
好了,不说了,产品经理又来找我了,说是要加个"五彩斑斓的黑"的需求...
溜了溜了~ 🏃♂️
本文纯属个人经验分享,如有雷同,那说明咱们踩的是同一个坑。


评论 0