news 2026/6/14 3:55:02

[译]我们为了继续使用 Golang 而对自己说的谎言

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
[译]我们为了继续使用 Golang 而对自己说的谎言

本文是对 Lies we tell ourselves to keep using Golang的整理与翻译。观点仅代表原作者

内容结构概览

  1. 开篇:Go 的吸引力与长期成本
  2. 第一层自我安慰:质疑批评者,而不是讨论问题
  3. 第二层自我安慰:大厂在用,所以我们也能用
  4. Go 确实有优点:运行时、goroutine、GC、工具链
  5. 真正的代价:语言设计不足会转移到业务代码里
  6. Go 是一座孤岛:工具链、FFI、cgo、跨语言集成问题
  7. 零值哲学的两面性:slice、map、channel、结构体默认值
  8. “小心点就行”为什么不够:人工审查和 linter 无法替代类型系统
  9. 这不是 Rust 神教文章:Rust 也有问题,但它提供了另一种增量采用路径
  10. Go 适合原型吗:没有真正的“临时代码”,重写往往不会发生
  11. 结尾:我们应该如何更诚实地评估技术选型

我们为了继续使用 Go,对自己说过哪些谎?

很多程序员第一次接触 Go,都会有一种轻松感。

语法简单,编译快,部署方便,标准库够用,go testgo fmtgo build开箱即用。写一个 HTTP 服务,几行代码就能跑起来;起 goroutine 也不需要先学习一整套复杂的异步模型;交叉编译更是让不少被 C/C++ 构建系统折磨过的人眼前一亮。

所以 Go 很容易让人喜欢。

但问题是:一门语言容易上手,不等于它适合长期维护复杂系统。一个工具在早期给你的速度,可能会在几年后变成组织里的隐性债务。很多 Go 项目不是一开始就痛苦,而是随着系统变大、团队变大、边界变多、线上事故变多,才慢慢发现那些“简单设计”背后其实藏着大量人工约定。

原文《Lies we tell ourselves to keep using Golang》讨论的正是这个问题。

它不是简单地说“Go 不好”,也不是把 Rust 拿出来当万能解药。更准确地说,它在拆解一种技术选型中的自我辩护机制:当我们已经投入大量 Go 代码、团队习惯、招聘体系和基础设施之后,我们会很自然地为继续使用它寻找理由。

这些理由里,有些是合理的,有些则只是让我们暂时不去面对问题。

这篇文章就按原文思路,把这些“我们对自己说过的话”重新梳理一遍。


一、当我们不想听批评时,最容易先质疑批评者

很多技术争论不是从问题本身开始的,而是从质疑发言者开始的。

有人指出 Go 的问题,常见回应往往不是:

“这个具体问题是否存在?”

而是:

“你是不是不懂语言设计?”

“你是不是只会 Rust?”

“你是不是带着偏见?”

“你是不是拿 Go 去做了不适合它的事情?”

这种回应很省力。只要把批评者归类为“外行”“喷子”“语言洁癖”“Rust 传教士”,就不需要继续面对他提出的问题了。

但这其实是一种逃避。

一个问题是否真实存在,不取决于指出它的人是谁。初级工程师也可能看出一个系统设计里的明显缺陷;资深工程师反而可能因为长期适应了某种痛苦,把痛苦当成常态。

在很多团队里,越是老员工,越习惯对各种不合理设计说“这个一直都是这样”。新人反而会问:

“为什么这里要手动判空?”

“为什么这个字段没有初始化也能编译?”

“为什么这个函数会不会修改参数,从调用处看不出来?”

“为什么这个接口接收interface{},但实际只允许三种类型?”

这些问题听起来幼稚,但它们非常重要。因为它们说明系统的约束没有被语言和工具表达出来,而是被默默转移到了人的脑子里。

成熟的工程文化不应该因为提问者不够资深,就忽略问题本身。


二、“大公司也在用 Go”,并不能证明它适合你

技术选型时,很多人喜欢看大公司案例。

某某公司使用 Go 支撑了海量流量。

某某基础设施项目是 Go 写的。

某某云原生组件全家桶都是 Go。

