2025年八月月 发布的文章

Go的“七宗罪”:一篇“Go依然不够好”如何引爆社区激辩?

本文永久链接 – https://tonybai.com/2025/08/25/go-is-still-not-good

大家好,我是Tony Bai。

在技术圈,平静的湖面下往往暗流涌动。对于Go语言社区而言,这股潜藏已久的暗流,被近期的一篇名为《Go is still not good》的博文彻底引爆。作者Thomas Habets,一位自称拥有超过十年Go使用经验的资深开发者,在他的这篇文章中系统性地列举了他眼中Go语言的“七宗罪”。这篇文章迅速登上Hacker News热榜,吸引了超过700条评论,形成了一场规模空前的社区大辩论。

参与者中不乏Go的早期采纳者、贡献者和日常重度使用者。他们争论的焦点,早已超越了语法糖的优劣,直指Go语言最核心的设计哲学——那些曾被誉为“简单”和“务实”的基石,如今在一些开发者眼中,却成了束缚发展、埋下隐患的“原罪”。

在这篇文章中,我就和大家一起跟随这场激辩,逐一剖析这引发轩然大波的“七宗罪”,看看从中能得到哪些有益的启示。

第一宗罪:歧义之空——nil 的双重身份

这是Go语言中最著名的“陷阱”,也是原文作者打响的第一枪。一个持有nil指针的接口变量,其自身并不等于nil。

package main

import "fmt"

type Error interface {
    Error() string
}

type MyError struct{}

func (e *MyError) Error() string { return "my error" }

func GetError() *MyError {
    // 假设在某种条件下,我们返回一个 nil 的具体错误类型指针
    return nil
}

func main() {
    var err Error = GetError()

    // 输出: false
    // 尽管接口 err 内部持有的值是 nil,但接口本身因为包含了类型信息 (*MyError),所以它不为 nil。
    fmt.Println(err == nil) 

    if err != nil {
        // 这段代码会被执行,然后可能在后续操作中引发 panic
        fmt.Printf("An error occurred: %v (type: %T)\n", err, err)
        // err.Error() // 若MyError的Error方法有解引用操作,此处会panic
    }
}

我们知道:Go的接口(interface)在内部实现为一个包含两部分的“胖指针”(fat pointer):一个指向类型元数据的指针和一个指向实际数据的指针。只有当这两个指针都为nil时,接口变量本身才被认为是nil。在上述例子中,err的内部状态是(type=*MyError, value=nil)。因为类型信息存在,err != nil的判断为真,导致程序逻辑错误地进入了错误处理分支,挑战了开发者的常规直觉。

社区激辩

  • 批评者阵营:Hacker News上,有用户提供了一个经典的Playground示例,展示了这个问题如何在生产环境中导致panic,并评论道:“这确实会在生产中咬你一口,而且在代码审查中极易被忽略。”另一位用户则更为尖锐,他引用了Rob Pike关于Go是为“非研究型、刚毕业的年轻工程师”设计的言论,反问道:“一个声称为了简化编程而设计的语言,却包含如此令人困惑的nil行为,这本身就是一种讽刺。”

  • 辩护者阵营:另一派观点认为,这并非缺陷,而是Go底层数据结构逻辑的直接体现。有开发者解释道:“接口值是一个包含类型和值的偶对。(&Cat, nil)当然不等于(nil, nil)。”他们认为,一旦理解了接口的内存模型,这个问题便不再神秘,甚至可以利用这一特性(例如,在nil接收者上调用方法)。然而,这种辩护本身就强化了批评者的观点:一门标榜高级和简单的语言,却要求开发者对底层的实现细节有如此深刻的理解,这是否可以看作设计上的一种失败呢?

第二宗罪:作用域之惑——被迫扩展的err变量生命周期

Go通过if err := foo(); err != nil语法,优雅地将err变量的作用域限制在if块内,这被广泛认为是最佳实践。然而,当函数调用需要返回除error之外的值时,这种优雅便荡然无存。

bar, err := foo()
if err != nil {
    return err
}
// 此处的err变量将在整个函数剩余部分都有效,即使它现在的值是nil

