2025年四月月 发布的文章

Rob Pike的“抱怨”与Go的“解药”:直面软件膨胀的四大根源

本文永久链接 – https://tonybai.com/2025/04/27/rob-pike-on-bloat

大家好,我是Tony Bai。

今年年初,Go语言之父、UTF-8编码的发明者Rob Pike的一篇题为”On Bloat”(关于膨胀)的演讲幻灯片(在2024年下旬做的)在技术圈,尤其是在Hacker News(以下简称HN)上,引发了相当热烈的讨论。Pike作为业界泰斗,其对当前软件开发中普遍存在的“膨胀”现象的犀利批评,以及对依赖管理、软件分层等问题的深刻担忧,无疑戳中了许多开发者的痛点。

HN上的讨论更是五花八门,开发者们纷纷从自身经历出发,探讨“膨胀”的定义、成因和后果。有人认为膨胀是“层层叠加的间接性”导致简单修改寸步难行;有人认为是“不必要的功能堆砌”;还有人归咎于“失控的依赖树”和“缺乏纪律的开发文化”。

那么,Rob Pike究竟在“抱怨”什么?他指出的软件膨胀根源有哪些?而作为我们Gopher,Go语言的设计哲学和工具链,能否为我们从纯技术层面提供对抗膨胀的“解药”呢?今天,我们就结合Pike的演讲精髓和HN的热议,深入聊聊软件膨胀的四大根源,并从Go的视角尝试寻找一下应对之道。

“膨胀”的真相:远不止代码大小和运行速度

在深入探讨根源之前,我们需要认识到,“膨胀”并不止是字面意义上我们理解的最终编译产物的大小或者应用的运行速度慢,Pike的观点和HN讨论中的“软件膨胀”体现在多个维度:

  • 复杂性失控: 过度的抽象层次、复杂的依赖关系、难以理解的代码路径,使得维护和迭代变得异常困难。
  • 维护成本剧增: 添加新功能的长期维护成本(包括理解、测试、修复Bug、处理兼容性)远超初次实现的成本,但往往被低估。
  • 不可预测性与脆弱性: 庞大且快速变化的依赖树使得我们几乎无法完全理解和掌控软件的实际构成和行为,任何更新都可能引入未知风险。

下面我们具体看看Pike指出的“膨胀”几个核心根源:

根源一:特性 (Features) —— “有用”不等于“值得”

Pike 指出,我们不断地为产品添加特性,以使其“更好”。但所有特性都会增加复杂性和成本,而维护成本是最大的那部分,远超初次实现。他警示我们要注意“有用谬论” —— 并非所有“有用”的功能都值得我们付出长期的维护代价。

HN讨论也印证了这一点:功能冗余、为了匹配竞品或满足某个高层“拍脑袋”的想法而添加功能、甚至开发者为了个人晋升而开发复杂功能的现象屡见不鲜。

技术层面:Go的“解药”在哪?

  • 简洁哲学: Go从设计之初就强调“少即是多”,鼓励用简单的原语组合解决问题,天然地抵制不必要的复杂性。
  • 强大的标准库: Go 提供了功能丰富且高质量的标准库,覆盖了网络、并发、加解密、I/O 等众多领域,减少了对外部特性库的依赖。很多时候,“自己动手,丰衣足食”(使用标准库)比引入一个庞大的外部框架更符合Go的风格。
  • 关注工程效率: Go的设计目标之一是提高软件开发(尤其是大型项目)的工程效率和可维护性,这促使Go社区更关注代码的清晰度和长期成本。

注:技术层面包括语言、工具以及设计思路和方法。

根源二:分层 (Layering) —— 在错误的层级“打补丁”

Pike 认为,现代软件层层叠加(硬件 -> 内核 -> 运行时 -> 框架 -> 应用代码),当出现问题时,我们太容易在更高的层级通过包装(wrap)来“修复”问题,而不是深入底层真正解决它。这导致了层层叠叠的“创可贴”,增加了复杂性和维护难度。他列举了ChromeOS文件App的例子,并强调要在正确的层级实现功能和修复

