Docker 构建速度总是上不来?先看看镜像层和上下文
很多人第一次写 Dockerfile 时,只关心“能不能构建成功”。等到项目稍微变大一点,就会开始抱怨镜像构建太慢。这个时候常见的反应是去找加速源、换基础镜像、甚至怀疑 Docker 本身不稳定。但经验告诉我,真正拖慢本地构建速度的,很多时候不是网络,而是你自己把缓存层次和构建上下文写得太随意了。
很多人第一次写 Dockerfile 时,只关心“能不能构建成功”。等到项目稍微变大一点,就会开始抱怨镜像构建太慢。这个时候常见的反应是去找加速源、换基础镜像、甚至怀疑 Docker 本身不稳定。但经验告诉我,真正拖慢本地构建速度的,很多时候不是网络,而是你自己把缓存层次和构建上下文写得太随意了。
刚开始用 Docker Compose 做本地联调时,最容易犯的错误就是还在用宿主机思维理解容器之间的访问关系。比如 API 容器去连数据库时,配置里还写 127.0.0.1,结果明明数据库已经启动了,应用还是一直报连接失败。这个问题其实不是 Compose 配错了,而是没有建立起一个很关键的认知:同一个 Compose 网络里,服务名本身就是彼此访问时最方便的地址。
用 slice 实现栈和队列几乎是 Go 入门练习里的必做题。因为语法不复杂,append 也足够顺手,所以很多人会觉得这类题没什么难度。但真正把它写进稍微像样一点的小程序里时,你会发现问题并不在“会不会写”,而在“有没有考虑清楚容量变化、出队后残留引用、空结构操作这些细节”。
很多人刷算法题时喜欢挑复杂题,觉得这样更有挑战。但从练代码基本功的角度看,真正值得反复写的,往往是那些规则简单、边界丰富的小题。递归和二分查找就是这种题型的代表。它们不靠花哨技巧取胜,却非常适合训练你是不是能把停止条件、输入约束和函数职责写清楚。
Go 标准库里的 encoding/json 已经够用了,平时做接口、配置读取、消息体解析,大多数场景都离不开它。问题在于,很多初学阶段写出来的 JSON 代码“能跑”但并不稳。真正进入接口联调或者服务之间互相调用时,字段命名、默认值、空值含义这些细节很快就会冒出来。
刚开始写 Go 的时候,遇到文本拼接最容易想到的还是 +。代码少时当然没问题,但一旦进入循环、日志拼接、模板前处理或者接口响应组装,这种写法很快就会让代码既不清晰,也不够稳妥。真正开始做一些小工具之后,我发现自己最常用的其实不是花哨技巧,而是把 strings.Builder 和 bytes.Buffer 的边界分清楚。
刚从 GOPATH 思维切到 Go Modules 时,很多人最直观的感受是目录终于自由了,不一定要把项目放在固定路径下。可真正让我觉得它实用的,是本地联调依赖仓库时,不用再靠复制粘贴代码或者临时改 import 路径硬顶过去。
Go 初学阶段很容易把 slice 和 map 当成“比数组和字典好用一点的容器”。真正开始写业务后,很多让人困惑的 bug 其实都和这两个基础类型有关:为什么函数里改了 slice,外面数据也变了;为什么 map 还没赋值就 panic;为什么看起来只是截了个子切片,内存却迟迟下不来。
Go 学久一点以后,很多人都会记住一句话:interface 要小。但真到项目里,接口问题往往不是“大或小”这么简单,而是抽象出现得太早。实现只有一种、调用方式还没定稳,就先定义一堆 UserService、OrderRepository 接口,最后只会让代码多一层跳转。
刚开始写 Go 的时候,很多人会觉得错误处理很重复:每层都要判断 err != nil,看起来很机械。可真到了线上排查问题时,最怕的不是判断多,而是最后拿到的错误只有一句 “open failed” 或 “query error”,根本不知道是哪一步出的事。