if err = foo2(); err != nil { // 复用err
    return err
}

// ... 大量代码 ...

return err

Go的短变量声明:=要求左侧至少有一个新变量。为了接收bar这个新值,err也被迫在函数作用域内被重新声明(或首次声明)。这导致err的生命周期被人为地拉长,污染了整个函数的作用域。

社区激辩

  • 批评者阵营:原文作者尖锐地指出,这种设计“强迫你做错误的事情”。一个本应是局部的错误变量,现在却像个幽灵一样在整个函数中游荡,增加了代码阅读者的认知负担。读者必须时刻追踪err变量最后一次被赋值的位置,这极易导致bug,尤其是在重构或修改长函数时。
  • 辩护者阵营:对此的辩护声音较弱,大多认为这是个“可以忍受的小麻烦”。他们认为,这是为了保持语法一致性(:=的规则)而付出的代价。然而,这恰恰暴露了Go在追求一种形式上的“简单”时,牺牲了更重要的“上下文清晰性”。

第三宗罪:所有权之乱——append的隐式副作用

slice是Go的基石之一,但其与底层数组(backing array)的模糊关系,通过append函数暴露无遗,构成了另一个经典的“搬起石头砸自己的脚”。

原文的例子一针见血地揭示了append行为的不可预测性:

package main

import "fmt"

func main() {
    // 案例一:当容量足够时,发生“幽灵写入”
    a := []string{"hello", "world", "!"}
    b := a[:1]                 // b与a共享底层数组,且cap(b) == 3
    b = append(b, "NIGHTMARE") // 修改了b,因为容量足够,直接修改了底层数组
    fmt.Println(a)// 结果:a变成了[hello NIGHTMARE !]

    // 案例二:当容量不足时,修改“失败”
    a = []string{"hello", "world", "!"}
    b = a[:1]
    b = append(b, "BACON", "THIS", "SHOULD", "WORK") // 容量不足,分配了新数组
    fmt.Println(a)// 结果:a依然是[hello world !]
}

我们知道:append的行为取决于slice的容量(cap)。如果追加后未超出容量,它会就地修改底层数组;否则,会分配一个新的、更大的数组。这种设计不仅让append的性能变得不确定,更严重的是,它破坏了函数调用的封装性,使得slice既不像值类型(可能被远程修改),也不像纯粹的引用类型(可能因重分配而断开联系)。

社区激辩

  • 批评者阵营:Hacker News上一位获得高赞的评论是这样的:“append的例子是Go缺陷中最恶劣、最不可原谅的。”这种行为使得数据流变得难以追踪,迫使开发者必须时刻警惕slice的容量,或养成防御性编程的习惯,例如总是重新接收append的返回值。这与Go追求的“明确”背道而驰。
  • 辩护者阵营:支持者认为这是为了性能做出的合理权衡,避免了不必要的内存分配。他们强调,Go官方文档已明确说明了slice的工作原理。然而,这再次回到了那个核心问题:一门标榜“简单”的语言,是否应该包含如此微妙且需要深度理解才能安全使用的核心数据结构?

第四宗罪:作用域陷阱——函数级的defer

defer是Go处理资源释放的利器,但它的作用域是整个函数,而非其所在的词法块(lexical scope)。这在循环中处理资源时会成为一个严重的资源泄漏问题。

for _, file := range files {
    f, err := os.Open(file)
    if err != nil { /* ... */ continue }
    // defer不会在每次循环结束时执行,而是堆积到函数返回时执行
    // 如果文件列表很长,将耗尽文件句柄
    defer f.Close()
    // ... process file
}

根本原因在于defer语句的执行被推入一个与当前函数关联的栈中,在函数返回前统一执行。这简化了编译器的实现,并确保了panic时资源也能被释放。

社区激辩

  • 批评者阵营:一个开发者的高赞评论代表了社区的普遍困惑:“我至今不明白defer为什么是函数作用域而非词法作用域。”这与C++的RAII或Java的try-with-resources相比,是一种设计上的倒退。公认的解决方法是使用匿名函数func(){…}()包裹循环体,但这无疑增加了代码的丑陋和复杂性。
  • 辩护者阵营:有用户指出,函数级作用域也有其便利之处,例如可以在if块中有条件地注册一个defer。但总体而言,社区普遍认为,默认应该是更安全、更符合直觉的词法作用域。