在HN的讨论中,有开发者描述的修改按钮颜色需要穿透17个文件和多个抽象层的例子,正是这种“错误分层”或“过度抽象”的生动体现。

技术层面:Go的“解药”在哪?

  • 小接口哲学: Go 鼓励定义小而专注的接口,这使得组件之间的依赖更清晰、更松耦合。当问题出现时,更容易定位到具体的接口实现层去修复,而不是在外部层层包装。
  • 组合优于继承: Go 通过组合(struct embedding)而非继承来实现代码复用,避免了深度继承带来的复杂性和脆弱性,使得在“正确层级”修改代码更易操作。
  • 显式错误处理: if err != nil 的模式强制开发者在调用点处理错误,使得问题更难被“隐藏”到上层去统一“包装”处理,鼓励在错误发生的源头附近解决或添加上下文。

根源三:依赖 (Dependencies) —— 看不见的“冰山”

这是Pike演讲中着墨最多、也最为忧虑的一点。他用数据(NPM 包平均依赖 115 个其他包,每天 1/4 的依赖解析发生变化)和实例(Kubernetes 的复杂依赖图)强调:

  • 现代软件依赖数量惊人且变化极快。
  • 我们几乎不可能完全理解自己项目的所有直接和间接依赖。
  • 依赖中隐藏着巨大的维护成本、Bug 和安全风险
  • 简单的 npm update 或 audit 无法解决根本问题

他强烈建议要理解依赖的成本严格、定期地审视依赖树,并推荐了 deps.dev 这样的工具。

HN 社区对此深有同感,纷纷吐槽“为了一个函数引入整个库”、“脆弱的传递性依赖”、“供应链安全”等问题,并呼唤更好的依赖分析工具。

技术层面:Go的“解药”在哪?

  • Go Modules: 相比 NPM 等包管理器,Go Modules 提供了相对更好的依赖管理机制,包括语义化版本控制、go.sum 校验和、最小版本选择 (MVS) 等,提高了依赖的可预测性和安全性,但也要注意Go module并非完美
  • 强大的标准库: 这是 Go 对抗依赖泛滥的最有力武器。很多功能可以直接使用标准库,避免引入外部依赖。
  • 社区文化: Go 社区相对而言更推崇稳定性和较少的依赖。引入一个大型框架或过多的外部库在 Go 社区通常需要更充分的理由。
  • 工具支持: Go 提供了 go mod graph, go mod why 等命令,可以帮助开发者理解依赖关系。结合 deps.dev,可以在一定程度上实践 Pike 的建议。

根源四:开源模式 (Open Source Development) —— “大门敞开” vs “严格把关”

Pike 对比了两种开源开发模式:

  • “真正的开源方式” (The true open source way): 接受一切贡献 (Accept everything that comes)。他认为这是膨胀和 Bug 的巨大来源
  • 更好的方式: 设立严格的代码质量、标准、评审、测试、贡献者审查等“门槛”,对允许合入的内容有标准。这种方式维护成本低得多。

他暗示 Go 项目本身更倾向于后者,强调“先做好再提交”(make it good before checking it in)。可能很多Gopher也感受到了这一点,Go项目本身对代码质量的review非常严格,这一定程度上也“延缓”了一些新特性进入Go的时间点。

HN 的讨论中也涉及了类似 “Bazaar vs Cathedral” 的模式对比,但观点更加复杂,认为现实中的项目往往处于两者之间的某个位置,并且“完全不接受外部贡献”也并非良策。

技术层面:Go的“解药”在哪?

  • Go 自身的开发模式: Go 语言本身(由 Google 主导)的开发流程相对严谨,对代码质量和向后兼容性有较高要求,可以看作是“严格把关”模式的体现。
  • 标准库的设计: Go 标准库的设计精良、接口稳定,为开发者提供了一个高质量的基础平台,减少了对外部“随意贡献”的依赖。
  • 社区项目实践: 观察 Go 社区一些知名的开源项目,其贡献流程和代码标准通常也比较严格。

反思与现实:Go 也非万能,“警惕与纪律”仍是关键