这些信息当然有价值。它们可以帮助我们发现值得评估的技术。但它们不能直接替代评估。

公司技术博客通常不会完整展示真实成本。它会展示成功经验、架构图、性能优化、业务收益,却很少说:

“我们当初选错了。”

“这个语言给我们制造了很多额外复杂度。”

“我们花了很多人力绕过工具链和类型系统限制。”

“我们现在已经被历史包袱绑住了。”

因为公司博客还有招聘、品牌、技术影响力等目标,它天然更愿意讲一个成功故事。

这就导致一个误区:看到优秀团队使用 Go,我们以为 Go 本身解决了问题;但也可能是优秀团队用大量经验、人力和内部工具弥补了 Go 的不足。

原文提到 Tailscale 的例子很典型。

Tailscale 确实大量使用 Go,而且团队里有非常强的 Go 专家。他们能深入 Go 运行时、链接器、网络栈和底层细节,遇到坑时有能力自己修、自己绕、甚至推动上游改进。

但问题是:你的团队也是这样吗?

你是否也有足够多理解 Go 底层实现的人?

你是否能承担为了一个语言限制而长期维护复杂 workaround 的成本?

你是否像 Google 一样,有能力在 Go 之上构建一整套额外工程体系?

大公司能用,不等于你能用。

专家团队能承受,不等于普通团队也能承受。

更关键的是,有些复杂度并不是业务本身带来的,而是语言选择带来的。比如 Go 缺少 sum type,导致表达“IPv4 或 IPv6”这类概念并不自然;缺少不可变数据支持,导致防止修改只能靠复制和约定;缺少 newtype 一类机制,导致类型隔离很别扭;不支持运算符重载,导致一些本该自然表达的值对象操作变得啰嗦。

这些并不是“高深学术问题”,它们会实实在在地影响业务建模。


三、承认 Go 的优点:它确实很会让人上手

批评 Go,并不意味着否认 Go 的优点。

Go 的优点非常明确,而且很多都是工程上真正有价值的优点。

第一,Go 的并发模型很容易使用。

goroutinechannel让很多网络服务开发者第一次感受到:“原来并发程序也可以写得这么直接。”不需要显式管理线程池,不需要一开始就被复杂的 async/await 生态吓住。

第二,Go 的运行时做了大量事情。

调度、网络 I/O、GC、goroutine 栈、阻塞检测、profiling,这些都和语言体验绑定得很紧。你可以很方便地 dump goroutine 栈,可以用 pprof 分析程序,可以在很多情况下不用过早关心“函数颜色”问题。

第三,Go 的工具链非常统一。

gofmt消灭了大量代码风格争论。

go test降低了测试门槛。

go build和交叉编译让部署体验比很多传统语言舒服得多。

go mod虽然也有争议,但相比许多历史包袱沉重的构建系统,它确实简单直接。

第四,Go 的标准库覆盖了很多后端开发常见需求。

HTTP、TLS、JSON、profiling、testing、context、template、net、sync 等,基本能让你不依赖太多第三方库就写出一个可运行服务。

这些优点解释了为什么很多人一开始会被 Go 吸引。

问题在于:一个非常方便的运行时,并不自动等于一门适合复杂系统建模的语言。


四、一个好用的运行时,不能抵消语言设计的所有问题

Go 最大的吸引力之一,是它的运行时。

但当你选择 Go 时,你不只是选择了 goroutine 和 GC。你还选择了:

  • Go 自己的工具链
  • Go 自己的构建系统
  • Go 自己的链接模型
  • Go 自己的调用约定
  • Go 自己的 GC 行为
  • Go 标准库的设计取舍
  • Go 语言本身的类型系统和表达能力

这是一整套平台,不是一个可以随便拆出来的“并发库”。

原文里有一个很尖锐的判断:Go 团队真正喜欢的是异步运行时和网络服务开发体验,语言本身像是围绕这个目标“长出来”的,而不是从严肃的类型建模、语义表达和长期维护角度精心设计出来的。

这句话可能有争议,但它指出了一个真实感受:Go 在很多地方更像是“够用就行”。