第五宗罪:异常之隐——被标准库“吞噬”的panic

Go的哲学是:error用于可预见的错误,panic用于程序无法继续的灾难。然而,作者指出,标准库中的fmt.Print和net/http服务器等关键部分,会主动recover从panic中恢复,这破坏了panic的基本约定。

这意味着开发者必须编写“异常安全”的代码。你必须假设任何传递给标准库的代码都可能在panic后被恢复。因此,像互斥锁(mutex)这样的资源必须通过defer来确保释放,否则一旦发生被“吞噬”的panic,就会造成死锁。作者愤怒地指出:“所有希望都破灭了。你必须写异常安全的代码,但你又不应该使用异常。你只能承受异常带来的所有负面影响。”

社区激辩:这一点在社区中几乎没有辩护的声音。这被视为一种设计上的不一致和“伪善”。语言在表层倡导一种错误处理哲学,却在底层库中悄悄破坏它,迫使开发者为这种矛盾买单。

第六宗罪:编码之殇——对非UTF-8的“绥靖政策”

Go的string类型本质是只读的[]byte,不强制其为合法的UTF-8。这在与操作系统交互(如处理文件名)时提供了灵活性,但也埋下了隐患。

作者控诉,这种“宽松”策略是数据丢失的根源。当工具不假思索地按UTF-8处理文件名时,遇到非UTF-8编码的文件名可能会跳过或处理失败,导致在备份、恢复等关键操作中“静默地”遗漏数据。

社区激辩

  • 批评者阵营:他们认为类型系统应防止此类错误。有用户激烈地评论道:“Go让你很容易做那些看起来99.7%的时间都有效,但却是愚蠢、错误、不正确的事情……然后有一天,你的用户就因为一个非UTF-8文件名而永久丢失了数据。”
  • 辩护者阵营:另一方则认为Go的做法才是务实的。有用户指出,一个强制Unicode正确性的文件接口在真实世界中是有问题的。Rust的OsStr虽然严谨,但人体工程学极差。Go的方式虽然“混乱”,但在实践中更方便。这揭示了严谨性与便利性之间的深刻矛盾。

第七宗罪:承诺之虚——伪善的“简单”与被忽视的性能

这并非单一技术点,而是对Go整体设计理念的综合批判。

  • 简单性的代价是复杂性转移:许多评论者指出,Go语言层面的“简单”,是把复杂性推给开发者来承担。没有枚举、没有强大的泛型(即使1.18加入了,也限制颇多)、没有Result类型,导致开发者需要手写大量重复的样板代码和自定义数据结构。
  • 内存管理的“信任危机”:原文作者提到“RAM is cheap”是危险的思维。Hacker News上有开发者分享了其在内存敏感项目中被Go的非压缩GC和堆碎片化问题折磨的经历,他们甚至不得不重写部分标准库以避免内存分配。这与Go宣称的“高性能”和“无忧GC”形成了鲜明对比。

为何着一篇文章能掀起千层浪?

这场激辩之所以如此激烈,是因为它触及了Go社区内部长期存在的深层张力:

  1. “Google的Go” vs “世界的Go”:Go的许多设计源于解决Google内部特定问题的需求(C++编译慢、monorepo文化)。这种“出身”决定了它在某些方面与更广阔的编程世界存在脱节。早年对单调时钟的忽视就是典型例子。
  2. 简单主义 vs 现代语言特性:Go的创造者们带着一种“回归本源”的复古主义情怀,刻意回避了过去几十年编程语言理论的发展成果,如高级类型系统、代数数据类型等。这使得Go易于上手,但也让它在处理复杂逻辑时显得捉襟见肘,迫使开发者“用代码的冗余换取语言的简单”。
  3. 显式 vs 便利:if err != nil是显式的,但它不便利。Result类型和?操作符是便利的,但它在某种程度上是隐式的。Go坚定地站在了“显式”这一边,但社区中渴望“便利”的声音从未停止。