虽然 Go 的设计哲学和工具链在对抗软件膨胀方面提供了许多“天然优势”和“解药”,但我们必须清醒地认识到,Go 语言本身并不能完全免疫膨胀

正如 Pike 在其“建议”(Advice) 中反复强调的,以及 HN 讨论中部分开发者指出的,最终软件的质量很大程度上取决于开发者和团队的“警惕与纪律” (vigilance and discipline)

  • 我们是否真正理解并避免了增加不相称成本的特性
  • 我们是否努力在正确的层级解决问题
  • 我们是否审慎地评估和管理了每一个依赖
  • 我们是否坚持了高标准的开发和评审流程

如果缺乏这些,即使使用 Go,项目同样可能变得臃肿、复杂和难以维护。同时,HN 讨论也提醒我们,软件膨胀背后还有更深层次的组织、文化和经济因素,这些往往超出了单纯的技术和开发者纪律所能解决的范畴。

小结:拥抱 Go 的简洁,但需务实前行

Rob Pike 的“抱怨”为我们敲响了警钟,Hacker News 的热议则展现了软件膨胀问题的复杂性和普遍性。它确实是我们在工程实践中需要持续对抗的“熵增”现象。

Go 语言以其简洁、显式、组合的设计哲学,以及强大的标准库和相对稳健的依赖管理,在技术层面上,为我们提供了对抗膨胀的有力武器。理解并拥抱这些 Go 的“基因”,无疑能在一定程度上帮助我们构建更健康、更可持续的软件系统。

当然,Pike 的观点也并非金科玉律。有批评者指出,他的视角可能带有一定的“NIH(非我发明)倾向”,并且存在两个关键的“盲点”:

  1. 忽视了“不使用依赖”同样是巨大的技术债。 每一行自写的代码都需要永远维护。
  2. 现实中的选择往往不是“使用依赖 vs 自己实现”,而是“使用依赖 vs 根本不做这个功能”。 面对复杂的合规要求(如 ADA、GDPR)、第三方集成或 FIPS 认证等,从零开始构建的成本(可能需要数百人年)往往让“自己实现”变得不切实际。为了让产品能够及时上线并满足用户(哪怕是 Pike 本人可能也在使用的“缓慢”网站)的需求,引入依赖和一定的“膨胀”有时是必要且务实的选择。

注:“NIH(非我发明)倾向”是一种心理现象,指的是人们对他人提出的想法或创新持有偏见,通常因为这些想法不是自己发明的。这种倾向使得人们倾向于低估或拒绝其他人的创意,尽管这些创意可能是有价值的。

这种批评也提醒了我们,虽然 Pike 对简洁和纪律的呼吁值得我们高度重视,但在真实的商业环境和复杂的工程约束下,我们必须做出务实的权衡。纯粹的技术理想有时需要向现实妥协。

最终,我们每一位 Gopher 都需要在理解 Go 简洁之道的同时,保持批判性思维和务实态度。 在日常的每一个决策中,审慎地权衡简单与复杂、理想与现实、引入依赖与自主掌控,才能在这场与“膨胀”的持久战中,找到最适合我们项目和团队的平衡点,交付真正有价值且可持续的软件。

你如何看待 Rob Pike 对软件膨胀的观点?你认为他的批评切中要害,还是忽视了现实的复杂性?欢迎在评论区分享你的思考与实践!

参考资料

  • Rob Pike – On Bloat – https://docs.google.com/presentation/d/e/2PACX-1vSmIbSwh1_DXKEMU5YKgYpt5_b4yfOfpfEOKS5_cvtLdiHsX6zt-gNeisamRuCtDtCb2SbTafTI8V47/pub?slide=id.p
  • HN:On Bloat – https://news.ycombinator.com/item?id=43045713
  • Pike is wrong on bloat
  • On Bloat – https://commandcenter.blogspot.com/2025/02/on-bloat-these-are-slides-from-talk-i.html

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

【规律之手】资深码农都懂?软件工程中的13条“潜规则”定律

本文永久链接 – https://tonybai.com/2025/04/26/13-laws-of-software-engineering

大家好,我是Tony Bai。