比如错误处理。

Go 的错误处理很显式,这是优点。但它没有提供足够强的类型机制来表达错误的结构、上下文和可恢复性,最后大量业务代码都变成:

result,err:=doSomething()iferr!=nil{returnerr}

显式是显式了,但并不代表信息更丰富,也不代表调用方更安全。

再比如可变性。

在 Go 里,你很难从调用处直接判断一个方法是否会修改接收者:

typeUserstruct{Namestring}func(u User)Rename(namestring){u.Name=name}func(u*User)RenameInPlace(namestring){u.Name=name}

调用时看起来都像:

u.Rename("alice")u.RenameInPlace("bob")

真正决定是否修改原对象的,是方法签名里的值接收者还是指针接收者。调用方如果不跳到定义处,很难仅凭调用点判断副作用。

在小程序里这不算问题。

在大型代码库里,这会逐渐变成认知成本。

函数是否会修改输入?

是否会持有某个指针?

是否会启动 goroutine?

是否会把对象保存到全局结构里?

是否允许传入 nil?

这些信息如果不能被类型签名清楚表达,就只能依赖文档、注释、代码评审和团队习惯。

而文档会过期,注释会过期,评审会疲劳,团队成员会流动。


五、Go 是一座孤岛:舒适,也封闭

原文有一节叫 “Go is an island”。

这个比喻很准确。

Go 的封闭性带来了一些好处。比如 Go 可以深度控制自己的运行时、调度器、网络栈和 profiling 体验。你可以用同一套工具观察业务代码和标准库内部行为,这在很多语言里并不容易。

但代价也很明显:Go 和外部世界的互操作并不舒服。

如果你不用 cgo,你基本生活在 Go 自己的世界里。

Go 有自己的汇编风格、自己的链接器、自己的调用约定、自己的运行时。很多其他语言生态里成熟的调试器、内存检查工具、FFI 经验,并不能直接平滑迁移到 Go。

如果你要从 Go 调 C,通常会碰到 cgo。

如果你要从其他语言调用 Go,也会把 Go runtime 一起带进去。

这不是单纯的技术细节,而是架构层面的影响。

很多团队最后会发现:和 Go 集成最舒服的边界,是网络边界。

也就是说,不要直接把 Go 嵌进别的语言进程里,而是让 Go 服务通过 HTTP、gRPC、JSON-RPC 或其他 RPC 协议对外提供能力。

这当然可行,也是微服务架构中很常见的做法。

但网络边界不是免费的。

你要维护协议。

你要维护序列化和反序列化。

你要处理版本兼容。

你要处理超时、重试、幂等、鉴权、观测性。

你还要确保边界两侧的数据约束是一致的。

如果语言本身不能很好表达不变量,这些不变量就会被迫散落在校验逻辑、文档、单元测试、接口约定和线上监控里。

这就是 Go 的一个核心问题:它把很多复杂度从语言层面移走了,但复杂度并没有消失,只是换了地方。


六、零值:Go 最迷人的设计之一,也是最危险的设计之一

Go 很推崇零值。

零值的好处是显而易见的:变量声明后马上可用,很多类型不需要显式构造。

比如:

varcountintvarnamestringvaritems[]int

count是 0,name是空字符串,items是 nil slice。很多情况下这很方便。

但方便的背后,是一个问题:零值到底是不是一个合法业务状态?

有时候是。

比如 nil slice:

varitems[]intfmt.Println(len(items))// 0

这很自然。nil slice 可以被当成空集合看待,len返回 0,很多操作也能正常工作。

但 nil map 就不一样:

varmmap[string]intm["age"]=18// panic

你必须先初始化:

m:=make(map[string]int)m["age"]=18

再看结构体字段默认值:

typeParamsstruct{AintBint}funcWork(p Params){fmt.Println(p.A,p.B)}funcmain(){Work(Params{A:1})}

这段代码可以编译,B会默认为 0。

问题是:这个 0 是调用者故意传的,还是忘了传?

编译器不知道。

当你给Params新增字段时,很多调用点不会报错。它们会静默使用零值。这个行为在小代码里很方便,在大代码库里却可能制造非常隐蔽的 bug。