小结

将Go的这些“罪状”简单归结为“错误”也是片面的。它们是Go强硬的、自洽的设计哲学所带来的必然产物。

  • 这是一门有“历史”的现代语言:Go的设计深受其创造者们在C、Unix、Plan 9上的经验影响。它继承了C的简洁,但也继承了其对底层细节的暴露。
  • 承认权衡,理解其生态位:Go在“开发效率”、“运行性能”和“语言简单性”之间做出了明确的取舍,在云原生、微服务领域找到了无与伦比的“甜蜜点”。
  • 缓慢的进化也是一种承诺:Go团队对语言的改变极为谨慎,以维护其著名的向后兼容性承诺。但它并非一成不变。泛型的加入、for range循环变量作用域的修正,都表明Go在倾听社区的声音。

《Go is still not good》及其引发的激辩,为我们提供了一个宝贵的窗口,去重新审视这门既年轻又充满“历史感”的语言。它提醒我们,没有完美的语言,只有充满权衡的工具。

对于Go开发者而言,理解这“七宗罪”的来龙去脉,不仅能帮助我们写出更健壮、更地道的代码,更能让我们清晰地认识到Go的优势与边界。与其无休止地争论它是否“足够好”,不如深入思考:它是否是解决我们当前问题的正确工具? 而这,或许才是这场大辩论给予我们的最大启示。


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

AI 时代的初级工程师生存指南:别让“万能”的AI工具,毁掉你最宝贵的成长期

本文永久链接 – https://tonybai.com/2025/08/24/junior-engineer-survival-guide-in-ai-age

大家好,我是Tony Bai。

这是一个对初级工程师而言,最好也最坏的时代。

说它“最好”,是因为我们从未拥有过如此强大的工具。一名刚走出校门的毕业生,在入职的第一天,就能手握Claude Code、ChatGPT、Gemini Cli、Cursor、Copilot 等强大的 AI 编程助手。面对一个从未接触过的复杂任务——比如,为一个 Go 项目编写一个复杂的 gRPC 中间件——他可能只需要组织几次提示词,一段看起来完美、功能齐全、甚至带着单元测试的代码就诞生了。

那种“我什么都行”的强大感和即时满足感,是十年前的我完全无法想象的。

但说它“最坏”,也恰恰源于此。在这种令人沉醉的“魔幻”体验背后,一个直击灵魂的问题正在浮现:

这种惊人的“效率”,是在加速你的成长,还是在为你铺设一条通往“能力空心化”的捷径?

今天,我想和大家一起聊聊这个话题。这不仅是一份给初级工程师的生存指南,更是我们每一个身处 AI 浪潮中的技术人,都应该进行的深刻反思。

“浅层技能” vs “内功心法”:AI 正在拉开的差距

要理解 AI 带来的潜在风险,我们首先需要区分两种截然不同的技能:“浅层技能”“内功心法”

“浅层技能”,关注的是“是什么”(”What”)。在 AI 时代的初期,这主要体现为:

  • 擅长编写精妙的 Prompt。
  • 能快速地从 AI 处获得“能用”的代码片段。
  • 熟练地进行“复制-粘贴-修改”的“胶水”工作。

而如今,随着 Gemini CLI、Claude Code 这类编码智能体(Coding Agent)以及深度集成在 IDE 中的 AI 工具的兴起,这种“浅层技能”又演化出了一个更集成、也更具迷惑性的新形态。

“复制-粘贴”的动作消失了。取而代之的,是 Agent 直接读取你的整个代码库,然后给你一个可以直接应用的变更集(diff)。在这里,“浅层技能”表现为:

  • 将高阶的、模糊的任务指令(‘重构这个文件’、‘修复这个bug’)下发给 Agent。
  • 对 Agent 提出的变更集进行表面化的审查——检查代码风格是否一致、命名是否规范,但缺乏对底层逻辑、性能陷阱和安全隐患的深度洞察。
  • 最终,熟练地点击‘应用’(Apply)或‘接受’(Accept)按钮,成为一个高效的“变更批准员”