做软件开发时间越长,越觉得背后似乎有只“无形的手”在影响着项目进度、团队协作、系统架构甚至技术决策。有些现象反复出现,从早期的一头雾水,到后来的似曾相识,再到最后的会心一笑(或许是苦笑),让人不得不感慨其中蕴含的某些“规律”。

最近看到国外一位开发者ANTON ZAIDES总结了软件工程领域的13条“定律”。它们中有些广为人知,有些则相对小众,但都非常实用。它们虽然不像物理定律那样严格精确,但确实精准地捕捉到了我们日常工作中经常遇到的挑战和现象,堪称是工程师和管理者都应该了解的宝贵“经验法则”或“心智模型”。

今天,就让我们一起来了解和学习一下这13条定律,看看它们是如何在我们身边运作的。

注:下面文中各条定律的配图也借自ANTON ZAIDES的原文章。

1. 帕金森定律 (Parkinson’s law)

定律:工作会不断扩展,填满所有可用的时间 (Work expands to fill the available time.) (任务总能拖到最后期限前完成?)

这是最著名的定律之一。简单说,如果你给一个任务设定了1周的期限,它很可能就会花掉1周;如果设定了2周,它就可能花掉2周。这常常被用来解释为什么设定“伪造”的(有时甚至不合理的)截止日期似乎能提高效率——它迫使人们在有限的时间内集中精力。但这一定律也容易被滥用,导致不切实际的期望和压力。

合理设定Deadlines是必要的,但要警惕其副作用,并结合对工作量的实际评估。它提醒我们时间管理的重要性,以及在没有明确时间约束时,任务可能无限膨胀的风险。

2. 霍夫施塔特定律 (Hofstadter’s law)

定律:事情总是比你预期的要花费更长的时间,即使你已经考虑了霍夫施塔特定律。 (It always takes longer than you expect, even when you take into account Hofstadter’s Law.) (估时永远不准?)

这是对软件项目估时困难最精准的自嘲。几乎所有的软件项目都会延期,即使你已经预留了缓冲时间。这一定律完美地平衡了帕金森定律:如果你因为帕金森定律而设置过短的Deadline,结果很可能是团队burnout或者项目持续延期。

软件工时评估极其困难,充满了不确定性。简单的缓冲时间往往不够。有效的项目管理需要在时间、资源和可协商的范围 (negotiable scope) 之间找到平衡,并依赖持续的沟通和实践经验。

3. 布鲁克斯定律 (Brooks’ law)

定律:向一个以延期的软件项目中增加人力,将使其更加延期。(Adding manpower to a late software project makes it later.) (人月神话?)

这就是著名的“9个孕妇不能在1个月内生出一个婴儿”的道理。当项目延期,高层管理者常常会说:“这个项目很紧急,你可以从其他团队调配任何你需要的人!” 但项目经理的内心可能是:“请别再打扰我们,让我们专心工作就好”。

增加新人手需要时间成本:新人需要学习项目背景、熟悉代码库、建立沟通渠道。这些都会消耗现有团队成员的时间和精力,增加沟通开销,短期内甚至可能降低整体生产力。

在项目后期,尤其是面临延期时,要极其谨慎地考虑增加人手。更好的策略可能是缩减范围、优化流程或给予现有团队更多不受干扰的时间。

4. 康威定律 (Conway’s law)

**定律:组织输出的设计是这些组织的沟通结构的副本。(Organizations produce designs which are copies of the communication structures of these organizations.) (你的架构是不是反映了你的团队结构?)

简而言之,你的系统架构往往是你团队组织结构的镜像。如果你的公司有独立的“前端团队”和“后端团队”,他们之间的沟通壁垒和协作模式,会直接反映在前后端接口的设计、数据格式的匹配度以及可能出现的额外“胶水代码”上。

这一定律提醒我们组织结构对技术决策的深远影响。反过来,我们也可以利用 逆康威定律 (Inverse Conway Maneuver):为了达成期望的系统架构,主动调整团队的组织结构和沟通方式。例如,想要微服务化?那就组建更小、更自治、拥有端到端职责的团队。