如果B表示重试次数,0 也许合理。

如果B表示超时时间,0 可能意味着立刻超时,也可能意味着永不超时。

如果B表示价格、余额、权限级别、版本号、租户 ID,它到底是不是合法值,就必须靠业务逻辑判断。

Go 的零值哲学,把这个判断交给了程序员。


七、channel 的零值和关闭语义,也不是免费午餐

Go 的 channel 是并发模型的核心之一,但 channel 的一些语义非常容易踩坑。

常见规则包括:

向 nil channel 发送,会永久阻塞 从 nil channel 接收,会永久阻塞 向已经关闭的 channel 发送,会 panic 从已经关闭的 channel 接收,会立即返回零值

这些规则都能解释,也都有设计上的理由。

但从使用者角度看,它们意味着:你必须非常清楚一个 channel 当前处于什么状态。

它是不是 nil?

它是不是已经关闭?

谁负责关闭?

会不会重复关闭?

有没有 goroutine 永远阻塞在发送或接收上?

关闭后读到的零值,是真的业务值,还是通道关闭导致的默认值?

Go 提供了一些机制帮助你发现问题,比如 pprof、goroutine dump、deadlock 检测等。但这些更多是问题发生后帮助你定位,而不是在编译期阻止你写出有问题的程序。

这就是原文反复强调的一点:

Go 的回答经常是“你小心点”。

但“你小心点”不是工程体系。


八、“每个问题单独看都不大”,不代表合起来也不大

Go 争论中很常见的一种回应是:

“这个确实要注意,但也没那么严重。”

“nil 要检查,这不是常识吗?”

“map 要初始化,这不是 Go 基础吗?”

“channel 要规范使用,这不是代码评审该管的吗?”

“结构体字段新增后要检查调用点,这不是测试该覆盖的吗?”

单独看,每句话好像都对。

但工程问题的难点不在于单个坑,而在于所有坑会叠加。

一个语言如果在很多地方都要求人“记得小心”,那么最后系统安全性就依赖于:

  • 每个开发者都理解这些细节
  • 每次代码评审都能发现这些问题
  • 每个测试都覆盖到边界情况
  • 每份文档都及时更新
  • 每次重构都不会漏掉隐含约束
  • 每次线上变更都没有侥幸路径

这不现实。

人类不擅长长期维护大量隐式不变量。我们会累,会忘,会换项目,会交接不完整,会在凌晨三点处理事故时做出错误判断。

优秀的语言和工具不可能消灭所有 bug,但可以消灭一部分本不该存在的 bug。

“不能解决所有问题,所以不值得解决任何问题”,是一种错误逻辑。

安全带不能阻止所有车祸,但这不是不系安全带的理由。

类型系统不能证明所有业务逻辑正确,但这不是放弃类型表达的理由。

编译器不能替你设计系统,但它可以帮你拒绝明显错误的状态。


九、这不是“Rust 完美,Go 垃圾”的故事

原文很明确地反对一种稻草人式解读:

“你批评 Go,是不是因为你觉得 Rust 完美?”

不是。

Rust 有自己的问题。

学习曲线陡,编译时间长,生命周期和借用检查会让新人痛苦,异步生态也有复杂性,unsafe Rust 写错了照样危险。Rust 的类型系统强大,但也意味着你需要在前期投入更多建模成本。

所以问题不是“Go 和 Rust 谁是神”。

问题是:当我们面对复杂系统时,是否愿意承认一些约束应该由工具帮我们维护,而不是全部压给人?

Rust 的成功,很大程度上来自它可以增量采用。

很多真实项目并不是一夜之间用 Rust 重写全部系统,而是在 C/C++、Python、Firefox、Android、Linux kernel 等生态里,把某些安全性要求更高、边界更清晰的组件逐步迁移到 Rust。

这和 Go 的成功路径不同。

Go 的成功很大程度来自“开箱即用”和“默认选择足够好”。它让你快速写出服务。

Rust 的成功更多来自“可以和现有系统逐步结合”,并在内存安全、类型表达、可变性控制等方面提供更强工具。