你看,无论是“复制代码”还是“批准变更”,其本质是相通的:你依然只停留在知道“是什么”,而没有深入到“为什么”。 你知道这段由 Agent 修改的代码能工作,但你很可能依然不清楚它背后的原理。这种新模式甚至更危险,因为它让你感觉更“专业”,更容易在不知不觉中放弃思考。

“内功心法”,关注的则是“为什么”(The “Why”)“怎么做”(The “How”)。这包括:

  • 设计模式:为什么在这里 AI 选择用工厂模式,而不是单例模式?这两种模式的权衡是什么?
  • 数据结构与算法:AI 生成的这个函数,其核心数据结构是什么?它的时间复杂度和空间复杂度在各种情况(最好、最坏、平均)下分别是多少?
  • 架构权衡:这段看似独立的代码,被集成到系统中后,是会提升整个系统的内聚性,还是会引入一个危险的耦合点?
  • 调试能力:当这段“完美”的 AI 代码,在生产环境中因为一个罕见的并发条件而出现诡异的 Bug 时,你有能力深入其中,定位并解决问题吗?

“浅层技能”决定了你使用工具的熟练度,而“内功心法”则决定了你作为一名工程师的能力天花板

“舒适”的代价:正在累积的“认知负债”

问题在于,AI 工具太“舒适”了。它让我们能轻易地绕过那些艰难的、需要深度思考的“内功”修炼,直接获得“浅层”的结果。这种舒适感,正在让我们不知不觉地背上沉重的“认知负债”(Cognitive Debt)

这个概念精辟地描述了一种权衡:我们用即时的便利,换取了长期的批判性思维、记忆力、以及创造性自主权的丧失。 你正在向机器借用脑力,但这笔债,未来需要连本带利地偿还。

最近的一项科学研究,用数据血淋淋地揭示了这一点。研究者将参与者分为三组写论文:纯脑力组、搜索引擎组和 LLM 组。结果令人震惊:

在 LLM 组中,83% 的参与者在写完论文后,几乎无法复述出自己文章中的任何观点(见下图)。而在另外两组,几乎每个人都能做到。

这证明了,当我们把思考过程完全外包给 AI 时,知识并没有真正“流经”我们的大脑,我们只是成为了一个信息的“搬运工”。

这背后,是纳西姆·塔勒布在其著作《反脆弱》中提到的深刻哲理:小的压力和不适,会让我们变得更强大。

  • 肌肉,需要通过举起沉重的杠铃,在撕裂和修复中才能生长。
  • 我们的大脑,同样需要通过“精神举重”——也就是主动思考、艰难探索、反复试错的“摩擦力”——才能成长。

而初级工程师的职业生涯前三年,正是进行这种“精神举重”、构建个人能力护城河最宝贵的黄金时期。如果在这个阶段,你沉迷于 AI 带来的舒适感,持续累积“认知负债”,无异于在一个本应最大化“肌肉增长”的年纪,选择了全程坐轮椅。

其长期危害是显而易见的:

  1. 成长停滞:解决问题的“认知肌肉”因缺乏锻炼而萎缩。
  2. 危险的“信心差”:你产生了一种“我能搞定任何代码”的虚假自信,但这与“我能维护和解释任何我写的代码”的真实能力之间,存在着巨大的鸿沟。这在团队协作和处理线上故障时,是极其危险的。
  3. 沦为“API 粘合工”:长期以往,你可能会彻底丧失从零开始构建系统的能力,沦为只会将 AI 生成的黑箱代码块“粘合”在一起的低阶操作员,失去了真正的工程创造力和不可替代性。

“破局”指南:如何成为 AI 的主人,而非奴隶?

那么,我们该如何打破这个困局?关键在于心态的转变方法的调整。你需要将 AI 从一个无所不知的“神谕”(Oracle),转变为一个需要你引导和挑战的“陪练”

这里有四条具体的实战法则:

法则一:“先思后问”法则