5. 坎宁安定律 (Cunningham’s law)

定律:在互联网上获得正确答案的最佳方式不是提问,而是发布一个错误的答案。 (The best way to get the right answer on the internet is not to ask a question; it’s to post the wrong answer.) (想得到反馈?先大胆“错”一个?)

这条定律巧妙地利用了人性——人们往往更乐于纠正错误,而不是回答问题。

在工作中遇到阻碍时,可以巧妙运用这一定律。例如,与其提交一个请求单等待DevOps团队处理,不如自己尝试写一个(哪怕不完美的)解决方案,提交一个Pull Request。即使写得不对,通常也能更快地获得相关人员的注意和具体的修改建议,同时也促进了知识的传递和流程的改进。主动迈出第一步,哪怕是“错误”的一步,也比原地等待更有效。

6. 斯特金定律 (Sturgeon’s law)

定律:任何事物(特别是人类创造出来的)90% 都是垃圾。 (Ninety percent of everything is crap.) (你做的功能多少是真正有价值的?)

这条定律是对现实的残酷揭示,有点像加强版的“二八定律”。无论是代码、想法、功能特性,大部分都可能是平庸甚至无用的。你发布的大部分功能可能对用户价值寥寥,只有那一小部分核心功能支撑着你的产品。

这要求我们具备批判性思维,勇于质疑。作为开发者,不能仅仅被动接受产品经理给出的需求列表,而要思考功能的真正价值,避免将精力浪费在那“90%的垃圾”上。这也解释了为什么“10倍工程师”并非指写10倍代码的人,而是能创造10倍价值的人——他们更懂得识别和聚焦于那重要的10%。

7. 扎文斯基定律 (Zawinski’s law)

定律:每一个程序都试图扩展直到它能阅读邮件为止。那些不能如此扩展的程序会被可以如此扩展的程序替代掉。 (Every program attempts to expand until it can read mail. Those programs which cannot so expand are replaced by ones that can.) (警惕功能蔓延!)

这条定律形象地描述了“功能蔓延”(feature creep) 的现象。程序(或产品)总有一种内在的趋势去添加越来越多的功能,最终变得臃肿不堪。尤其在AI时代,给任何应用加上一个聊天机器人似乎都轻而易举。

我们要警惕无休止的功能添加!保持产品的核心价值和简洁性至关重要。过多的功能不仅会增加复杂度和维护成本,还可能让用户(尤其是新用户)感到困惑,找不到真正需要的功能。需要有意识地做减法,抵制“什么都想要”的诱惑。

8. 海勒姆定律 (Hyrum’s law / The Law of Implicit Interfaces)

定律:当你有足够多的API用户时,你在合同(文档)中承诺什么都无关紧要:你系统中所有可观察的行为都会被某些人所依赖。 (With a sufficient number of users of an API, it does not matter what you promise in the contract: all observable behaviors of your system will be depended on by somebody.) (接口行为不能轻易改动!)

这条定律对API设计和维护者至关重要,它揭示了接口(API)维护的残酷现实。即使某个行为没有写在你的官方文档里,只要它是可观察到的(比如某个特定的错误返回格式、某个未公开的内部端点、某个副作用),一旦有足够多的用户,就一定会有人依赖上这个行为。

因此,API的设计和变更需要极其谨慎。任何微小的改动,即使是修复Bug或改变未承诺的行为,都可能破坏依赖者的系统。这也解释了为什么移除那些依据斯特金定律属于“垃圾”的特性如此困难——总有用户在依赖它们。进行接口设计时,要尽可能减少可观察的副作用,明确接口契约,并为变更做好版本管理和兼容性策略。

9. 普莱斯定律 (Price’s law / Price’s Square Root Law)

定律:在一个组织中,一半的工作是由占总人数平方根的人完成的。 (50% of the work is done by the square root of the total number of people.) (团队里的核心贡献者?)

这一定律量化了贡献度的不平均分布。例如,在一个10人的团队里,大约3个人 (√10 ≈ 3.16) 完成了50%的工作;在一个 100 人的公司里,大约 10个人 (√100 = 10) 的产出相当于剩下90人的总和。这也可以在一定程度上解释为什么Twitter在大规模裁员后没有立即崩溃。

团队规模的扩大并不会带来线性的产出增长。如果你想让产出翻倍,可能需要4倍的人员规模。这警示管理者在扩张团队时要关注人效和组织结构,识别并赋能那些核心贡献者。

10. 瑞格曼效应 (The Ringelmann effect)

定律:当一个团体的规模增加时,个体成员的生产力趋于下降。 (The tendency for individual members of a group to become increasingly less productive as the size of their group increases.) (人多不一定力量大?)

这个效应早在1913年就被发现(通过拔河实验)。团队越大,个体平均贡献的力量越小。原因主要有两个:一是动机丧失(即“社会惰化”,觉得自己的贡献不重要或难以衡量);二是协调成本增加(沟通、同步、冲突解决等开销变大)。

这也是对布鲁克斯定律和普莱斯定律的有力补充。保持小而精干的团队往往效率更高,尤其是在需要高度协作和创新的领域。明确的职责划分、有效的沟通机制和对个体贡献的认可,有助于缓解瑞格曼效应。

11. 古德哈特定律 (Goodhart’s law)

定律:当一个度量本身成为目标时,它就不再是一个好的度量。 (When a measure becomes a target, it ceases to be a good measure.) (警惕 KPI 陷阱!)

这是关于KPI和度量最著名的警告。一旦某个指标(如代码行数、PR 数量、Bug 修复数、用户增长数、客户满意度)被设定为考核目标,人们就会想方设法“优化”这个指标本身,而不是优化它所代表的真实价值,最终导致该指标失去意义。例如,为了提高代码行数而写冗余代码,为了快速关闭工单而不是定位根因并从根本上解决问题。

对任何单一的量化指标都要保持警惕。度量是必要的,但不能迷信指标。需要结合多个指标、定性分析以及对最终业务价值的判断,来全面评估绩效和进展。

12. 吉尔布定律 (Gilb’s law)

定律:任何你需要量化的东西,都可以用某种方式来衡量,这种衡量方式优于完全不衡量。 (Anything you need to quantify can be measured in some way that is superior to not measuring it at all.) (与上一条辩证看,还是要量化!)

这一定律是古德哈特定律的必要平衡。它告诉我们,尽管度量可能不完美、可能被“攻击”,但完全放弃量化是不可取的。“没有度量,就没有改进”。找到一个(哪怕是粗糙的)量化方法,总比凭感觉行事要好。

因此,不要因为害怕古德哈特定律而彻底放弃量化。关键在于选择合适的度量维度(比如 DORA 指标、开发者体验 DevEx 等),持续迭代和优化度量方法,并结合业务背景进行解读。

13. 墨菲定律 (Murphy’s law)

定律:任何可能出错的事情,最终都会出错。 (Anything that can go wrong will go wrong.) (那个被你忽略的边缘 Case…?)

这条定律大家再熟悉不过了。它提醒我们,那些看起来概率极小、懒得处理的边缘情况、那个被你忽略的潜在Bug、那一次“应该没问题”的侥幸操作,往往会在最关键的时候给你带来麻烦。

在软件工程中,要有敬畏之心。进行充分的测试(尤其是边缘情况测试)、建立健壮的错误处理和容错机制、实施灰度发布和监控告警,都是应对墨菲定律的必要手段。不要低估任何可能出错的环节。

小结:定律是启发,而非束缚

这13条定律,更像是前辈们用经验和教训为我们绘制的“认知地图”。它们并非严格的科学定理,但在理解软件开发这个复杂系统时,能为我们提供宝贵的视角和警示。

将这些定律记在心中,不是为了给自己设限或者找借口,而是为了让我们在日常的编码、设计、沟通和决策中,多一份清醒,多一份审慎,少踩一些坑,从而更从容地驾驭软件工程这门充满挑战与乐趣的艺术。

你对这些定律有哪些特别的感触?或者在你多年的开发生涯中,还总结出了哪些有趣的“私房定律”?

欢迎在评论区留下你的思考和故事!


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

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 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