这两者都是成功故事,但不是同一种成功。


十、不要制造虚假的二选一

很多争论会被简化成二选一:

要么追求开发速度,要么追求正确性。

要么快速上线,要么过度设计。

要么用 Go 写业务,要么用 Rust 把所有细节建模到极致。

这是假二分。

现实里你可以做中间选择。

你可以在 Rust 里选择不建模所有细节,把不关心的错误归到OtherUnknown

你也可以在 Go 里非常谨慎地做类型封装、构造函数、校验器、lint 规则、代码生成、review checklist。

区别在于成本。

在 Go 里,你可以写出相对安全的代码,但语言本身给你的帮助有限。你要靠纪律、封装、测试、规范、review 和经验。

在 Rust 里,你也可以写得很粗糙,但语言会强迫你面对一些 Go 可以暂时忽略的问题,比如空值、所有权、可变借用、错误类型、生命周期等。

这不是说 Rust 总是更好,而是说不同语言把复杂度放在了不同位置。

Go 倾向于让代码早期看起来简单,把很多约束推迟到运行期、测试期、评审期和线上期。

Rust 倾向于让你在编码阶段就处理更多约束,因此早期更慢,但后期能减少一部分不确定性。

技术选型要诚实地评估这种成本转移。


十一、Go 适合做原型吗?

很多人会退一步说:

“好吧,也许 Go 不适合特别复杂的生产系统。但它很适合做原型。先用 Go 快速写出来,以后真复杂了再重写。”

这句话听起来很合理,但原文指出了一个残酷事实:

几乎不存在真正的临时代码。

大多数团队都非常抗拒重写,而且有充分理由。

重写很贵。

重写期间不能快速交付新功能。

迁移过程容易丢细节。

新旧系统要长时间并存。

线上流量切换有风险。

团队要重新学习新技术。

业务方也不愿意为“技术债偿还”长期等待。

所以很多所谓原型,最后都会变成生产系统。

一旦第一批服务用 Go 写了,后续服务继续用 Go 的理由会越来越多:

“团队已经会 Go 了。”

“已有代码都是 Go。”

“已有脚手架都是 Go。”

“已有库都是 Go。”

“已有部署模板都是 Go。”

“别的语言和 Go 集成太麻烦。”

这时你继续写 Go,可能不是因为它仍然是最佳选择,而是因为退出成本越来越高。

这就是技术路径依赖。


十二、Go 的“简单”,可能只是把复杂度藏起来了

Go 的拥护者常说:Go 很简单。

这句话要分两层理解。

Go 的语法确实简单。

但语法简单,不代表系统简单。

当语言缺少表达能力时,复杂度不会消失,而是进入业务代码。

比如,没有 sum type,你就需要用字符串常量、枚举模拟、接口模拟、结构体加 tag 模拟。

比如,没有不可变数据,你就需要靠约定和复制避免意外修改。

比如,零值可能合法也可能非法,你就需要靠构造函数和校验函数维持不变量。

比如,函数签名不能表达副作用,你就需要靠命名、注释和文档提示调用方。

比如,interface{}太宽,你就需要在运行时做类型断言和错误处理。

于是,代码库里会出现大量样板逻辑:

ifvalue==""{returnerrors.New("missing value")}ifp.Timeout==0{p.Timeout=defaultTimeout}ifreq.UserID==0{returnerrors.New("invalid user id")}switchv:=x.(type){casestring:// ...caseint:// ...default:returnerrors.New("unsupported type")}

这些代码不是业务核心,却充满代码库。

它们本质上是在手动维护类型系统没帮你表达的东西。

长期看,所谓“简单语言”可能会制造“复杂项目”。


十三、为什么 Go 不一定适合初学者?

Go 经常被推荐给初学者,因为语法少、工具简单、上手快。

但原文提出了一个反直觉观点:Go 不一定适合初学者,因为它允许太多明显有问题的代码通过编译。

对初学者来说,最难的不是少写几个关键字,而是建立正确的程序思维。

什么状态是合法的?

什么状态是不可能出现的?

哪些值可以为空?

哪些对象可以被修改?

谁拥有资源?

谁负责关闭?

错误是否被处理?

并发是否可能泄漏?

这些问题如果语言不提醒,初学者就只能靠踩坑学习。

Go 的编译器经常保持沉默。它相信程序员会小心处理。

但初学者恰恰最不知道哪里需要小心。

这会导致一种错觉:代码能编译,服务能跑,就以为系统没问题。直到线上出现 nil、零值、竞态、goroutine 泄漏、map panic、channel 阻塞、错误吞掉,才发现“简单”并不等于“安全”。


十四、linter 和代码评审能解决吗?

能解决一部分,但不能解决根本问题。

linter 很有用。

代码评审也很有用。

测试更有用。

但它们不能完全替代语言本身的表达能力。

如果某个约束可以被类型系统表达,就应该尽量让编译器检查。因为编译器不会疲劳,不会漏看,不会因为赶需求而降低标准,也不会因为团队新人不了解历史背景而放过错误。

而 linter 和 review 往往是补救机制。

它们是在语言允许你写出危险代码之后,再试图把其中一部分拦下来。

这就像门本来没有锁,然后你安排几个人每天站在门口提醒大家别乱进。提醒当然有用,但不如门锁本身可靠。

Go 的问题不是“完全不能写好代码”。

Go 当然能写出好代码,很多优秀项目就是 Go 写的。

问题是:写出长期可靠的大型 Go 系统,需要大量额外纪律和经验,而这些成本经常在技术选型时被低估。


十五、我们为了继续使用 Go,常说哪些话?

原文最后总结了一组“我们为了继续使用 Go,对自己说的谎”。

换成中文语境,大概可以整理成下面这些:

第一,别人都在用,所以它也适合我们。

但别人有别人的团队能力、历史包袱、业务场景和专家储备。大厂案例只能作为评估输入,不能作为决策结论。

第二,批评 Go 的人都是精英主义者或语言洁癖。

但问题是否存在,和提出问题的人是否讨人喜欢无关。把批评者标签化,只会阻止我们讨论事实。

第三,Go 的 runtime、GC 和工具链足够好,所以语言层面的缺陷可以忽略。

Go 的运行时确实优秀,工具链也确实舒服。但你采用的是整个平台,不只是 goroutine。

第四,每个语言设计缺陷单独看都不严重,所以合起来也不严重。

这不成立。工程事故往往不是一个大问题造成的,而是很多“小心点就行”的地方叠加造成的。

第五,只要团队小心一点,加 linter,多 review,就能克服这些问题。

这能缓解,但不能根治。长期把系统正确性寄托在人的持续注意力上,本身就是风险。

第六,Go 写起来快,所以开发生产系统也快。

写代码只是软件工程的一小部分。真正耗时的是理解、维护、排障、重构、扩展和协作。

第七,语言简单,所以系统也简单。

语言少了表达能力,复杂度就会转移到业务代码和团队流程里。

第八,可以先少量使用,以后再迁移。

技术一旦进入核心系统,就会形成路径依赖。越往后,迁移越难。

第九,以后可以重写。

绝大多数“以后重写”,最后都不会发生。即使发生,也会非常昂贵。


十六、那我们到底该怎么评价 Go?

更现实的态度不是“用”或“不用”,而是问清楚几个问题。

第一,你的项目复杂度在哪里?

如果是简单 CLI、内部工具、小型服务、脚本替代品、基础设施 glue code,Go 可能非常合适。

如果是复杂业务领域建模、高安全性系统、跨语言嵌入、强约束协议、长期演进的大型核心系统,就要更谨慎。

第二,你的团队是否真的理解 Go 的成本?

不是会写http.HandleFunc就叫理解 Go。

真正的成本包括 nil、zero value、interface、反射、逃逸分析、GC 行为、goroutine 生命周期、channel 关闭、context 传播、错误处理、cgo、模块管理、代码生成、lint 体系、测试策略等。

第三,你是否有能力建立工程约束?

比如:

  • 强制构造函数
  • 避免裸interface{}
  • 明确 nil 语义
  • 对结构体新增字段建立检查机制
  • 对 goroutine 生命周期做统一管理
  • 对 channel 所有权和关闭责任做规范
  • 用静态分析补充编译器不足
  • 对错误处理建立统一模式
  • 用契约测试保护服务边界

如果没有这些,Go 的“简单”会很快变成线上复杂度。

第四,你是否能接受未来退出成本?

一旦项目大量使用 Go,并且内部库、脚手架、部署、监控、招聘都围绕 Go 建立,未来切换语言会越来越难。

这不是 Go 独有的问题,所有技术栈都有路径依赖。但 Go 的“孤岛”特征会让跨语言集成和渐进迁移更痛。


十七、结语:不要因为一开始舒服,就忽略长期代价

Go 最大的魅力,是它让人一开始很舒服。

这也是它最危险的地方。

很多语言会在一开始就暴露复杂度,让你觉得难。Go 则倾向于把复杂度推迟,让你先跑起来,再在系统变大后慢慢付账。

这不是说 Go 不能用。

Go 可以用,而且在很多场景里非常好用。

但我们不应该用一些自我安慰的话掩盖它的成本:

“大家都在用。”

“Go 很简单。”

“我们小心点就行。”

“以后可以重写。”

“加点 linter 就好了。”

“这个坑大家都知道。”

真正成熟的工程判断,不是选择某个流行技术,而是诚实面对它会把复杂度放在哪里。

有些复杂度放在编译器里。

有些复杂度放在类型系统里。

有些复杂度放在运行时里。

有些复杂度放在代码评审里。

有些复杂度放在凌晨三点的值班电话里。

选择 Go,并不是选择没有复杂度。

只是选择让很多复杂度以后再出现。

而工程里最贵的,往往正是那些一开始被推迟的问题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/14 3:51:02

Flowable UI 6.6.0 保姆级安装教程:从Tomcat部署到第一个流程启动

Flowable UI 6.6.0 零基础实战指南:从环境搭建到流程自动化在数字化转型浪潮中,业务流程自动化已成为企业提升效率的关键。作为轻量级开源BPM引擎,Flowable以其简洁的架构和丰富的功能组件,成为技术团队快速实现工作流管理的首选方…

作者头像 李华
网站建设 2026/6/14 3:49:59

7-Zip ZS版(开源免费解压缩软件)

链接:https://pan.quark.cn/s/17e9dc47d1757-Zip ZS版是一个基于著名开源压缩软件 7-Zip 的修改版,它由第三方开发者维护,并引入了额外的压缩算法和其他改进。软件本身是一款开源、免费的文件压缩和解压缩工具,而 ZS版则在此基础上…

作者头像 李华
网站建设 2026/6/14 3:45:00

告别官方U盘依赖?聊聊KUKA机器人系统备份的‘平替’方案与安全风险

告别官方U盘依赖?KUKA机器人系统备份的替代方案与安全实践在工业自动化领域,KUKA机器人系统的稳定运行直接关系到生产线效率。当系统出现故障时,快速恢复的关键在于是否拥有可靠的备份。传统做法依赖官方KUKA.RecoveryUSB,但动辄数…

作者头像 李华
网站建设 2026/6/14 3:38:53

别再搞混了!SAP重复制造与离散制造的核心区别,看完这篇就懂了

SAP重复制造与离散制造:核心差异与实战避坑指南在制造业数字化转型浪潮中,SAP系统作为行业标杆解决方案,其制造模块的复杂性常常让从业者感到困惑。特别是当项目涉及不同制造类型的选择时,很多顾问会陷入"该用重复制造还是离…

作者头像 李华
网站建设 2026/6/14 3:38:53

3毛钱的国产RS485芯片,真能省掉TVS和偏置电阻?CS48505S实测拆解

国产RS485芯片CS48505S实测:3毛钱能否颠覆传统设计? 在工业自动化和物联网设备设计中,RS485总线因其抗干扰能力和长距离传输特性而广受欢迎。但传统RS485电路设计中,工程师们不得不面对一个现实问题:外围电路复杂且成本…

作者头像 李华