在向 AI 提问前,强迫自己先进行独立思考。用纸笔、伪代码或注释,勾勒出你自己的解决方案轮廓。哪怕这个方案很粗糙,甚至可能是错的,这个思考的过程本身就是一次宝贵的“精神举重”。

然后,你可以这样做:

“不要让 AI 直接为你解题;相反,提供你自己的解法,让它解释你可能错在哪里,或者有哪些可以改进的地方。”

通过这种方式,你把 AI 从一个“答案机”变为一个能启发你、挑战你的“批判性思维伙伴”。你始终是思考的主导者。

法则二:“刨根问底”法则

永远不要满足于 AI 给出的第一份“能用”的代码。对它生成的每一段关键代码,都要像对待一位资深同事的 Code Review 意见一样,进行苏格拉底式的反复追问

  • “你为什么选择这种数据结构?它和另一种方案相比,优劣势分别是什么?”
  • “这段代码的时间复杂度和空间复杂度是多少?在什么情况下会达到最坏情况?”
  • “请为这段代码生成五个可能会导致它出错的边缘案例(corner cases)。”
  • “这段代码遵循了哪些设计模式?请为我解释这个模式的核心思想。”

通过这种刨根问底,你把 AI 从一个“代码生成器”,变成了一个免费的、24小时在线的、极具耐心的私人导师

法则三:“刻意练习”法则

定期进行“无 AI 编程”的刻意练习。这就像健身房里的“力量训练日”。给自己设定一些小项目、算法题,或者工作中的某个非紧急模块,规定自己在不使用任何 AI 代码生成辅助的情况下,从零开始完整地实现它。

这个过程可能会让你感到“不适”,速度会很慢,甚至会碰壁。但请记住塔勒布的教诲:不适感不是麻烦,而是训练场。 这正是你构建底层能力、“流血流汗”的真实成长过程。

法则四:“源码为师”法则

当 AI 生成了一段使用了你从未见过的库函数或框架特性的代码时,不要只满足于知道“怎么用”。花15分钟时间,去看看那个函数或特性的源码实现。

这是理解其背后原理、设计哲学和实现细节的最直接方式。AI 可以为你指出一条通往宝藏的道路,但路边的风景和地下的矿藏,需要你自己去探索和挖掘。

你的不可替代性,藏在 AI 的“盲区”里

遵循以上法则,你会发现,AI 不仅不会成为你成长的障碍,反而会成为你成长的强大加速器。更重要的是,它会帮助你将精力聚焦在那些 AI 永远无法取代的、真正体现工程师价值的领域——也就是 AI 的“盲区”。

  • 深度理解业务:AI 不懂你的用户,不懂你的商业模式。将技术与业务场景深度结合,提供有洞察力的解决方案,这是你的核心价值。
  • 系统性思考:AI 能生成一个函数,但它尚无法设计一个健壮、可扩展、可维护的大规模生产级系统。培养自己的架构思维和全局视野,是你拉开差距的关键。
  • 人际协作与沟通:理解团队成员的需求,清晰地阐述技术方案的利弊,组织有效的讨论,推动项目落地。这些“软技能”,在 AI 时代变得比以往任何时候都更加重要。

小结:别在黄金时代,选择一条最容易的路

职业生涯的初期,是一个人构建个人“能力护城河”最关键的时期。在这个阶段,最宝贵的不是表面的“效率”,而是“成长深度”

AI 时代,给了我们前所未有的强大工具,但工具的价值,永远取决于使用者的智慧。

所以,请警惕那些让你过于舒适的捷径。不要在最需要“扎马步”的年纪,沉迷于“轻功”带来的快感。 真正的成长,永远伴随着求索的痛苦和突破的喜悦。让我们去拥抱那些有益的“摩擦力”,掌握那些 AI 无法生成的“内功心法”。

选择那条更难、但更有价值的路,你才能在未来的技术浪潮中,真正地立于不败之地。

参考资料

-《Your Brain on ChatGPT: Accumulation of Cognitive Debt when Using an AI Assistant for Essay Writing Task》- https://arxiv.org/pdf/2506.08872v1


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言进阶课 Go语言精进之路1 Go语言精进之路2 Go语言第一课 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats