<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tony Bai &#187; 注释</title>
	<atom:link href="http://tonybai.com/tag/%e6%b3%a8%e9%87%8a/feed/" rel="self" type="application/rss+xml" />
	<link>https://tonybai.com</link>
	<description>一个程序员的心路历程</description>
	<lastBuildDate>Thu, 30 Apr 2026 23:46:25 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>数据打脸刻板印象：Go 的“样板代码”竟然和 Rust 一样多？</title>
		<link>https://tonybai.com/2026/02/08/go-boilerplate-code-vs-rust-data-refutes-stereotypes/</link>
		<comments>https://tonybai.com/2026/02/08/go-boilerplate-code-vs-rust-data-refutes-stereotypes/#comments</comments>
		<pubDate>Sat, 07 Feb 2026 23:39:52 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[BenBoyter]]></category>
		<category><![CDATA[BoilerplateCode]]></category>
		<category><![CDATA[BoilerplateCodeDensity]]></category>
		<category><![CDATA[Comment]]></category>
		<category><![CDATA[Complexity]]></category>
		<category><![CDATA[DataAnalysis]]></category>
		<category><![CDATA[ErrorHandling]]></category>
		<category><![CDATA[Expressivity]]></category>
		<category><![CDATA[FunctionalLanguages]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GoLanguage]]></category>
		<category><![CDATA[Go语言]]></category>
		<category><![CDATA[InformationDensity]]></category>
		<category><![CDATA[Rust]]></category>
		<category><![CDATA[SLOC]]></category>
		<category><![CDATA[SoftwareEngineering]]></category>
		<category><![CDATA[Stereotype]]></category>
		<category><![CDATA[StructuralRedundancy]]></category>
		<category><![CDATA[SyntacticSugar]]></category>
		<category><![CDATA[TypeSystem]]></category>
		<category><![CDATA[ULOC]]></category>
		<category><![CDATA[ULOCMetric]]></category>
		<category><![CDATA[ULOC指标]]></category>
		<category><![CDATA[UniqueLinesOfCode]]></category>
		<category><![CDATA[信息密度]]></category>
		<category><![CDATA[函数式语言]]></category>
		<category><![CDATA[刻板印象]]></category>
		<category><![CDATA[唯一代码行数]]></category>
		<category><![CDATA[复杂度]]></category>
		<category><![CDATA[数据分析]]></category>
		<category><![CDATA[样板代码]]></category>
		<category><![CDATA[样板代码密度]]></category>
		<category><![CDATA[注释]]></category>
		<category><![CDATA[源代码行数]]></category>
		<category><![CDATA[类型系统]]></category>
		<category><![CDATA[结构化冗余]]></category>
		<category><![CDATA[表达力]]></category>
		<category><![CDATA[语法糖]]></category>
		<category><![CDATA[软件工程]]></category>
		<category><![CDATA[错误处理]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5845</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2026/02/08/go-boilerplate-code-vs-rust-data-refutes-stereotypes 大家好，我是Tony Bai。 在编程语言的鄙视链中，Go 语言常常因为其“繁琐”而饱受诟病。 “if err != nil 写断手”、“缺乏语法糖”、“到处都是重复的样板代码”…… 这些似乎已经成为了 Go 的标签。 相比之下，Rust 往往被视为“表达力”的代表，拥有强大的宏、模式匹配和类型系统，能够用更少的代码做更多的事。 然而，Ben Boyter 最近的一项硬核研究，通过分析 GitHub 上各语言 Top 100 仓库（总计约 4 亿行代码），得出了一个令编程语言社区大跌眼镜的结论： 在代码重复率和“样板代码”密度上，Go 和 Rust 几乎处于同一水平线。 不仅是行数：ULOC 指标 传统的 SLOC（源代码行数）往往无法真实反映项目的复杂度和冗余度。Ben Boyter 使用了他开发的工具 scc 中的一个特殊指标：ULOC (Unique Lines of Code，唯一代码行数)。 ULOC 指标并非简单的“全量去重”，而是通过剥离“结构性噪音”来更精准地衡量系统的真实复杂度。其计算逻辑如下： 剔除结构化冗余：不仅排除了空行，还排除了单纯的闭合大括号行（}）以及在不同文件中大量重复出现的公共引用代码（如 include 或 import）。 过滤文件级模板：有效识别并扣除在项目中每个文件顶部几乎完全相同的 License（许可证）声明头，避免这些非逻辑性的“样板文字”虚增代码总量。 计入注释成本：与传统 SLOC 不同，ULOC 会保留注释统计。作者认为，注释与代码一样需要同等的维护精力，反映了开发者的思考过程，因此属于“有效工作量”。 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2026/go-boilerplate-code-vs-rust-data-refutes-stereotypes-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2026/02/08/go-boilerplate-code-vs-rust-data-refutes-stereotypes">本文永久链接</a> &#8211; https://tonybai.com/2026/02/08/go-boilerplate-code-vs-rust-data-refutes-stereotypes</p>
<p>大家好，我是Tony Bai。</p>
<p>在编程语言的鄙视链中，Go 语言常常因为其“繁琐”而饱受诟病。</p>
<p>“if err != nil 写断手”、“缺乏语法糖”、“到处都是重复的样板代码”…… 这些似乎已经成为了 Go 的标签。</p>
<p>相比之下，Rust 往往被视为“表达力”的代表，拥有强大的宏、模式匹配和类型系统，能够用更少的代码做更多的事。</p>
<p>然而，<a href="https://boyter.org/posts/boilerplate-tax-ranking-popular-languages-by-density/">Ben Boyter 最近的一项硬核研究</a>，通过分析 GitHub 上各语言 Top 100 仓库（总计约 4 亿行代码），得出了一个令编程语言社区大跌眼镜的结论：</p>
<p><strong>在代码重复率和“样板代码”密度上，Go 和 Rust 几乎处于同一水平线。</strong></p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/system-programming-in-go-pr.png" alt="" /></p>
<h2>不仅是行数：ULOC 指标</h2>
<p>传统的 SLOC（源代码行数）往往无法真实反映项目的复杂度和冗余度。Ben Boyter 使用了他开发的工具 <a href="https://github.com/boyter/scc">scc</a> 中的一个特殊指标：<a href="https://boyter.org/posts/sloc-cloc-code-new-metic-uloc/">ULOC (Unique Lines of Code，唯一代码行数)</a>。</p>
<p>ULOC 指标并非简单的“全量去重”，而是通过剥离“结构性噪音”来更精准地衡量系统的真实复杂度。其计算逻辑如下：</p>
<ul>
<li>剔除结构化冗余：不仅排除了空行，还排除了单纯的闭合大括号行（}）以及在不同文件中大量重复出现的公共引用代码（如 include 或 import）。</li>
<li>过滤文件级模板：有效识别并扣除在项目中每个文件顶部几乎完全相同的 License（许可证）声明头，避免这些非逻辑性的“样板文字”虚增代码总量。</li>
<li>计入注释成本：与传统 SLOC 不同，ULOC 会保留注释统计。作者认为，注释与代码一样需要同等的维护精力，反映了开发者的思考过程，因此属于“有效工作量”。</li>
</ul>
<p>通过这种方式计算出的 Dryness（干度），代表了剔除“语法支架”和“版权模板”后，真正的业务逻辑与注释在代码中的占比。百分比越高，说明重复代码越少，信息密度越高；百分比越低，说明“样板代码”或重复结构越多。</p>
<h2>令人震惊的对比：Go vs Rust</h2>
<p>让我们直接看数据（数据来源：<a href="https://github.com/EvanLi/Github-Ranking/">GitHub Top 100 仓库</a>分析，2026年2月）：</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/go-boilerplate-code-vs-rust-data-refutes-stereotypes-2.png" alt="" /></p>
<p><strong>发现了吗？Rust (60.5%) 和 Go (58.78%) 的差距微乎其微，甚至可以说在统计学上是等价的。</strong></p>
<p>Ben Boyter 在文章中坦言，他之前也持有“Go 的样板代码比 Rust 多得多”的刻板印象。但数据表明，虽然两者的“啰嗦”方式不同，但结果是一样的：</p>
<ul>
<li>Go 的啰嗦：体现在显式的错误处理、显式的循环结构，以及为了简单性而不得不写的重复逻辑。</li>
<li>Rust 的啰嗦：体现在复杂的类型系统设置、Trait 的实现（impl blocks）、以及为了满足借用检查器而编写的“仪式性”代码。</li>
</ul>
<p>正如作者所总结的：</p>
<blockquote>
<p>Go 狂热者：“Go 很简单！” -> “是的，简单到你需要把同一件事写很多遍。”<br />
  Rust 狂热者：“Rust 完美表达！” -> “是的，但你花了 40% 的时间在写 setup 代码和 trait 实现。”</p>
</blockquote>
<h2>其他颠覆性的发现</h2>
<p>除了 Go 和 Rust 的“握手言和”，这份报告还有几个极具冲击力的发现：</p>
<h3>1. Lisp 家族是“干度之王”</h3>
<p>Clojure 以 77.91% 的惊人密度位居榜首。Haskell 紧随其后。</p>
<p>这验证了一个古老的观点：如果你想要最高的“人类思想 vs 击键次数”比率，Lisp 和函数式语言依然是王者。它们几乎每一行代码都是纯粹的业务逻辑。</p>
<h3>2. Java 居然比 Go 和 Rust 都“干”？</h3>
<p>Java 的得分为 65.72%，显著高于 Go、Rust 和 C#。</p>
<p>这听起来反直觉，毕竟 Java 以 PublicStaticVoidMain 这种冗长著称。但这可能说明：</p>
<ul>
<li>现代 Java 及其生态（Spring 等）通过注解等方式极大地消除了样板代码。</li>
<li>或者，Top 100 的 Java 项目多为成熟的业务系统，核心逻辑占比大，而 Go/Rust 项目中系统级代码（通常包含更多底层重复逻辑）较多。</li>
</ul>
<h3>3. 脚本语言的特异性</h3>
<p>Shell Script 的密度极高（72.24%），但这主要是因为 Shell 脚本通常很短且高度定制化（Bespoke），很难复用，因此“唯一性”很高。</p>
<h2>小结：复杂度的守恒</h2>
<p>这个研究告诉我们一个道理：语言特性（Features）并不一定能消除复杂度，它往往只是转移了复杂度。</p>
<p>Go 选择了少量的特性，导致逻辑必须通过显式的重复代码来表达；Rust 选择了丰富的特性（宏、泛型、Trait），导致开发者必须编写大量的结构性代码来支撑这些特性。</p>
<p>对于 Gopher 来说，这或许是一种宽慰：别再为 if err != nil 感到羞愧了。隔壁写 Rust 的兄弟，虽然代码看起来很酷，但他们为了让编译器开心而敲击键盘的次数，并不比你少。</p>
<p><strong>毕竟，软件工程没有银弹，只有取舍。</strong></p>
<p>资料链接：https://boyter.org/posts/boilerplate-tax-ranking-popular-languages-by-density/</p>
<hr />
<p><strong>聊聊你的“啰嗦”体验</strong></p>
<p>看完这个数据，你是感到“意料之中”还是“大吃一惊”？在你的实际开发中，你觉得 Go 的 if err != nil 更磨人，还是 Rust 的类型体操和 Trait 实现更让你头大？你认同“复杂度守恒”这个观点吗？</p>
<p>欢迎在评论区留下你的看法，让我们来一场理性的“语言之争”！</p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2026, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2026/02/08/go-boilerplate-code-vs-rust-data-refutes-stereotypes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>看完《疯狂动物城2》，我发现“完美架构”的谎言被戳破了</title>
		<link>https://tonybai.com/2025/12/07/zootopia-2-perfect-architecture-lie-exposed/</link>
		<comments>https://tonybai.com/2025/12/07/zootopia-2-perfect-architecture-lie-exposed/#comments</comments>
		<pubDate>Sun, 07 Dec 2025 08:06:05 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Agnes]]></category>
		<category><![CDATA[APIGateway]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[Cgo]]></category>
		<category><![CDATA[cloudnative]]></category>
		<category><![CDATA[Evolve]]></category>
		<category><![CDATA[fortran]]></category>
		<category><![CDATA[Gary]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[LegacyCode]]></category>
		<category><![CDATA[LegacySystem]]></category>
		<category><![CDATA[Microservices]]></category>
		<category><![CDATA[MonolithicApplication]]></category>
		<category><![CDATA[PerfectArchitecture]]></category>
		<category><![CDATA[Reptile]]></category>
		<category><![CDATA[Rewrite]]></category>
		<category><![CDATA[Tundratown]]></category>
		<category><![CDATA[wrapper]]></category>
		<category><![CDATA[Zootopia2]]></category>
		<category><![CDATA[云原生]]></category>
		<category><![CDATA[单体应用]]></category>
		<category><![CDATA[完美架构]]></category>
		<category><![CDATA[微服务]]></category>
		<category><![CDATA[文档]]></category>
		<category><![CDATA[核心交易系统]]></category>
		<category><![CDATA[注释]]></category>
		<category><![CDATA[演进]]></category>
		<category><![CDATA[热感应]]></category>
		<category><![CDATA[爬行动物]]></category>
		<category><![CDATA[疯狂动物城2]]></category>
		<category><![CDATA[遗留代码]]></category>
		<category><![CDATA[遗留系统]]></category>
		<category><![CDATA[重构]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5495</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/12/07/zootopia-2-perfect-architecture-lie-exposed 大家好，我是Tony Bai。 还记得昨天那篇文章里，我还在为那个“标题党”的题目（《如果〈疯狂动物城〉是一个分布式系统&#8230;》）向大家“真诚致歉”吗？ 当时，带着重温第一部的滤镜，我信誓旦旦地跟大家吹牛，说动物城简直就是 Go 语言构建的云原生架构典范——高效、隔离、完美。 但这周六下午看完《疯狂动物城2》，我不得不承认：草率了，这次“打脸”来得太快。 如果说第一部展示了架构师眼中的“理想国”，那么第二部则残忍地揭开了“完美架构”背后的谎言。 看着银幕上那条被大家畏惧、却掌握着关键线索的蛇（Gary），以及那个被冰雪掩埋的真相，我脊背发凉。这哪里是童话？这分明就是一部《大型遗留系统（Legacy System）维护血泪史》。 作为架构师，我在这部电影里看到了三个关于“新老技术”的扎心隐喻。 被埋葬的“爬行动物”：那些我们不敢碰的 Legacy Code 在电影里，我们得知了一个惊天秘密：动物城引以为傲的“温控系统”和城市规划，并非现在的创始人（林雪猁, Lynxley）设计的，而是源自一位爬行动物——Agnes（Gary的曾祖母）。 但为了打造一个看似光鲜、只有可爱哺乳动物的“新城区”（Tundratown），管理者选择了掩盖历史。他们直接把爬行动物的家园（Reptile Ravine）埋在了厚厚的冰雪之下，假装它们从未存在。 这一幕，像极了我们对待“遗留代码（Legacy Code）”的态度。 在现代化的 Go 微服务、Kubernetes 集群（Tundratown）之下，往往深埋着一套跑了20年的、由 C/C++ 甚至 Fortran 编写的核心交易系统（爬行动物）。 * 它们古老、丑陋（代码风格甚至没有缩进）； * 它们看起来危险（改一行代码可能崩全站，就像蛇会咬人）； * 所以，我们选择“封印”它。我们用一层又一层的 Wrapper、API 网关把它包裹起来，假装我们已经拥有了一个全新的、完美的系统。 但电影告诉我们：物理掩埋解决不了问题。 当危机来临，那些被忽视的底层逻辑，终将“破土而出”。 Gary 的热感应：老技术独有的“超能力” 电影里有一段非常精彩的情节：朱迪和尼克束手无策时，是蛇 Gary 利用响尾蛇特有的“热感应”能力，看透了迷雾，找到了线索。 这让我想起，每当新技术（如 AI、Web3）甚嚣尘上时，我们往往会轻视那些“老古董”。 我们觉得 Go/Rust 这种现代语言无所不能。 我们觉得 C 语言指针复杂、汇编晦涩、SQL 存储过程老土。 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/zootopia-2-perfect-architecture-lie-exposed-1.jpg" alt="" /></p>
<p><a href="https://tonybai.com/2025/12/07/zootopia-2-perfect-architecture-lie-exposed">本文永久链接</a> &#8211; https://tonybai.com/2025/12/07/zootopia-2-perfect-architecture-lie-exposed</p>
<p>大家好，我是Tony Bai。</p>
<p>还记得昨天那篇文章里，我还在为那个“标题党”的题目（《<a href="https://tonybai.com/2025/12/06/zootopia-distributed-system-written-in-go">如果〈疯狂动物城〉是一个分布式系统&#8230;</a>》）向大家“真诚致歉”吗？</p>
<p>当时，带着重温第一部的滤镜，我信誓旦旦地跟大家吹牛，说动物城简直就是 <strong>Go 语言构建的云原生架构典范</strong>——高效、隔离、完美。</p>
<p><strong>但这周六下午看完《疯狂动物城2》，我不得不承认：草率了，这次“打脸”来得太快。</strong></p>
<p>如果说第一部展示了架构师眼中的“理想国”，那么第二部则残忍地揭开了<strong>“完美架构”背后的谎言</strong>。</p>
<p>看着银幕上那条被大家畏惧、却掌握着关键线索的蛇（Gary），以及那个被冰雪掩埋的真相，我脊背发凉。这哪里是童话？这分明就是一部<strong>《大型遗留系统（Legacy System）维护血泪史》</strong>。</p>
<p>作为架构师，我在这部电影里看到了三个关于“新老技术”的扎心隐喻。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/google-adk-in-action-qr.png" alt="" /></p>
<h2>被埋葬的“爬行动物”：那些我们不敢碰的 Legacy Code</h2>
<p>在电影里，我们得知了一个惊天秘密：动物城引以为傲的“温控系统”和城市规划，并非现在的创始人（林雪猁, Lynxley）设计的，而是源自一位爬行动物——<strong>Agnes（Gary的曾祖母）</strong>。</p>
<p>但为了打造一个看似光鲜、只有可爱哺乳动物的“新城区”（Tundratown），管理者选择了<strong>掩盖历史</strong>。他们直接把爬行动物的家园（Reptile Ravine）埋在了厚厚的冰雪之下，假装它们从未存在。</p>
<p>这一幕，像极了我们对待<strong>“遗留代码（Legacy Code）”</strong>的态度。</p>
<p>在现代化的 Go 微服务、Kubernetes 集群（Tundratown）之下，往往深埋着一套跑了20年的、由 C/C++ 甚至 Fortran 编写的<strong>核心交易系统（爬行动物）</strong>。<br />
*   它们<strong>古老</strong>、<strong>丑陋</strong>（代码风格甚至没有缩进）；<br />
*   它们看起来<strong>危险</strong>（改一行代码可能崩全站，就像蛇会咬人）；<br />
*   所以，我们选择<strong>“封印”</strong>它。我们用一层又一层的 Wrapper、API 网关把它包裹起来，假装我们已经拥有了一个全新的、完美的系统。</p>
<p>但电影告诉我们：<strong>物理掩埋解决不了问题。</strong> 当危机来临，那些被忽视的底层逻辑，终将“破土而出”。</p>
<h2>Gary 的热感应：老技术独有的“超能力”</h2>
<p>电影里有一段非常精彩的情节：朱迪和尼克束手无策时，是蛇 Gary 利用响尾蛇特有的<strong>“热感应”</strong>能力，看透了迷雾，找到了线索。</p>
<p>这让我想起，每当新技术（如 AI、Web3）甚嚣尘上时，我们往往会轻视那些“老古董”。</p>
<ul>
<li>我们觉得 Go/Rust 这种现代语言无所不能。</li>
<li>我们觉得 C 语言指针复杂、汇编晦涩、SQL 存储过程老土。</li>
</ul>
<p>但真到了极端场景——比如需要极致的性能优化、极底层的硬件交互时，我们发现，还得靠那些“老家伙”。<strong>Gary 代表的，正是那些虽不时髦、但拥有独特“底层视角”的技术能力。</strong></p>
<p>正如 Go 语言之所以强大，不是因为它切断了过去，而是因为它通过 CGO、通过汇编支持，保留了与底层世界对话的能力。</p>
<p><strong>不要傲慢地认为新技术能替代一切。有时候，解开死锁的钥匙，藏在一行 10 年前写的 C 代码里。</strong></p>
<h2>创始人的日记：文档与“去伪存真”</h2>
<p>(以下内容涉及核心剧透)</p>
<p>电影的高潮，是朱迪必须找到 Agnes 留下的<strong>日记本</strong>和<strong>专利书</strong>，才能揭穿谎言，拯救城市。</p>
<p>这本日记，不就是我们梦寐以求的<strong>“核心架构文档”</strong>吗？</p>
<p>在很多大厂里，随着人员流动（老一辈架构师离职），系统的“设计初衷”往往丢失了。后来的维护者（Lynxley）为了 KPI，可能会歪曲系统原有的设计，堆砌不合理的“补丁”，甚至把系统改造成一个不可维护的怪兽。</p>
<p>朱迪和尼克的冒险，本质上是一次<strong>“考古式重构”</strong>：</p>
<ol>
<li><strong>阅读源码</strong>（寻找日记）；</li>
<li><strong>理解上下文</strong>（Agnes 的初衷是共存，而不是隔离）；</li>
<li><strong>修正架构</strong>（打破冰墙，让爬行动物回归）。</li>
</ol>
<p>这给所有 Go 开发者提了个醒：<strong>写代码时，请留下你的“日记”。</strong> 好的注释和文档，是连接过去与未来的纽带。不要让后来者通过“猜谜”来维护你的系统。</p>
<h2>写在最后</h2>
<p>电影结局，爬行动物回到了动物城，与哺乳动物和谐共处。</p>
<p>二宝问我：“爸爸，蛇和兔子真的能做朋友吗？”</p>
<p>我说：“能啊，只要它们互相尊重。”</p>
<p>技术世界也是如此。我们推崇 <strong>Go</strong> 的简洁、<strong>云原生</strong>的弹性，但这并不意味着我们要鄙视那些运行在角落里的<strong>单体应用</strong>或<strong>老旧语言</strong>。</p>
<p><strong>真正的“完美架构”，不是推倒重来（Rewrite），而是包容与演进（Evolve）。</strong></p>
<p>它能容得下时髦的微服务（朱迪），也能接纳古老的遗留系统（Gary）。它承认系统的复杂性，并用工程化的手段管理这种复杂，而不是掩耳盗铃。</p>
<p>走出影院，看着手里 2025 年的新技术，再想想公司里那堆跑了 10 年的老代码，我突然多了一份敬畏。</p>
<p><strong>原来，致敬历史，才是通往未来的捷径。</strong></p>
<hr />
<p><strong>互动话题：</strong></p>
<p><strong>在你的职业生涯中，有没有哪一次“挖坟”经历（维护极老的遗留代码），让你意外地学到了很多东西？或者，你有没有遇到过像 Gary 一样看似可怕、实则核心的“祖传代码”？</strong></p>
<p>欢迎在评论区分享你的“考古”故事！</p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2025, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2025/12/07/zootopia-2-perfect-architecture-lie-exposed/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>写出让同事赞不绝口的Go代码：Reddit工程师总结的10条地道Go编程法则</title>
		<link>https://tonybai.com/2025/10/21/10-go-programming-rules-from-reddit/</link>
		<comments>https://tonybai.com/2025/10/21/10-go-programming-rules-from-reddit/#comments</comments>
		<pubDate>Mon, 20 Oct 2025 23:55:36 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Abstraction]]></category>
		<category><![CDATA[Channel]]></category>
		<category><![CDATA[Codereview]]></category>
		<category><![CDATA[common]]></category>
		<category><![CDATA[DoubleReporting]]></category>
		<category><![CDATA[errgroup]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[Go代码]]></category>
		<category><![CDATA[Go编程法则]]></category>
		<category><![CDATA[GuardClauses]]></category>
		<category><![CDATA[happypath]]></category>
		<category><![CDATA[helpers]]></category>
		<category><![CDATA[HighCohesion]]></category>
		<category><![CDATA[iferrnil]]></category>
		<category><![CDATA[KonradReiche]]></category>
		<category><![CDATA[LGTM]]></category>
		<category><![CDATA[misc]]></category>
		<category><![CDATA[Mutex]]></category>
		<category><![CDATA[nil指针的解引用]]></category>
		<category><![CDATA[nil指针类型]]></category>
		<category><![CDATA[orchestration]]></category>
		<category><![CDATA[packageName.Identifier]]></category>
		<category><![CDATA[panic]]></category>
		<category><![CDATA[reddit]]></category>
		<category><![CDATA[ReturnEarly]]></category>
		<category><![CDATA[shadowing]]></category>
		<category><![CDATA[SilentlyDiscarding]]></category>
		<category><![CDATA[SilentlyIgnoring]]></category>
		<category><![CDATA[style]]></category>
		<category><![CDATA[SwallowingtheError]]></category>
		<category><![CDATA[sync.Mutex]]></category>
		<category><![CDATA[sync.WaitGroup]]></category>
		<category><![CDATA[util]]></category>
		<category><![CDATA[What]]></category>
		<category><![CDATA[Why]]></category>
		<category><![CDATA[上下文]]></category>
		<category><![CDATA[业务背景]]></category>
		<category><![CDATA[为测试而抽象]]></category>
		<category><![CDATA[作用域]]></category>
		<category><![CDATA[健壮性]]></category>
		<category><![CDATA[共享状态]]></category>
		<category><![CDATA[冗余]]></category>
		<category><![CDATA[协调]]></category>
		<category><![CDATA[单元测试]]></category>
		<category><![CDATA[变量名的长度]]></category>
		<category><![CDATA[变量声明]]></category>
		<category><![CDATA[变量阴影]]></category>
		<category><![CDATA[可维护性]]></category>
		<category><![CDATA[可读性]]></category>
		<category><![CDATA[吞噬错误]]></category>
		<category><![CDATA[命名]]></category>
		<category><![CDATA[大杂烩]]></category>
		<category><![CDATA[契约]]></category>
		<category><![CDATA[实现细节]]></category>
		<category><![CDATA[导出的API]]></category>
		<category><![CDATA[就近声明]]></category>
		<category><![CDATA[局部性]]></category>
		<category><![CDATA[工匠精神]]></category>
		<category><![CDATA[工程师]]></category>
		<category><![CDATA[心智负担]]></category>
		<category><![CDATA[快乐路径]]></category>
		<category><![CDATA[意图]]></category>
		<category><![CDATA[扁平化]]></category>
		<category><![CDATA[接口]]></category>
		<category><![CDATA[提前返回]]></category>
		<category><![CDATA[文档]]></category>
		<category><![CDATA[未校验的输入]]></category>
		<category><![CDATA[模棱两可的返回契约]]></category>
		<category><![CDATA[死锁]]></category>
		<category><![CDATA[注释]]></category>
		<category><![CDATA[演进]]></category>
		<category><![CDATA[生产环境之痛]]></category>
		<category><![CDATA[竞态问题]]></category>
		<category><![CDATA[组织声明]]></category>
		<category><![CDATA[编排]]></category>
		<category><![CDATA[缩进]]></category>
		<category><![CDATA[自顶向下]]></category>
		<category><![CDATA[过早抽象]]></category>
		<category><![CDATA[运行时Panic]]></category>
		<category><![CDATA[通信]]></category>
		<category><![CDATA[逻辑复杂度]]></category>
		<category><![CDATA[重复报告]]></category>
		<category><![CDATA[错误处理]]></category>
		<category><![CDATA[防卫语句]]></category>
		<category><![CDATA[防御性检查]]></category>
		<category><![CDATA[零信任]]></category>
		<category><![CDATA[静默丢弃]]></category>
		<category><![CDATA[静默忽略]]></category>
		<category><![CDATA[领域]]></category>
		<category><![CDATA[高内聚]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5279</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/10/21/10-go-programming-rules-from-reddit 大家好，我是Tony Bai。 在团队协作中，Code Review是我们与同事交流最频繁的阵地。我们都渴望自己提交的代码能够清晰、健壮，赢得同事的“LGTM”（Looks Good To Me）。但有时，一些看似“吹毛求疵”的风格评论，如“改下变量名”或“这里缩进不对”，会让我们感到困惑。 这些评论真的只是个人偏好吗？来自Reddit的工程师Konrad Reiche在其GoLab 2025的精彩分享《Writing Better Go》中给出了否定的答案。他一针见血地指出：大多数“风格(style)”评论，其本质并非关乎审美，而是关乎如何避免未来的生产环境之痛。 本文将和大家一起解读一下这场分享中提炼出的十条黄金法则。它们是Konrad从数百个Reddit的内部Pull Request中沉淀出的模式与智慧，内容涵盖了从错误处理的艺术、接口设计的哲学，到并发模式的选择、代码的组织与命名等方方面面。掌握它们，将帮助你写出真正让同事赞不绝口的地道Go代码，从根本上提升代码质量与团队协作效率。 法则 01：精准处理错误 Go的if err != nil是其哲学的核心，但如何正确地处理err，却是一门艺术。错误的错误处理方式，是生产环境中许多难以追踪的bug和panic的根源。这里Konrad列出的几种错误处理禁忌，都十分值得我们注意。 禁忌1：静默丢弃 (Silently Discarding) 这是最危险的行为，完全无视了函数可能失败的契约。 // BAD: Silently Discarding // pickRandom可能会因为输入为空而返回错误，但我们用 _ 彻底忽略了它。 // 如果发生错误，result将是其零值（空字符串），程序可能会在后续逻辑中以意想不到的方式失败。 result, _ := pickRandom(input) log.Printf("The random choice is: %s", result) 禁忌2：静默忽略 (Silently Ignoring) 比丢弃稍好，但同样危险。我们接收了错误，却没有做任何处理。 // BAD: Silently Ignoring [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/10-go-programming-rules-from-reddit-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2025/10/21/10-go-programming-rules-from-reddit">本文永久链接</a> &#8211; https://tonybai.com/2025/10/21/10-go-programming-rules-from-reddit</p>
<p>大家好，我是Tony Bai。</p>
<p>在团队协作中，Code Review是我们与同事交流最频繁的阵地。我们都渴望自己提交的代码能够清晰、健壮，赢得同事的“LGTM”（Looks Good To Me）。但有时，一些看似“吹毛求疵”的风格评论，如“改下变量名”或“这里缩进不对”，会让我们感到困惑。</p>
<p>这些评论真的只是个人偏好吗？来自Reddit的工程师Konrad Reiche在其GoLab 2025的精彩分享《<a href="https://speakerdeck.com/konradreiche/writing-better-go-lessons-from-10-code-reviews">Writing Better Go</a>》中给出了否定的答案。他一针见血地指出：<strong>大多数“风格(style)”评论，其本质并非关乎审美，而是关乎如何避免未来的生产环境之痛。</strong></p>
<p>本文将和大家一起解读一下这场分享中提炼出的<strong>十条黄金法则</strong>。它们是Konrad从数百个Reddit的内部Pull Request中沉淀出的模式与智慧，<strong>内容涵盖了从错误处理的艺术、接口设计的哲学，到并发模式的选择、代码的组织与命名等方方面面。</strong>掌握它们，将帮助你写出真正让同事赞不绝口的地道Go代码，从根本上提升代码质量与团队协作效率。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/go-network-programming-complete-guide-pr.png" alt="" /></p>
<h2>法则 01：精准处理错误</h2>
<p>Go的if err != nil是其哲学的核心，但如何正确地处理err，却是一门艺术。错误的错误处理方式，是生产环境中许多难以追踪的bug和panic的根源。这里Konrad列出的几种错误处理禁忌，都十分值得我们注意。</p>
<h3>禁忌1：静默丢弃 (Silently Discarding)</h3>
<p>这是最危险的行为，完全无视了函数可能失败的契约。</p>
<pre><code class="go">// BAD: Silently Discarding
// pickRandom可能会因为输入为空而返回错误，但我们用 _ 彻底忽略了它。
// 如果发生错误，result将是其零值（空字符串），程序可能会在后续逻辑中以意想不到的方式失败。
result, _ := pickRandom(input)
log.Printf("The random choice is: %s", result)
</code></pre>
<h3>禁忌2：静默忽略 (Silently Ignoring)</h3>
<p>比丢弃稍好，但同样危险。我们接收了错误，却没有做任何处理。</p>
<pre><code class="go">// BAD: Silently Ignoring
// 我们检查了err，但if语句块是空的。
// 程序会继续执行，仿佛错误从未发生，但result的值是不可信的。
result, err := pickRandom(input)
if err != nil {
    // An empty block is a sign of trouble.
}
log.Printf("The random choice is: %s", result)
</code></pre>
<h3>禁忌3：吞噬错误 (Swallowing the Error)</h3>
<p>这种模式在错误发生时，向上层调用者返回nil，彻底抹除了错误的痕迹。上层调用者无法知道操作是成功了，还是静默地失败了。</p>
<pre><code class="go">// BAD: Swallowing the Error
result, err := pickRandom(input)
if err != nil {
    return nil // 发生了错误，但我们却向上层返回了一个nil
}
</code></pre>
<h3>禁忌4：重复报告 (Double Reporting)</h3>
<p>一个经典的错误是在一个地方记录日志，然后又将err返回给上层，导致调用链中多处重复记录同一个错误。这会严重干扰日志分析和告警系统。</p>
<pre><code class="go">// BAD: Double Reporting
func process() error {
    result, err := pickRandom(input)
    if err != nil {
        // 在这里记录了日志...
        slog.Error("pickRandom failed", "error", err)
        // ...然后又将错误返回
        return err
    }
    // ...
    return nil
}

func main() {
    if err := process(); err != nil {
        // 调用方又记录了一次日志！
        slog.Error("process failed", "error", err)
    }
}
</code></pre>
<p><strong>原则：在一个调用层级，要么处理错误，要么将错误返回给上层去处理，但最好不要两者都做。</strong> 通常，只有在程序的最高层（如main函数或HTTP handler）才应该记录日志。</p>
<p>以上的这些“禁忌”虽然糟糕，但通常只会导致逻辑错误或日志混乱。而接下来的这个模式，则会直接导致程序崩溃（panic）。</p>
<h3>最危险的坏味道：模棱两可的返回契约</h3>
<p>这种模式发生在：一个函数在返回非nil错误的同时，也返回了一个<strong>非nil的指针类型</strong>的值。</p>
<pre><code class="go">// http.DefaultClient.Do 的文档明确说明，当发生某些错误时（如重定向错误），
// 它会同时返回一个非nil的*http.Response和一个非nil的error。
// 这是一个经过深思熟虑并有文档说明的特例。
//
// 但在绝大多数我们自己编写的代码中，这种模式是极其危险的。

func fetch(req *http.Request) (*http.Response, error) {
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        // 危险！在这里，resp可能是一个非nil的指针，指向一个部分有效或无效的Response。
        // 如果我们直接将它返回...
        return resp, err
    }
    return resp, nil
}

func main() {
    invalid := &amp;http.Request{} // 一个无效的请求
    resp, err := fetch(invalid)
    if err != nil {
        slog.Error("fetch failed", "error", err)

        // 调用者在这里陷入了两难：
        // 1. 我应该信任err，并认为resp是无效的吗？
        // 2. 还是应该检查一下resp是否为nil？

        // 如果调用者不假思索地访问resp...
        slog.Info(resp.Status) // &lt;-- PANIC!
        // 将会引发: panic: runtime error: invalid memory address or nil pointer dereference
    }
}
</code></pre>
<p><strong>问题的根源在于</strong>，这个fetch函数建立了一个<strong>模棱两可的契约</strong>。当调用方收到一个非nil的err时，它无法安全地假设另一个返回值resp的状态。如果调用者没有进行额外的nil检查就直接访问resp.Status，程序就会因为空指针解引用而崩溃。</p>
<p>一个健壮的、地道的Go函数，应该为调用者提供一个清晰无比的契约，消除所有猜测：</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/10-go-programming-rules-from-reddit-2.png" alt="" /></p>
<p>按照上述实践，我们的fetch函数修改为：</p>
<pre><code class="go">func fetch(req *http.Request) (*http.Response, error) {
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        // 无论resp此时是什么，我们都返回nil，建立清晰的契约
        return nil, err
    }
    return resp, nil
}
</code></pre>
<p>通过始终返回nil, err，调用者可以极大地简化其错误处理逻辑，放心地编写代码：</p>
<pre><code class="go">resp, err := fetch(invalid)
if err != nil {
    slog.Error("fetch failed", "error", err)
    // 在这个分支里，我们100%确定resp是nil，无需再做任何检查。
    return
}
// 只有在err为nil时，才安全地访问resp。
slog.Info(resp.Status)
</code></pre>
<p>这不仅避免了panic，更重要的是，它降低了代码的认知负荷，让程序变得更简单、更可预测。这，就是地道的Go错误处理之道。</p>
<h2>法则 02：不要过早添加接口</h2>
<p>在Go的世界里，“接口”是一个极其强大的工具，但它也极易被滥用，成为过度设计的重灾区。演讲者指出了两种最常见的接口误用场景：<strong>过早抽象</strong>和<strong>为测试而抽象</strong>。</p>
<p><strong>过早抽象</strong>通常源于开发者从Java等传统面向对象语言带来的思维惯性。在那些语言中，“面向接口编程”是金科玉律，导致开发者习惯于在编写任何具体实现之前，先定义一个接口。例如，在构建一个缓存包时，开发者可能会立刻定义一个Cache接口，并随之创建LFU和LRU等多种实现。</p>
<pre><code class="go">// cache/cache.go
package cache

// 过早定义的接口
type Cache[T any] interface {
    Get(ctx context.Context, key string) (*T, error)
    Set(ctx context.Context, key string, value T) error
}

// LFU 实现...
type LFU[T any] struct { /* ... */ }
// LRU 实现...
type LRU[T any] struct { /* ... */ }
</code></pre>
<p>然后，在服务的代码中直接依赖这个cache.Cache接口：</p>
<pre><code class="go">type EligibilityService struct {
    cache   cache.Cache[model.Product] // 依赖于接口
    catalog *product.Catalog
}
</code></pre>
<p>这种做法的问题在于，它在<strong>需求尚不明确</strong>的时候，就引入了一个额外的抽象层。如果你的项目在可预见的未来都只需要一种缓存实现（比如LFU），那么这个Cache接口就是多余的。它不仅没有带来任何好处，反而增加了代码的间接性，使得跳转定义和理解代码变得更加困难。</p>
<p><strong>Go的哲学恰恰相反：先从具体类型开始。</strong> 应该直接依赖*cache.LFU：</p>
<pre><code class="go">type EligibilityService struct {
    cache   *cache.LFU[model.Product] // 直接依赖具体类型
    catalog *product.Catalog
}
</code></pre>
<p>只有当未来你<strong>真正需要多种可互换的实现</strong>时（例如，需要根据配置在LFU和LRU之间切换），再回头去提取一个接口也不迟。这个原则可以用一个简单的“试金石”来检验：<strong>如果你能不写接口就实现功能，那你可能根本就不需要那个接口。</strong></p>
<p><strong>为测试而抽象</strong>是Go中最常见的接口滥用反模式。为了在单元测试中mock一个依赖（比如UserService），开发者常常会为其创建一个接口，仅仅是为了让测试代码能够传入一个mockUserService。这种做法虽然在短期内解决了测试问题，但却用一个“测试的便利性”污染了生产代码的设计，得不偿失。</p>
<p>更地道的做法是<a href="https://tonybai.com/2023/04/20/provide-fake-object-for-external-collaborators">优先使用真实实现的测试替身</a>，例如使用google.golang.org/grpc/test/bufconn来测试gRPC服务，而不是为每个gRPC客户端都定义一个接口。</p>
<h2>法则 03：优先使用Mutex，Channel用于编排</h2>
<p>“Channel很聪明。但在生产环境中，更简单的往往更安全。” 这句话精准地概括了Go并发编程中的一个核心权衡。Go的并发哲学常被新手误解为“无脑用Channel”，但资深的Gopher都明白，对于<strong>保护共享状态</strong>这一最常见的并发场景，sync.Mutex通常是更简单、更安全、性能也更易于推理的选择。</p>
<p>Channel的强大之处在于其<strong>协调和通信</strong>的能力，但这份强大也伴随着复杂性。演讲中列举了多种由Channel引发的panic或死锁场景，例如<strong>关闭一个已关闭的channel</strong>、<strong>向一个已关闭的channel发送数据</strong>、或者<strong>在一个无缓冲的channel上发送但没有接收者</strong>。这些运行时错误提醒我们，Channel的生命周期和goroutine之间的同步需要精心管理。</p>
<p>一个典型的过度使用Channel的例子，是将一个简单的并发处理任务，构建成一个由多个goroutine、多个channel、select和sync.WaitGroup构成的复杂扇出/扇入（fan-out/fan-in）模式。虽然这种模式在功能上是可行的，但其心智负担远高于一个使用互斥锁的简单替代方案。</p>
<pre><code class="go">// 使用Mutex的简单、安全的并发模式
var mu sync.Mutex
resps := make([]int, 0)

g, ctx := errgroup.WithContext(ctx)
for _, v := range input {
    v := v // capture loop variable
    g.Go(func() error {
        resp, err := process(ctx, v)
        if err != nil {
            return err
        }
        mu.Lock()
        resps = append(resps, resp)
        mu.Unlock()
        return nil
    })
}
if err := g.Wait(); err != nil {
    return 0, err
}
return merge(resps...), nil
</code></pre>
<p>在这个例子中，我们使用errgroup来管理goroutine的生命周期和错误传递，并用一个简单的sync.Mutex来保护对共享切片resps的并发写入。这个模式清晰、直接，并且通过go test -race可以轻松检测出潜在的竞态问题。</p>
<p>因此，<strong>最佳实践的演进路径应该是</strong>：</p>
<ol>
<li><strong>默认从同步代码开始。</strong></li>
<li><strong>只有当性能分析（profiling）显示出明确的瓶颈时</strong>，才引入goroutine进行并发优化。</li>
<li>对于简单的共享状态保护，<strong>优先使用sync.Mutex和sync.WaitGroup</strong>。</li>
<li>当且仅当你的问题涉及到<strong>复杂的、需要协调多个goroutine执行流程的“编排”（orchestration）</strong>场景时，比如任务分发、信号传递、流式处理或实现CSP模式时，Channel才是那个更闪耀的工具。</li>
</ol>
<h2>法则 04：就近声明</h2>
<p>代码的物理位置，深刻地影响着其可读性和可维护性。一个普遍的原则是：<strong>代码和它所操作的数据，应该尽可能地放在一起</strong>。</p>
<p>这个原则贯穿了从包到函数再到代码块的每一个层面。在函数内部，最能体现这一点的就是<strong>变量声明的位置</strong>。许多来自C等旧语言的开发者，习惯在函数顶部声明所有将要用到的变量。</p>
<pre><code class="go">// BAD: 变量声明远离其使用位置
func fetch(auth auth, client Client, queries []string) ([]string, error) {
    var results []string
    var err error
    var authErr error // authErr的作用域贯穿整个函数

    if auth != nil {
        authErr = auth(func() error {
            results, err = client.PostSearch(queries)
            return err
        })
        if authErr != nil {
            return nil, authErr // 容易出错：这里应该返回authErr还是err?
        }
    } else {
        results, err = client.PostSearch(queries)
        if err != nil {
            return nil, err
        }
    }
    return results, nil
}
</code></pre>
<p>这种做法不仅让变量的作用域被人为地拉长，增加了阅读者追踪变量状态的心智负担，还可能引入微妙的bug，如上面代码中authErr和err的混淆。</p>
<p><strong>地道的Go代码，应该在尽可能靠近其首次使用的地方声明变量。</strong> 这不仅使代码更紧凑，更重要的是<strong>最小化了变量的作用域</strong>，减少了变量阴影（shadowing）等潜在问题的发生概率。Go的if err := &#8230;; err != nil短声明，正是这一原则的最佳体现。</p>
<p>重构后的fetch函数如下：</p>
<pre><code class="go">// GOOD: 变量在需要时才声明，作用域被最小化
func fetch(auth auth, client Client, queries []string) ([]string, error) {
    if auth != nil {
        var results []string
        // err只在if块内有效
        if err := auth(func() (err error) {
            results, err = client.PostSearch(queries)
            return err
        }); err != nil {
            return nil, err
        }
        return results, nil
    }
    // 如果没有auth，直接调用并返回
    return client.PostSearch(queries)
}
</code></pre>
<p>通过将变量声明移入它们所属的逻辑块，代码不仅变得更短，逻辑也更加清晰和安全。这种对作用域的精细控制，是编写可维护Go代码的一项核心技能。</p>
<h2>法则 05：避免运行时Panic</h2>
<p>在Go中，错误是预期的、可处理的程序行为，而panic则代表了不可恢复的、灾难性的程序错误。因此，编写健壮的代码，一个核心原则就是<strong>主动避免可预见的运行时panic</strong>。panic最常见的来源有两个：<strong>未校验的输入</strong>和<strong>对nil指针的解引用</strong>。</p>
<p>对于<strong>来自系统边界之外的输入</strong>，我们必须抱以“零信任”的态度。无论是来自外部的API请求、数据库的查询结果，还是从配置文件读取的数据，都应该在使用前进行严格的校验。</p>
<pre><code class="go">// BAD: 盲目信任输入，可能导致panic
func selectNotifications(req *pb.Request) {
    // 如果 req.Options 为 nil，这里会 panic
    max := req.Options.MaxNotifications
    // 如果 max 大于 req.Notifications 的长度，这里会 panic
    req.Notifications = req.Notifications[:max]
}

// GOOD: 在使用前进行防御性检查
func selectNotifications(req *pb.Request) {
    if req == nil || req.Options == nil {
        return
    }
    max := req.Options.MaxNotifications
    if len(req.Notifications) &gt; int(max) {
        req.Notifications = req.Notifications[:max]
    }
}
</code></pre>
<p><strong>对nil指针的解引用</strong>是另一个常见的panic来源，尤其是在处理JSON反序列化或Protobuf消息这类包含可选字段的场景。</p>
<pre><code class="go">type FeedItem struct {
    Score *float64 json:"score" // score可能为nil
}

// BAD: 如果item.Score是nil, 对其解引用会立即引发panic
func sumFeedScores(feed *Feed) float64 {
    var total float64
    for _, item := range feed.Items {
        total += *item.Score
    }
    return total
}
</code></pre>
<p>最佳的防御策略并非仅仅是在解引用前添加if item.Score != nil检查。更根本的解决方案是<strong>通过设计来消除nil的可能性</strong>。如果业务逻辑中Score字段不应为空，那么在定义FeedItem时就应该使用值类型float64而不是指针类型*float64。这从类型层面就杜绝了nil指针panic的发生，将潜在的运行时错误，提升为了编译期或反序列化时的明确错误，这正是Go强类型系统优势的体现。</p>
<h2>法则 06：最小化缩进</h2>
<p>代码的缩进层级，是其逻辑复杂度的最直观体现。深层嵌套的if-else结构，就像一条蜿蜒曲折的隧道，让代码的阅读者极易迷失方向，难以理清核心的“快乐路径”（happy path）。</p>
<p>一个典型的“坏味道”是将所有核心逻辑都包裹在层层if-else的“金字塔”之中：</p>
<pre><code class="go">// BAD: 逻辑嵌套在if-else金字塔中，难以阅读
func processRequest() error {
    if err := doSomething(); err == nil {
        if ok := check(); ok {
            // ... 核心业务逻辑在这里 ...
            process()
            return nil
        } else {
            return errors.New("check failed")
        }
    } else {
        return err
    }
}
</code></pre>
<p>在这段代码中，为了找到真正的核心逻辑process()，我们的视线需要穿透两个if层级。</p>
<p><strong>地道的Go代码，推崇使用“防卫语句”（Guard Clauses）和“提前返回”（Return Early）的模式来保持代码结构的扁平化。</strong> 这意味着在函数的开头，就优先处理掉所有的错误情况和边界条件，让函数的“快乐路径”代码能够保持在最左侧，不带任何缩进。</p>
<p>重构后的代码如下：</p>
<pre><code class="go">// GOOD: 优先处理错误，保持核心逻辑的扁平化
func processRequest() error {
    if err := doSomething(); err != nil {
        return err
    }
    if !check() {
        return errors.New("check failed")
    }
    // ... 核心业务逻辑在这里，清晰可见 ...
    process()
    return nil
}
</code></pre>
<p>这种风格不仅让代码的可读性大大提高，也使得每个逻辑分支更加独立，易于测试和维护。当你发现自己的函数主体被if包裹时，就应该警惕，思考是否能通过反转判断条件和提前返回，来“拉平”你的代码。</p>
<h2>法则 07：避免“大杂烩”包和文件</h2>
<p>util、common、helpers、misc…… 在许多代码库中，我们都能看到这些命名模糊的包和文件。它们如同厨房里的“杂物抽屉”，堆满了各种看似有用但彼此无关的工具函数、常量和类型。演讲者引用时尚大师Karl Lagerfeld的名言，并戏仿道：</p>
<blockquote>
<p>“Util packages are a sign of defeat. You lost control of your code base, so you created some util packages.”<br />
  （Util包是失败的标志。你对自己的代码库失去了控制，所以你创建了一些util包。）</p>
</blockquote>
<p>这种做法的根本问题在于，它遵循的是<strong>按“类型”而非“功能”或“领域”来组织代码</strong>。一个处理用户字符串的函数，和一个处理订单字符串的函数，可能仅仅因为它们都“操作字符串”，就被丢进了同一个util包。</p>
<p>这破坏了软件设计中最重要的原则之一：<strong>高内聚（High Cohesion）</strong>。代码应该和它所影响的东西、和它所属的业务领域，紧密地放在一起。一个user包应该包含所有与用户直接相关的逻辑，一个order包则应该包含所有订单的逻辑。当user包需要一个字符串处理函数时，它应该被定义在user包内部的一个私有函数，或者一个user/stringutil子包中，而不是被“流放”到一个遥远的、通用的util包。</p>
<p><strong>最佳实践是：</strong></p>
<ul>
<li><strong>按领域或功能来组织和命名你的包。</strong> 包名应具有描述性，反映其业务职责，如http, user, order。</li>
<li><strong>追求代码的局部性。</strong> 如果一个辅助函数只被user包使用，那它就应该留在user包里。只有当它被多个<strong>不同领域</strong>的包共享时，才考虑将其提取到一个真正可复用的、具有明确职责的公共包中。</li>
</ul>
<p>避免创建“杂物抽屉”，能迫使我们更深入地思考代码的结构和归属，从而构建出内聚性更强、更易于理解和维护的系统。</p>
<h2>法则 08：按重要性组织声明</h2>
<p>Go语言有一个便利的特性：函数在调用前无需预先声明。这与C/C++等语言不同，让我们可以更自由地组织代码。然而，这份自由并不意味着声明的顺序无关紧要。恰恰相反，一个经过深思熟虑的文件布局，是提升代码可读性的关键所在。</p>
<p><strong>地道的Go代码，其文件组织方式应该像一篇写得很好的文章：最重要的信息在前，实现细节在后。</strong> 读者在打开一个.go文件时，应该能以“自顶向下”的方式，快速理解这个文件的核心职责和对外暴露的API。</p>
<p>因此，一个通用的最佳实践是，将<strong>导出的、面向API的函数放在文件顶部</strong>。它们是一个包的“门面”，是其他包与本包交互的入口。紧随其后的，才应该是那些作为实现细节的、未导出的私有辅助函数。</p>
<pre><code class="go">// GOOD: 导出的API函数在前，实现细节在后
package strings

// Trim 是这个包的核心API之一，放在最前面
func Trim(s, cutset string) string {
    // ...
    return trimLeftUnicode(trimRightUnicode(s, cutset), cutset)
}

// trimLeftUnicode 和 trimRightUnicode 是实现细节，放在后面
func trimLeftUnicode(s, cutset string) string { /* ... */ }
func trimRightUnicode(s, cutset string) string { /* ... */ }
</code></pre>
<p>这种“按重要性，而非按字母顺序或依赖关系”的排序原则，也同样适用于测试文件。我们应该将核心的测试用例函数（TestXxx）放在文件的前部，而将用于辅助测试的mock结构体或帮助函数放在文件的后部。这能让其他开发者在审查或修改测试时，第一时间就抓住测试的核心意图，而不是被大段的辅助代码分散注意力。</p>
<h2>法则 09：精心命名</h2>
<p>“计算机科学中只有两件难事：缓存失效和命名。” 这句古老的谚语至今仍然适用。命名不仅是一门艺术，更是深刻影响代码可读性的核心技能。</p>
<p>在Go中，一个常见的“坏味道”是在变量名中添加其类型作为后缀，例如userMap、idStr或injectFn。Go是一门静态类型语言，编译器和IDE都能明确地告诉我们每个变量的类型。在名称中重复这些信息是冗余的，它让名称描述的是<strong>“它是什么”</strong>，而不是<strong>“它包含了什么”</strong>。</p>
<p><strong>一个好的变量名，应该描述其内容或用途，而非其类型。</strong></p>
<pre><code class="go">// BAD: 名称包含了类型信息
var userMap map[string]*User
var idStr string

// GOOD: 名称描述了内容和用途
var usersByID map[string]*User // 清楚地表明这是一个通过ID索引用户的map
var id string                // 简洁明了
</code></pre>
<p>另一个与命名相关的地道实践，是<strong>变量名的长度应与其作用域成反比</strong>。在一个仅有几行代码的for循环中，使用i、k、v这样的单字母变量是完全可以接受且非常常见的，因为它们的作用域极小，读者一眼就能看明白。</p>
<p>但对于一个在整个函数中都有效的变量，或者一个包级别的变量，就应该使用更具描述性的、完整的名称，以降低读者的认知负含。</p>
<p>最后，在为包和导出的标识符命名时，要时刻<strong>思考它们在调用点的可读性</strong>。Go的代码在调用时总是以packageName.Identifier的形式出现。因此，好的命名会利用这个上下文来避免重复。例如，consumer.NewHandler就比consumer.NewConsumerHandler更简洁、更地道，因为consumer这个包名已经提供了足够的上下文。</p>
<h2>法则 10：为“Why”写文档，而不是“What”</h2>
<p>代码本身就能清晰地告诉你它“做了什么”（What）。一行a := b + c的代码，任何有基础的程序员都能看懂。因此，一条注释如果只是在复述这行代码的功能，例如// a equals b plus c，那它就是毫无价值的噪音。</p>
<p><strong>注释和文档的真正价值，在于解释代码存在的“为什么”（Why）。</strong> 它们应该为未来的读者（通常就是几个月后的你自己）提供那些无法从代码本身直接看出的、宝贵的上下文。</p>
<p>设想一下这个函数：</p>
<pre><code class="go">// BAD: 这条注释只是在复述代码的功能，没有提供任何额外信息
// Escapes internal double quotes by replacing " with \".
func EscapeDoubleQuotes(s string) string {
    if strings.HasPrefix(s, ") &amp;&amp; strings.HasSuffix(s, ") {
        core := strings.TrimPrefix(strings.TrimSuffix(s, "), ")
        escaped := strings.ReplaceAll(core, ", \")
        // ...
        return fmt.Sprintf("%s", escaped)
    }
    return s
}
</code></pre>
<p>这段代码的逻辑有些奇怪，读者会困惑于“为什么要做这么复杂的操作？”。现在，我们来看一个更好的注释：</p>
<pre><code class="go">// GOOD: 这条注释解释了这段代码存在的“为什么”，为读者提供了关键的业务背景
// We can sometimes receive a label like ""How-To"" because the frontend
// wraps user-provided labels in quotes, even when the value itself
// contains literal " characters. In this case, we attempt to escape all
// internal double quotes, leaving only the outermost ones unescaped.
func EscapeDoubleQuotes(s string) string {
    // ...
}
</code></pre>
<p>有了这段注释，任何未来的维护者都能立刻理解这段代码的意图和它所要处理的特殊边界情况。无论是代码中的注释，还是Pull Request的描述，我们的核心目标都应该是<strong>沟通意图，而非机械地描述行为</strong>。读者通常能看懂代码在做什么，但他们真正挣扎的，是理解当初为什么要这么写。</p>
<h2>小结：成为一名值得信赖的Go工匠</h2>
<p>从错误处理的契约清晰度，到接口使用的审慎时机；从Mutex与Channel的选择哲学，到代码组织的局部性原则；再到对命名、缩进和文档意图的精雕细琢——这十条法则，共同描绘出了一位成熟Go工程师的画像。</p>
<p>通过Konrad Reiche的分享，我们得以清晰地看到，那些在Code Review中反复出现的“风格”问题，其背后往往并非个人偏好之争，而是关乎<strong>可维护性、可读性和长期健壮性</strong>的深刻工程考量。它们的核心目标并非追求代码的完美或审美上的愉悦。</p>
<p><strong>它们的唯一目的，是减少未来团队协作中的摩擦</strong>——为未来的代码阅读者、维护者，以及几个月后的你自己，减少理解、修改和调试代码时的痛苦。一份清晰、健壮、易于维护的代码，正是同事们最希望看到的，也是最能体现你专业素养和“工匠精神”的“名片”。</p>
<p>每一个看似“吹毛求疵”的建议，最终都指向了同一个目标：<strong>让代码变得显而易见、本质安全、且易于演进。</strong></p>
<p>Code Review的真正意义，也正在于此。它不仅是保证当前功能交付安全的流程，更是整个团队共同学习、传授经验、建立统一技术直觉和品味的宝贵熔炉。当你开始在CR中给出或收到这类有深度的评论，并能理解其背后的“Why”时，你就走在了成为一名值得同事信赖、能够写出传世代码的Go工匠的正确道路上。</p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p><strong>想系统学习Go，构建扎实的知识体系？</strong></p>
<p>我的新书《<a href="https://book.douban.com/subject/37499496/">Go语言第一课</a>》是你的首选。源自2.4万人好评的极客时间专栏，内容全面升级，同步至Go 1.24。首发期有专属五折优惠，不到40元即可入手，扫码即可拥有这本300页的Go语言入门宝典，即刻开启你的Go语言高效学习之旅！</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/go-primer-published-4.png" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2025, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2025/10/21/10-go-programming-rules-from-reddit/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Dave Cheney 复出首谈：那些我反复强调的Go编程模式</title>
		<link>https://tonybai.com/2025/09/17/some-things-i-keep-repeating-about-go/</link>
		<comments>https://tonybai.com/2025/09/17/some-things-i-keep-repeating-about-go/#comments</comments>
		<pubDate>Wed, 17 Sep 2025 00:36:26 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[DaveCheney]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GopherConEurope]]></category>
		<category><![CDATA[Go语言]]></category>
		<category><![CDATA[if]]></category>
		<category><![CDATA[internal]]></category>
		<category><![CDATA[main]]></category>
		<category><![CDATA[main.run模式]]></category>
		<category><![CDATA[Naming]]></category>
		<category><![CDATA[switch]]></category>
		<category><![CDATA[一致性约定]]></category>
		<category><![CDATA[代码异味]]></category>
		<category><![CDATA[假装不可变性]]></category>
		<category><![CDATA[内部包]]></category>
		<category><![CDATA[初始化]]></category>
		<category><![CDATA[匿名结构体]]></category>
		<category><![CDATA[命名]]></category>
		<category><![CDATA[声明]]></category>
		<category><![CDATA[思维]]></category>
		<category><![CDATA[标识符]]></category>
		<category><![CDATA[注释]]></category>
		<category><![CDATA[流程控制]]></category>
		<category><![CDATA[短标识符]]></category>
		<category><![CDATA[编程模式]]></category>
		<category><![CDATA[萨丕尔]]></category>
		<category><![CDATA[萨丕尔-沃尔夫假说]]></category>
		<category><![CDATA[认知负荷]]></category>
		<category><![CDATA[语句]]></category>
		<category><![CDATA[语言]]></category>
		<category><![CDATA[辅助函数]]></category>
		<category><![CDATA[辅助函数价值]]></category>
		<category><![CDATA[进阶课]]></category>
		<category><![CDATA[长标识符]]></category>
		<category><![CDATA[防卫语句]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=5171</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/09/17/some-things-i-keep-repeating-about-go 大家好，我是Tony Bai。 在阔别公众视野数年后，Go 社区的传奇人物 Dave Cheney 终于重返 GopherCon Europe 的舞台，发表了一场备受瞩目的复出首谈(注：我印象中的回归首谈^_^)。这场题为《那些我反复强调的 Go 编程之事》的演讲，没有追逐时髦的技术热点，而是选择回归编程的本源，分享了他十五年 Go 编程生涯中，那些被反复实践、验证并沉淀下来的核心理念。 本文将和大家一起深入解读这场演讲的三大核心支柱：命名、初始化与流程控制、以及辅助函数，并探讨为何这些看似简单的模式，却是编写可读、可维护、可测试 Go 代码的基石。 引言：一位 Go “哲人”的回归与沉淀 对于许多 Go 开发者而言，Dave Cheney 的名字不仅代表着一位高产的贡献者，更像是一位编程哲学的布道者。在他“消失”的几年里，社区依旧在流传和实践着他提出的诸多模式。因此，当他重返 GopherCon Europe 2025的舞台时，整个社区都在好奇：他反复强调的那些 Go 编程理念，变了吗？ 答案既是“没有”，也是“更加深刻了”。 正如他在开场时所言，这次演讲是他对自己为多家公司编写了超过十年 Go 代码的经验总结，是他对 Peter Bourgon 经典演讲《Ways to do things》的致敬，更是一次对他自己编程风格的提纯与升华。他所分享的，正是那些在无数次代码审查、项目重构和生产救火中，被他反复提及、反复实践的编程模式。这些“重复之事”，构成了他编程哲学的坚实内核。 支柱一：命名 —— 程序的灵魂与第一印象 “我们应该执着地、狂热地关注程序中使用的每一个名字。” 演讲开篇，Dave 便直指编程的核心——命名。它涵盖了变量、常量、包、类型、方法和函数，是代码清晰度的源头。 告别“短标识符”的圣战 对于 Go 社区经久不衰的“短标识符 vs. 长标识符”之争，Dave [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/some-things-i-keep-repeating-about-go-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2025/09/17/some-things-i-keep-repeating-about-go">本文永久链接</a> &#8211; https://tonybai.com/2025/09/17/some-things-i-keep-repeating-about-go</p>
<p>大家好，我是Tony Bai。</p>
<p>在阔别公众视野数年后，Go 社区的传奇人物 <strong>Dave Cheney</strong> 终于重返 GopherCon Europe 的舞台，发表了一场备受瞩目的<strong>复出首谈</strong>(注：我印象中的回归首谈^_^)。这场题为《<a href="https://www.youtube.com/watch?v=RZe8ojn7goo">那些我反复强调的 Go 编程之事</a>》的演讲，没有追逐时髦的技术热点，而是选择回归编程的本源，分享了他十五年 Go 编程生涯中，那些被反复实践、验证并沉淀下来的核心理念。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/some-things-i-keep-repeating-about-go-2.png" alt="" /></p>
<p>本文将和大家一起深入解读这场演讲的三大核心支柱：<strong>命名、初始化与流程控制、以及辅助函数</strong>，并探讨为何这些看似简单的模式，却是编写可读、可维护、可测试 Go 代码的基石。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/go-micro-column-2025-pr.png" alt="" /></p>
<h2>引言：一位 Go “哲人”的回归与沉淀</h2>
<p>对于许多 Go 开发者而言，Dave Cheney 的名字不仅代表着一位高产的贡献者，更像是一位编程哲学的布道者。在他“消失”的几年里，社区依旧在流传和实践着他提出的诸多模式。因此，当他重返 GopherCon Europe 2025的舞台时，整个社区都在好奇：<strong>他反复强调的那些 Go 编程理念，变了吗？</strong></p>
<p>答案既是“没有”，也是“更加深刻了”。</p>
<p>正如他在开场时所言，这次演讲是他对自己为多家公司编写了超过十年 Go 代码的经验总结，是他对 Peter Bourgon 经典演讲《<a href="https://www.youtube.com/watch?v=LHe1Cb_Ud_M">Ways to do things</a>》的致敬，更是一次对他自己编程风格的提纯与升华。他所分享的，正是那些在无数次代码审查、项目重构和生产救火中，被他反复提及、反复实践的编程模式。这些“重复之事”，构成了他编程哲学的坚实内核。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/some-things-i-keep-repeating-about-go-3.png" alt="" /></p>
<h2>支柱一：命名 —— 程序的灵魂与第一印象</h2>
<p>“我们应该执着地、狂热地关注程序中使用的每一个名字。” 演讲开篇，Dave 便直指编程的核心——<strong>命名</strong>。它涵盖了变量、常量、包、类型、方法和函数，是代码清晰度的源头。</p>
<h3>告别“短标识符”的圣战</h3>
<p>对于 Go 社区经久不衰的“短标识符 vs. 长标识符”之争，Dave 引用了 Andrew Gerrand 的智慧之言，并将其作为命名第一法则：</p>
<blockquote>
<p>“最好的标识符，是能够描述其存在理由的最短的那个。”</p>
</blockquote>
<p>这意味着，名称的长度应与其生命周期和作用域成正比。一个只活几行的循环变量用 i 即可，而一个贯穿整个包的重要配置，则需要一个描述性的全名。</p>
<h3>重要的部分放前面</h3>
<p>“你不是在写惊悚小说”，Dave 强调，标识符中最重要的、最独特的部分应该放在前面，而不是让读者猜到最后。特别是在同一作用域内有多个同类事物时，清晰的前缀至关重要。</p>
<h3>包内外视角的二元性与一致性约定</h3>
<p>一个常见的问题是，包的作者和消费者对“好名字”的看法不同。在包内，request 可能是一个合理的变量名；但在包外，它变成了 completion.Request，显得冗长。</p>
<p>Dave 提出的解决方案是<strong>建立一致的缩写约定</strong>：</p>
<ol>
<li><strong>全局约定</strong>：例如，req 永远指代 *http.Request。</li>
<li><strong>包内约定</strong>：对于 completion.Request，在包内统一使用一个独特的缩写，如 creq，并将其用作<strong>接收器名、参数名和局部变量名</strong>。</li>
</ol>
<pre><code class="go">// 外部调用
func DoSomething(creq *completion.Request) {}

// 包内实现
func (creq *Request) Do() {}
</code></pre>
<p>这样，无论读者身处包内还是包外，creq 这个标识符的含义都是稳定且可预测的。</p>
<h3>让名字代替注释</h3>
<p>Dave 引用了 Kate Gregory 在《Beautiful C++》中的观点：如果你能给一个标识符起一个足够好的名字，你可能就不需要为它写注释了。一个名字本身就应该能自我解释。</p>
<p>他举了一个反例：一个名为 Validate 的函数，却没有返回 error。这本身就是一个“代码异味”（code smell），即便加上注释 // Validate validates the graph，这种“说了两遍”的重复也无法掩盖其名不副实的问题。经过检查，这个函数实际做的是“扁平化图节点”，一个更准确的名字 FlattenNodes 就能让注释变得多余。</p>
<h3>有时，最好的名字就是没有名字</h3>
<p>对于那些生命周期极短、仅用于临时数据传递的类型，最好的名字可能就是<strong>没有名字</strong>。例如，在处理 HTTP 请求时，如果需要先将 JSON 解码到一个中间结构体再进行验证，完全可以使用<strong>匿名结构体</strong>。</p>
<pre><code class="go">var payload struct {
    Name string json:"name"
    // ...
}
if err := json.NewDecoder(r.Body).Decode(&amp;payload); err != nil {
    // ...
}
</code></pre>
<p>为这个只在此函数中存活一次的类型绞尽脑汁想一个名字（如 requestClient），是完全不必要的认知负担。</p>
<h2>支柱二：初始化与流程控制 —— 对 if-else 的厌恶</h2>
<p>“if 很糟糕，else 更糟糕”，这是 Dave 对流程控制的核心观点。他认为，我们应该尽一切努力减少甚至消除代码中的 if-else 结构，尤其是那些用于延迟初始化的模式。</p>
<h3>假装 Go 拥有不可变性：一次且仅一次的初始化</h3>
<p>一个常见的反模式是“声明-后初始化”：</p>
<pre><code class="go">var thing Thing
if os.Getenv("ENV") == "prod" {
    thing = NewRealThing()
} else {
    thing = NewMockThing()
}
</code></pre>
<p>这不仅创造了一个 thing 未被初始化的“危险”中间状态，也增加了代码的认知负荷。Dave 的解决方案是：<strong>默认初始化，然后覆盖</strong>。</p>
<pre><code class="go">thing := NewMockThing() // 默认初始化
if os.Getenv("ENV") == "prod" {
    thing = NewRealThing() // 在特定条件下覆盖
}
</code></pre>
<p>更进一步，将这个选择逻辑<strong>封装进一个辅助函数 NewThing()</strong> 中，这不仅让调用点的代码变得干净（thing := NewThing(isProd)），还将这个选择逻辑变成了一个可独立测试的单元。</p>
<h3>“保持靠左” (Keep to Left) 与 switch 的偏爱</h3>
<p>这两个由 Matt Ryer 提出的模式，被 Dave 奉为圭臬：</p>
<ul>
<li><strong>保持靠左</strong>：即使用“防卫语句”（Guard Clauses）或前置条件检查，在函数开头处理掉所有错误和异常情况并提前返回。这能让成功路径（Happy Path）始终贴近编辑器的左侧边缘，避免代码陷入层层嵌套的 if-else “深渊”。</li>
<li><strong>用 switch 代替 if-else</strong>：对于选择逻辑，switch 语句通常比 if-else 链更清晰，因为它明确地表达了“基于某个值进行选择”的意图，并且更易于未来扩展（只需增加 case）。</li>
</ul>
<h3>main.run 模式：让 main 不再特殊</h3>
<p>main 函数是每个 Go 程序的入口，但它也是最“奇怪”的函数：它不能返回 error，并且隐式地依赖于大量的全局状态（操作系统环境、标准输入输出、命令行参数等），这使得它极难测试。</p>
<p>Dave 强烈推荐 <strong>main.run 模式</strong>，其应用非常简单：<br />
1.  创建一个新的、普通的 Go 函数，例如 run。<br />
2.  将 main 函数中的所有核心逻辑都移入 run 函数。<br />
3.  将所有之前隐式依赖的全局状态，作为参数显式地传递给 run 函数。<br />
4.  让 run 函数返回一个 error。</p>
<pre><code class="go">func main() {
    // main 函数只负责处理最终的错误并退出
    if err := run(os.Stdout, os.Args); err != nil {
        fmt.Fprintf(os.Stderr, "error: %v\n", err)
        os.Exit(1)
    }
}

// run 函数现在是一个纯粹的、可测试的 Go 函数
func run(stdout io.Writer, args []string) error {
    // ... 你的所有核心逻辑
    // ... 检查前置条件，构建状态
    // ... 进入主循环
    // ... 遇到任何问题，只需 return err
    return nil
}
</code></pre>
<p>这个简单的重构，让程序的核心逻辑变得<strong>完全可测试</strong>，并且可以在测试中并行运行，极大地提升了开发和维护的效率。Dave 提到，这个模式曾帮助他的团队解决了一个棘手的问题：日志系统初始化失败，导致程序在尝试记录“日志初始化失败”这个错误时直接崩溃。</p>
<h2>支柱三：辅助函数 (Helpers) —— 语言的延伸与表达力的提升</h2>
<p>贯穿整个演讲的，是 Dave 对使用辅助函数的强烈推崇。他认为，辅助函数是我们扩展项目“内部语言”最强大的工具。</p>
<h3>辅助函数的价值</h3>
<ul>
<li><strong>为匿名模式命名</strong>：像 pointer.To[T] 这样的泛型函数，<a href="https://tonybai.com/2025/08/17/create-pointer-to-simple-types/">为“获取一个值的指针”这个 Go 语法不支持的</a>、在处理 Protobuf 时反复出现的模式，赋予了一个清晰的名字，避免了为每个字段都声明一个临时变量的繁琐。</li>
<li><strong>封装重复逻辑与避免否定</strong>：将 if err != nil &amp;&amp; !errors.Is(err, context.Canceled) 这样的过滤逻辑，封装进一个 ignoreCancel(err) 辅助函数中，让调用点的意图一目了然。同样，对于布尔检查，if req.StreamIsFalse() 显然比 if !req.Stream 更易于朗读和理解。</li>
<li><strong>提升表达力 (Nicity)</strong>：创建像 to.JSON(w, data) 这样的辅助函数，可以像 Ruby on Rails 一样，用更符合领域语言的方式来编写代码。这不仅是语法糖，它还能隐藏一些必要的细节（如设置 Content-Type 头），确保一致性和正确性。</li>
<li><strong>延迟求值</strong>：在测试中断言失败时，我们常常希望打印出失败时的详细上下文，例如完整的 HTTP 响应体。一个常见的错误是直接将 dumpBody(resp) 作为断言失败时的消息参数。这会导致 dumpBody 无论测试成功与否都会被调用，从而消耗掉响应体。通过将 dumpBody 封装进一个实现了 fmt.Stringer 接口的辅助类型中，我们可以实现<strong>延迟求值</strong>——只有在断言失败、需要打印消息时，其 String() 方法才会被调用。</li>
</ul>
<h3>内部包(internal)的妙用</h3>
<p>Dave 建议，我们可以在项目内部的 internal 目录下创建像 to、is、list 这样的包，用来存放这些辅助函数和类型。这不仅能避免污染公共 API，还能为项目创建一套强大的、可复用的“内部标准库”。</p>
<h2>核心：尊重人类的认知极限——神奇数字 7±2</h2>
<p>为什么这些看似微小的细节——命名、if-else、辅助函数——如此重要？Dave 引用了著名的认知心理学结论：人类的短期记忆（working memory）只能同时处理 <strong>7±2</strong> 个信息单元。</p>
<p>这意味着，当我们的代码迫使读者同时记住太多事情时，他们的<a href="https://tonybai.com/2024/10/24/cognitive-load-impact-on-programming-language-choice-and-study">认知负荷</a>就会超载，理解代码的难度就会急剧增加。</p>
<ul>
<li>一个 if status >= 400 的判断，需要大脑同时处理“大于”和“等于”<strong>两个</strong>概念。而 if status > 399 则只占用<strong>一个</strong>认知单元。</li>
<li>一个层层嵌套的 if-else 结构，每深一层，就需要读者在记忆中压入一个新的上下文。</li>
<li>一个需要大段注释才能解释的函数，说明其名字和签名本身就消耗了大量的认知资源。</li>
</ul>
<p>所有这些模式——更短但精确的名字、消除 if-else、将逻辑封装进辅助函数——其最终目的都是一致的：<strong>减少读者在理解每一行代码时，需要装入短期记忆中的“东西”的数量。</strong></p>
<h2>小结：语言影响思维</h2>
<p>演讲的最后，Dave 引用了“萨丕尔-沃尔夫假说”来升华他的核心论点(注：笔者在GopherChina 2017年大会上的演讲《<a href="https://tonybai.com/2017/04/20/go-coding-in-go-way">Go coding in go way</a>》也引用了此观点，记得那年Dave也参加了Gopher China，就坐在我的前面^_^)：</p>
<blockquote>
<p>“你使用的语言，直接影响你思考问题的方式。”</p>
</blockquote>
<p>通过创建新的名词（类型）和动词（辅助函数），我们实际上是在<strong>扩展和塑造我们项目的内部语言</strong>。当这门内部语言变得更优雅、更精确、认知负荷更低时，我们对问题的思考也会变得更清晰、更深入。</p>
<p>一个难以命名的函数，或一段需要大段注释来解释的逻辑，都是设计需要改进的强烈信号。它在“恳求你进行重构”。</p>
<p>最终，我们编写的代码，不仅是给机器执行的指令，更是写给未来自己和同事的“信”。而 Dave Cheney 的这些建议，正是帮助我们写好这封“信”，使其清晰、优雅、易于理解的宝贵指南。</p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2025, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2025/09/17/some-things-i-keep-repeating-about-go/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Anthropic内部实践首次公开：揭秘Claude Code如何引爆全员生产力</title>
		<link>https://tonybai.com/2025/07/25/how-anthropic-teams-use-claude-code/</link>
		<comments>https://tonybai.com/2025/07/25/how-anthropic-teams-use-claude-code/#comments</comments>
		<pubDate>Fri, 25 Jul 2025 13:46:56 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Agent]]></category>
		<category><![CDATA[AI]]></category>
		<category><![CDATA[Anthropic]]></category>
		<category><![CDATA[Claude.md]]></category>
		<category><![CDATA[ClaudeCode]]></category>
		<category><![CDATA[GEMINI.md]]></category>
		<category><![CDATA[GeminiCli]]></category>
		<category><![CDATA[infra]]></category>
		<category><![CDATA[JS]]></category>
		<category><![CDATA[LLM]]></category>
		<category><![CDATA[monorepo]]></category>
		<category><![CDATA[openai]]></category>
		<category><![CDATA[React]]></category>
		<category><![CDATA[Rust]]></category>
		<category><![CDATA[terminal]]></category>
		<category><![CDATA[TS]]></category>
		<category><![CDATA[Vim]]></category>
		<category><![CDATA[人工智能]]></category>
		<category><![CDATA[代码库]]></category>
		<category><![CDATA[基础设施]]></category>
		<category><![CDATA[大模型]]></category>
		<category><![CDATA[并行]]></category>
		<category><![CDATA[强化学习]]></category>
		<category><![CDATA[文档]]></category>
		<category><![CDATA[智能体]]></category>
		<category><![CDATA[机器学习]]></category>
		<category><![CDATA[注释]]></category>
		<category><![CDATA[测试]]></category>
		<category><![CDATA[程序员]]></category>
		<category><![CDATA[编码]]></category>
		<category><![CDATA[设计]]></category>
		<category><![CDATA[调试]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=4954</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2025/07/25/how-anthropic-teams-use-claude-code 当 AI 编程助手从简单的代码补全工具，演变为深度集成于开发者工作流核心的“终端原生 AI”（Terminal-native AI）时，一个根本性的问题浮现出来：顶尖团队究竟是如何在日常工作中驾驭这股新力量的？ 理论和演示层出不穷，但真实、大规模、跨职能的实践案例却凤毛麟角。 现在，我们得到了来自源头的答案。 Anthropic 公司今天发布了一份极为详尽的内部案例研究，为我们提供了一次罕见的“幕后观察”机会，让我们得以一窥其内部团队——从最核心的产品开发、安全工程，到数据科学、乃至法务和营销团队——是如何将 Claude Code 作为其日常工作的核心伙伴。 这份案例研究的意义非凡，因为它不再是关于“AI 能做什么”的畅想，而是关于“AI 正在如何做”的实录。从数据工程师借助截图在几分钟内解决复杂的 Kubernetes 故障，到毫无编程经验的财务团队用自然语言构建起全自动的数据处理流；从新员工入职第一天就能高效导航庞大的单一代码库，到产品设计师直接将视觉稿转化为可交互的前端原型——我们看到的是一种工作范式的彻底变革以及生产力的“爆炸”！ 本文就翻译自Anthropic的这篇博客文章《How Anthropic teams use Claude Code》，希望各位读者都能从中受益。 Anthropic 的内部团队正在通过 Claude Code 改变其工作流程，使开发人员和非技术人员能够处理复杂项目、自动化任务，并弥合之前限制他们生产力的技能差距。 为了了解更多，我们与以下团队进行了交谈： 数据基础设施 产品开发 安全工程 推理 数据科学与可视化 产品工程 增长营销 产品设计 强化学习 (RL) 工程 法务 通过这些访谈，我们深入了解了不同部门如何使用 Claude Code、它对其工作的影响，以及为其他考虑采用该工具的组织提供的建议。 Claude Code 在数据基础设施领域的应用 数据基础设施团队为全公司的团队组织所有业务数据。他们使用 Claude Code 来自动化常规的数据工程任务、解决复杂的基础设施问题，并为非技术团队成员创建文档化的工作流程，以便他们能够独立地访问和操作数据。 主要 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2025/how-anthropic-teams-use-claude-code-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2025/07/25/how-anthropic-teams-use-claude-code">本文永久链接</a> &#8211; https://tonybai.com/2025/07/25/how-anthropic-teams-use-claude-code</p>
<p>当 AI 编程助手从简单的代码补全工具，演变为深度集成于开发者工作流核心的“终端原生 AI”（Terminal-native AI）时，一个根本性的问题浮现出来：<strong>顶尖团队究竟是如何在日常工作中驾驭这股新力量的？</strong> 理论和演示层出不穷，但真实、大规模、跨职能的实践案例却凤毛麟角。</p>
<p>现在，我们得到了来自源头的答案。</p>
<p>Anthropic 公司今天发布了一份极为详尽的内部案例研究，为我们提供了一次罕见的“幕后观察”机会，让我们得以一窥其内部团队——从最核心的产品开发、安全工程，到数据科学、乃至法务和营销团队——是如何将 <strong>Claude Code</strong> 作为其日常工作的核心伙伴。</p>
<p>这份案例研究的意义非凡，因为它不再是关于“AI 能做什么”的畅想，而是关于“AI 正在如何做”的实录。从数据工程师借助截图在几分钟内解决复杂的 Kubernetes 故障，到毫无编程经验的财务团队用自然语言构建起全自动的数据处理流；从新员工入职第一天就能高效导航庞大的单一代码库，到产品设计师直接将视觉稿转化为可交互的前端原型——我们看到的是一种工作范式的彻底变革以及生产力的“爆炸”！</p>
<p>本文就翻译自Anthropic的这篇博客文章《<a href="https://www.anthropic.com/news/how-anthropic-teams-use-claude-code">How Anthropic teams use Claude Code</a>》，希望各位读者都能从中受益。</p>
<hr />
<p>Anthropic 的内部团队正在通过 Claude Code 改变其工作流程，使开发人员和非技术人员能够处理复杂项目、自动化任务，并弥合之前限制他们生产力的技能差距。</p>
<p>为了了解更多，我们与以下团队进行了交谈：</p>
<ul>
<li>数据基础设施</li>
<li>产品开发</li>
<li>安全工程</li>
<li>推理</li>
<li>数据科学与可视化</li>
<li>产品工程</li>
<li>增长营销</li>
<li>产品设计</li>
<li>强化学习 (RL) 工程</li>
<li>法务</li>
</ul>
<p>通过这些访谈，我们深入了解了不同部门如何使用 Claude Code、它对其工作的影响，以及为其他考虑采用该工具的组织提供的建议。</p>
<h2>Claude Code 在数据基础设施领域的应用</h2>
<p>数据基础设施团队为全公司的团队组织所有业务数据。他们使用 Claude Code 来自动化常规的数据工程任务、解决复杂的基础设施问题，并为非技术团队成员创建文档化的工作流程，以便他们能够独立地访问和操作数据。</p>
<h3>主要 Claude Code 用例</h3>
<p><strong>使用截图进行 Kubernetes 调试</strong></p>
<p>当 Kubernetes 集群出现故障且无法调度新的 pod 时，团队使用 Claude Code 来诊断问题。他们将仪表盘的截图输入到 Claude Code 中，后者引导他们逐个菜单地浏览 Google Cloud 的 UI，直到发现一个指示 pod IP 地址耗尽的警告。随后，Claude Code 提供了创建新 IP 池并将其添加到集群的确切命令，从而无需网络专家的介入。</p>
<p><strong>为财务团队提供纯文本工作流</strong></p>
<p>工程师向财务团队成员展示了如何编写描述其数据工作流的纯文本文件，然后将其加载到 Claude Code 中以实现完全自动化的执行。没有编程经验的员工可以描述诸如“查询此仪表盘，获取信息，运行这些查询，生成 Excel 输出”之类的步骤，Claude Code 将执行整个工作流，包括询问像日期这样的必需输入。</p>
<p><strong>为新员工提供代码库导航</strong></p>
<p>当新的数据科学家加入团队时，他们被引导使用 Claude Code 来导航庞大的代码库。Claude Code 会读取他们的 Claude.md 文件，为特定任务识别相关文件，解释数据管道的依赖关系，并帮助新员工理解哪些上游数据源为仪表盘提供数据。这取代了传统的数据目录和可发现性工具。</p>
<p><strong>会话结束时的文档更新</strong></p>
<p>团队会要求 Claude Code 总结已完成的工作会话，并在每个任务结束时提出改进建议。这就创建了一个持续改进的循环，其中 Claude Code 帮助优化 Claude.md 文件。</p>
<p><strong>跨多个实例的并行任务管理</strong></p>
<p>在处理长时间运行的数据任务时，团队会在不同的代码仓库中为不同的项目打开多个 Claude Code 实例。每个实例都保持完整的上下文，因此当他们在数小时或数天后切换回来时，Claude Code 能准确记住他们当时正在做什么以及进行到哪里，从而实现了真正的并行工作流管理而不会丢失上下文。</p>
<h3>团队影响</h3>
<p><strong>无需专业知识即可解决基础设施问题</strong></p>
<p>解决了通常需要系统或网络团队成员介入的 Kubernetes 集群问题，使用 Claude Code 诊断问题并提供精确的修复方案。</p>
<p><strong>加速入职流程</strong></p>
<p>新的数据分析师和团队成员可以迅速理解复杂的系统，并在没有大量指导的情况下做出有意义的贡献。</p>
<p><strong>增强的支持工作流</strong></p>
<p>Claude Code 可以处理更大量的数据并识别异常（例如监控 200 个仪表盘），这是人类手动审查无法做到的。</p>
<p><strong>实现了跨团队自助服务</strong></p>
<p>没有编程经验的财务团队现在可以独立执行复杂的数据工作流。</p>
<h3>来自数据基础设施团队的顶级建议</h3>
<p><strong>编写详细的 Claude.md 文件</strong></p>
<p>团队表示，你在 Claude.md 文件中记录工作流、工具和期望越详尽，Claude Code 的表现就越好。这使得 Claude Code 在处理诸如根据现有设计模式建立新数据管道之类的常规任务时表现出色。</p>
<p><strong>对敏感数据使用 MCP 服务器而非 CLI</strong></p>
<p>他们建议使用 MCP 服务器而不是 BigQuery CLI，以便更好地控制 Claude Code 的访问权限，尤其是在处理需要日志记录或有潜在隐私问题的敏感数据时。</p>
<p><strong>分享使用会话</strong></p>
<p>团队举办了分享会，成员们在会上演示了各自的 Claude Code 工作流程。这有助于传播最佳实践，并展示了他们可能未曾发现的各种工具使用方法。</p>
<h2>Claude Code 在产品开发领域的应用</h2>
<p>Claude Code 产品开发团队使用他们自己的产品来为 Claude Code 构建更新，扩展产品的企业级功能和智能体体循环（agentic loop）功能。</p>
<h3>主要 Claude Code 用例</h3>
<p><strong>使用自动接受模式进行快速原型设计</strong></p>
<p>工程师通过启用“自动接受模式”（shift+tab）来进行快速原型设计，并建立自主循环，让 Claude 编写代码、运行测试并持续迭代。他们给 Claude 提出他们不熟悉的抽象问题，让它自主工作，然后在最终完善前审查那 80% 完成度的解决方案。团队建议从一个干净的 git 状态开始，并定期提交检查点，以便在 Claude 偏离轨道时可以轻松地恢复任何不正确的更改。</p>
<p><strong>核心功能的同步编码</strong></p>
<p>对于触及应用核心业务逻辑的更关键的功能，团队与 Claude Code 同步工作，提供带有具体实现指令的详细提示。他们在实时监控整个过程，以确保代码质量、风格指南合规性和正确的架构，同时让 Claude 处理重复性的编码工作。</p>
<p><strong>构建 Vim 模式</strong></p>
<p>他们最成功的异步项目之一是为 Claude Code 实现 Vim 键位绑定。他们要求 Claude 构建整个功能，最终实现的约 70% 来自 Claude 的自主工作，只需几次迭代即可完成。</p>
<p><strong>测试生成与错误修复</strong></p>
<p>团队在实现功能后，使用 Claude Code 编写全面的测试，并处理在代码审查中发现的简单错误修复。他们还使用 GitHub Actions 让 Claude 自动处理 Pull Request 中的评论，如格式化问题或函数重命名。</p>
<p><strong>代码库探索</strong></p>
<p>在处理不熟悉的代码库（如单一代码库或 API 端）时，团队使用 Claude Code 快速理解系统的工作方式。他们不再等待 Slack 上的回复，而是直接向 Claude 询问解释和代码引用，从而在上下文切换中节省了大量时间。</p>
<h3>团队影响</h3>
<p><strong>更快的特性实现</strong></p>
<p>Claude Code 成功地实现了像 Vim 模式这样的复杂功能，其中 70% 的代码由 Claude 自主编写。</p>
<p><strong>提高开发速度</strong></p>
<p>该工具可以快速地为功能制作原型并迭代想法，而不会陷入实现细节的泥潭。</p>
<p><strong>通过自动化测试提升代码质量</strong></p>
<p>Claude 生成全面的测试并处理常规的错误修复，在减少手动工作的同时保持了高标准。</p>
<p><strong>更好的代码库探索</strong></p>
<p>团队成员可以迅速熟悉单一代码库中不熟悉的部分，而无需等待同事的回复。</p>
<h3>来自 Claude Code 团队的顶级建议</h3>
<p><strong>创建自给自足的循环</strong></p>
<p>设置 Claude，让它通过自动运行构建、测试和代码检查来验证自己的工作。这使得 Claude 能够更长时间地自主工作并发现自己的错误，在要求它在编写代码之前生成测试时尤其有效。</p>
<p><strong>培养任务分类的直觉</strong></p>
<p>学会区分适合异步处理的任务（外围功能、原型设计）和需要同步监督的任务（核心业务逻辑、关键修复）。产品边缘的抽象任务可以用“自动接受模式”处理，而核心功能则需要更密切的监督。</p>
<p><strong>形成清晰、详细的提示</strong></p>
<p>当组件具有相似的名称或功能时，在你的请求中要极其具体。你的提示越好、越详细，你就越能相信 Claude 能独立工作，而不会意外地更改代码库的错误部分。</p>
<h2>Claude Code 在安全工程领域的应用</h2>
<p>安全工程团队专注于保障软件开发生命周期、供应链安全和开发环境安全。他们广泛使用 Claude Code 进行代码编写和调试。</p>
<h3>主要 Claude Code 用例</h3>
<p><strong>复杂的基础设施调试</strong></p>
<p>在处理事故时，他们向 Claude Code 提供堆栈跟踪和文档，要求它在代码库中追踪控制流。这大大缩短了生产问题的解决时间，使他们能够在大约 5 分钟内理解通常需要 10-15 分钟手动代码扫描才能发现的问题。</p>
<p><strong>Terraform 代码审查与分析</strong></p>
<p>对于需要安全审批的基础设施变更，团队将 Terraform 计划复制到 Claude Code 中，并提问“这会做什么？我会后悔吗？”。这创建了更紧密的反馈循环，使安全团队能更快地审查和批准基础设施变更，减少了开发过程中的瓶颈。</p>
<p><strong>文档合成与操作手册（runbooks）</strong></p>
<p>Claude Code 能消化多个文档源，并创建 Markdown 格式的操作手册、故障排除指南和概述。团队使用这些浓缩的文档作为调试实际问题的上下文，创建了比搜索完整知识库更高效的工作流程。</p>
<p><strong>测试驱动的开发工作流</strong></p>
<p>他们不再遵循以前“设计文档 → 粗糙代码 → 重构 → 放弃测试”的模式，而是要求 Claude Code 提供伪代码，引导它完成测试驱动的开发，并在其卡住时定期介入以引导方向，从而产生更可靠和可测试的代码。</p>
<p><strong>上下文切换与项目入职</strong></p>
<p>在为像“dependant”（一个用于安全审批工作流的 Web 应用）这样的现有项目贡献代码时，他们使用 Claude Code 编写、审查和执行用 Markdown 编写并存储在代码库中的规范，使得在几天内（而非几周）就能做出有意义的贡献。</p>
<h3>团队影响</h3>
<p><strong>缩短事故解决时间</strong></p>
<p>通常需要 10-15 分钟手动代码扫描的基础设施调试，现在大约只需要 5 分钟。</p>
<p><strong>改进的安全审查周期</strong></p>
<p>用于安全审批的 Terraform 代码审查速度更快，消除了开发者在等待安全团队批准时的阻塞。</p>
<p><strong>增强的跨职能贡献</strong></p>
<p>团队成员可以在几天内为项目做出有意义的贡献，而无需数周的上下文构建。</p>
<p><strong>更好的文档工作流</strong></p>
<p>从多个来源合成的故障排除指南和操作手册，创建了更高效的调试流程。</p>
<h3>来自安全工程团队的顶级建议</h3>
<p><strong>广泛使用自定义斜杠命令</strong></p>
<p>安全工程团队使用了整个单一代码库中 50% 的自定义斜杠命令实现。这些自定义命令简化了特定的工作流程并加速了重复性任务。</p>
<p><strong>让 Claude 先说</strong></p>
<p>他们不再是提出针对性问题以生成代码片段，而是告诉 Claude Code “边做边提交你的工作”，让它在定期检查的情况下自主工作，从而得到更全面的解决方案。</p>
<p><strong>充分利用其文档能力</strong></p>
<p>除了编码，Claude Code 在合成文档和创建结构化输出方面表现出色。团队提供写作样本和格式偏好，以获得可以立即在 Slack、Google Docs 和其他工具中使用的文档，从而避免界面切换的疲劳。</p>
<h2>Claude Code 在推理领域的应用</h2>
<p>推理团队管理着在 Claude 读取你的提示并生成响应时存储信息的内存系统。团队成员，尤其是那些刚接触机器学习的人，可以广泛使用 Claude Code 来弥补知识差距并加速他们的工作。</p>
<h3>主要 Claude Code 用例</h3>
<p><strong>代码库理解与入职</strong></p>
<p>团队严重依赖 Claude Code 在加入复杂代码库时快速理解其架构。他们不再手动搜索 GitHub 仓库，而是要求 Claude 找到调用特定功能的文件，在几秒钟内得到结果，而不是询问同事或手动搜索。</p>
<p><strong>带边缘案例覆盖的单元测试生成</strong></p>
<p>在编写完核心功能后，他们会要求 Claude 编写全面的单元测试。Claude 会自动包含被忽略的边缘案例，在几分钟内完成通常需要大量时间和脑力的工作，就像一个他们可以审查的编码助手。</p>
<p><strong>机器学习概念解释</strong></p>
<p>没有机器学习背景的团队成员依赖 Claude 来解释特定模型的功能和设置。以前需要一个小时谷歌搜索和阅读文档的事情，现在只需要 10-20 分钟，研究时间减少了 80%。</p>
<p><strong>跨语言代码翻译</strong></p>
<p>在测试不同编程语言的功能时，团队会解释他们想要测试的内容，然后 Claude 会用所需的语言（如 Rust）编写逻辑，从而无需为了测试目的而学习新语言。</p>
<p><strong>命令记忆与 Kubernetes 管理</strong></p>
<p>他们不再需要记住复杂的 Kubernetes 命令，而是向 Claude 询问正确的语法，比如“如何获取所有 pod 或部署的状态”，并获得他们基础设施工作所需的确切命令。</p>
<h3>团队影响</h3>
<p><strong>加速机器学习概念学习</strong></p>
<p>通过 Claude Code，他们的研究时间减少了 80%，以前需要一个小时谷歌搜索的事情现在只需要 10-20 分钟。</p>
<p><strong>更快的代码库导航</strong></p>
<p>该工具可以帮助团队成员在几秒钟内找到相关文件并理解系统架构，而不用依赖同事分享知识，后者通常需要几天时间。</p>
<p><strong>全面的测试覆盖</strong></p>
<p>Claude 自动生成带有边缘案例的单元测试，在保持代码质量的同时减轻了脑力负担。</p>
<p><strong>消除语言障碍</strong></p>
<p>团队可以在不熟悉的情况下，在像 Rust 这样的语言中实现功能，而无需学习它。</p>
<h3>来自推理团队的顶级建议</h3>
<p><strong>首先测试知识库功能</strong></p>
<p>尝试问各种问题，看看 Claude 的回答是否比谷歌搜索更快。如果它更快、更准确，那么它就是你工作流程中一个宝贵的时间节省工具。</p>
<p><strong>从代码生成开始</strong></p>
<p>给 Claude 具体指令，要求它编写逻辑，然后验证其正确性。这有助于在将其用于更复杂的任务之前，建立对该工具能力的信任。</p>
<p><strong>用它来编写测试</strong></p>
<p>让 Claude 编写单元测试可以显著减轻日常开发工作的压力。利用这个功能来保持代码质量，而无需花费时间手动思考所有测试用例。</p>
<h2>Claude Code 在数据科学与机器学习工程领域的应用</h2>
<p>数据科学和机器学习工程团队需要复杂的可视化工具来理解模型性能，但构建这些工具通常需要不熟悉的语言和框架的专业知识。Claude Code 使这些团队能够构建生产质量的分析仪表盘，而无需成为全栈开发人员。</p>
<h3>主要 Claude Code 用例</h3>
<p><strong>构建 JavaScript/TypeScript 仪表盘应用</strong></p>
<p>尽管对“JavaScript 和 TypeScript 知之甚少”，该团队仍使用 Claude Code 构建了完整的 React 应用，用于可视化强化学习 (RL) 模型的性能和训练数据。他们让 Claude 控制从零开始编写完整的应用程序，比如一个 5000 行的 TypeScript 应用，而无需自己理解代码。这至关重要，因为可视化应用上下文相对较低，不需要理解整个单一代码库，从而允许快速原型设计工具来理解模型在训练和评估期间的性能。</p>
<p><strong>处理重复的重构任务</strong></p>
<p>当面临合并冲突或半复杂的文件重构时——这些任务对于编辑器宏来说过于复杂，但又不足以进行大规模开发——他们会像使用“老虎机”一样使用 Claude Code：提交他们的状态，让 Claude 自主工作 30 分钟，然后要么接受解决方案，要么在不成功时重新开始。</p>
<p><strong>创建持久的分析工具而非一次性笔记本</strong></p>
<p>团队现在让 Claude 构建可以跨未来模型评估重用的永久性 React 仪表盘，而不是构建用完即弃的一次性 Jupyter 笔记本。这很重要，因为理解 Claude 的性能是“团队最重要的事情之一”——他们需要了解模型在训练和评估期间的表现，而“这实际上非同小可，简单的工具无法从一个上升的数字中获得太多信号。”</p>
<p><strong>零依赖任务委托</strong></p>
<p>对于完全不熟悉的代码库或语言中的任务，他们将整个实现委托给 Claude Code，利用其从单一代码库中收集上下文并执行任务的能力，而无需他们实际参与编码过程。这使得他们能够在专业领域之外提高生产力，而不是花时间学习新技术。</p>
<h3>团队影响</h3>
<p><strong>实现 2-4 倍的时间节省</strong></p>
<p>过去繁琐但可手动管理的常规重构任务现在完成得更快了。</p>
<p><strong>在不熟悉的语言中构建复杂的应用程序</strong></p>
<p>尽管 JavaScript/TypeScript 经验最少，也创建了 5000 行的 TypeScript 应用。</p>
<p><strong>从一次性工具转向持久性工具</strong></p>
<p>不再使用一次性的 Jupyter 笔记本，现在构建可重用的 React 仪表盘进行模型分析。</p>
<p><strong>直接的模型改进洞察</strong></p>
<p>第一手的 Claude Code 经验为未来模型迭代中更好的内存系统和用户体验改进提供了信息。</p>
<p><strong>实现了可视化驱动的决策</strong></p>
<p>通过先进的可视化工具，更好地理解 Claude 在训练和评估期间的性能。</p>
<h3>来自数据科学与机器学习工程团队的建议</h3>
<p><strong>像玩老虎机一样对待它</strong></p>
<p>在让 Claude 工作之前保存你的状态，让它运行 30 分钟，然后要么接受结果，要么重新开始，而不是试图纠正错误。从头开始通常比试图修复 Claude 的错误有更高的成功率。</p>
<p><strong>在需要时为了简单而中断</strong></p>
<p>在监督时，不要犹豫，停下来问 Claude “你为什么这么做？试试更简单的方法。” 模型默认倾向于更复杂的解决方案，但对更简单方法的请求反应良好。</p>
<h2>Claude Code 在产品工程领域的应用</h2>
<p>产品工程团队致力于 PDF 支持、引用和网络搜索等功能，这些功能为 Claude 的上下文窗口带来了额外的知识。跨越大型、复杂的代码库工作意味着不断遇到不熟悉的代码部分，需要花费大量时间来理解要检查哪些文件，并在进行更改前建立上下文。Claude Code 改善了这种体验，它作为一个向导，可以帮助他们理解系统架构、识别相关文件并解释复杂的交互。</p>
<h3>主要 Claude Code 用例</h3>
<p><strong>第一步工作流规划</strong></p>
<p>团队将 Claude Code 作为任何任务的“第一站”，要求它确定要检查哪些文件以进行错误修复、功能开发或分析。这取代了在开始工作前手动导航代码库和收集上下文的传统耗时过程。</p>
<p><strong>跨代码库的独立调试</strong></p>
<p>团队现在有信心在不向他人求助的情况下，处理代码库中不熟悉部分的错误。他们可以问 Claude “你认为你能修复这个 bug 吗？这是我看到的行为”，并且通常能立即取得进展，这在以前由于所需的时间投入而不可行。</p>
<p><strong>通过内部测试进行模型迭代测试</strong></p>
<p>Claude Code 自动使用最新的研究模型快照，使其成为体验模型变化的主要方式。这为团队在开发周期中提供了关于模型行为变化的直接反馈，这是他们以前在发布时没有经历过的。</p>
<p><strong>消除上下文切换的开销</strong></p>
<p>他们不再需要将代码片段复制和拖动到 Claude.ai 中，同时还要详细解释问题，而是可以直接在 Claude Code 中提问，无需额外的上下文收集，大大减少了脑力开销。</p>
<h3>团队影响</h3>
<p><strong>增强了处理不熟悉领域的信心</strong></p>
<p>团队成员可以独立地在不熟悉的代码库中调试错误和调查事件。</p>
<p><strong>在上下文收集中节省了大量时间</strong></p>
<p>Claude Code 消除了将代码片段复制和拖动到 Claude.ai 的开销，减少了上下文切换的负担。</p>
<p><strong>更快的轮岗入职</strong></p>
<p>轮岗到新团队的工程师可以迅速导航不熟悉的代码库，并在没有大量同事咨询的情况下做出有意义的贡献。</p>
<p><strong>提升了开发者的幸福感</strong></p>
<p>团队报告说，在日常工作流程中减少了摩擦，感觉更快乐、更有效率。</p>
<h3>来自产品工程团队的顶级建议</h3>
<p><strong>将其视为迭代的伙伴，而非一次性的解决方案</strong></p>
<p>不要期望 Claude 能立即解决问题，而是把它当作一个你与之迭代的合作者。这比试图在第一次尝试中就获得完美的解决方案效果更好。</p>
<p><strong>用它来建立在不熟悉领域的信心</strong></p>
<p>不要犹豫，去处理你专业领域之外的错误或调查事件。Claude Code 使得在通常需要大量上下文构建的领域独立工作成为可能。</p>
<p><strong>从最少的信息开始</strong></p>
<p>从你需要的最基本信息开始，让 Claude 引导你完成整个过程，而不是预先加载大量的解释。</p>
<h2>Claude Code 在增长营销领域的应用</h2>
<p>增长营销团队专注于在付费搜索、付费社交、移动应用商店、电子邮件营销和 SEO 方面建立效果营销渠道。作为一个非技术背景的单人团队，他们使用 Claude Code 来自动化重复性的营销任务，并创建传统上需要大量工程资源的智能体工作流。</p>
<h3>主要 Claude Code 用例</h3>
<p><strong>自动化 Google Ads 广告创意生成</strong></p>
<p>团队构建了一个智能体工作流，该工作流处理包含数百个现有广告及其效果指标的 CSV 文件，识别表现不佳的广告进行迭代，并生成符合严格字符限制（标题 30 个字符，描述 90 个字符）的新变体。使用两个专门的子智能体（一个用于标题，一个用于描述），该系统可以在几分钟内生成数百个新广告，而无需跨多个活动进行手动创建。这使他们能够大规模地进行测试和迭代，这是以前需要大量时间才能实现的。</p>
<p><strong>用于大规模创意制作的 Figma 插件</strong></p>
<p>他们开发了一个 Figma 插件，而不是为付费社交广告手动复制和编辑静态图像。该插件能识别框架并以编程方式通过替换标题和描述生成多达 100 个广告变体，将过去需要数小时的复制粘贴工作减少到每批次半秒。这使得创意产出提高了 10 倍，让团队可以在关键社交渠道上测试更多样化的创意变体。</p>
<p><strong>用于活动分析的 Meta Ads MCP 服务器</strong></p>
<p>他们创建了一个与 Meta Ads API 集成的 MCP 服务器，以直接在 Claude 桌面应用内查询活动表现、支出数据和广告效果，从而无需在不同平台之间切换进行性能分析，节省了关键时间，而每一个效率的提升都转化为更高的投资回报率（ROI）。</p>
<p><strong>带记忆系统的高级提示工程</strong></p>
<p>他们实现了一个基本的记忆系统，记录跨广告迭代的假设和实验，使系统在生成新变体时能够提取以前的测试结果作为上下文，从而创建了一个自我改进的测试框架。这使得系统性的实验成为可能，而这在以前是无法手动追踪的。</p>
<h3>团队影响</h3>
<p><strong>在重复性任务上节省了大量时间</strong></p>
<p>Claude Code 将广告文案创作时间从 2 小时减少到 15 分钟，为团队腾出更多时间进行战略性工作。</p>
<p><strong>创意产出增加 10 倍</strong></p>
<p>通过自动化广告生成和 Figma 集成，团队现在可以在各个渠道测试更多的广告变体，以获取最新的视觉设计元素。</p>
<p><strong>像一个更大的团队一样运作</strong></p>
<p>团队可以处理传统上需要专门工程资源的大型开发任务。</p>
<p><strong>战略焦点的转移</strong></p>
<p>团队可以花更多时间在整体战略和构建智能体自动化上，而不是手动执行。</p>
<h3>来自增长营销团队的顶级建议</h3>
<p><strong>识别支持 API 的重复性任务</strong></p>
<p>寻找涉及使用带有 API 的工具（如广告平台、设计工具、分析平台）进行重复性操作的工作流。这些是自动化的主要候选者，也是 Claude Code 提供最大价值的地方。</p>
<p><strong>将复杂工作流分解为专门的子智能体</strong></p>
<p>不要试图在一个提示或工作流中处理所有事情，而是为特定任务创建单独的智能体（例如标题智能体 vs. 描述智能体）。这在处理复杂需求时使调试更容易，并提高了输出质量。</p>
<p><strong>在编码前进行彻底的头脑风暴和提示规划</strong></p>
<p>花大量时间预先使用 Claude.ai 来思考你的整个工作流，然后让 Claude.ai 为 Claude Code 创建一个全面的提示和代码结构以供参考。此外，要循序渐进地工作，而不是要求一次性的解决方案，以避免 Claude 被复杂的任务压垮。</p>
<h2>Claude Code 在产品设计领域的应用</h2>
<p>产品设计团队支持 Claude Code、Claude.ai 和 Anthropic API，专注于构建 AI 产品。即使是非开发人员也可以使用 Claude Code 来弥合设计和工程之间的传统鸿沟，使他们能够直接实现其设计愿景，而无需与工程师进行大量的来回迭代。</p>
<h3>主要 Claude Code 用例</h3>
<p><strong>前端润色和状态管理变更</strong></p>
<p>团队不再创建详尽的设计文档，并与工程师就视觉调整（字体、颜色、间距）进行多轮反馈，而是直接使用 Claude Code 实现这些更改。工程师们注意到，他们正在进行“通常不会看到设计师做出的大的状态管理更改”，这使他们能够达到他们所设想的确切质量。</p>
<p><strong>GitHub Actions 自动化工单处理</strong></p>
<p>利用 Claude Code 的 GitHub 集成，他们只需提交描述所需更改的问题/工单，Claude 就会自动提出代码解决方案，而无需打开 Claude Code，为他们持续积压的润色任务创建了一个无缝的错误修复和功能优化工作流。</p>
<p><strong>快速交互式原型制作</strong></p>
<p>通过将模型图像粘贴到 Claude Code 中，他们可以生成功能齐全的原型，工程师可以立即理解并在此基础上进行迭代，取代了传统的静态 Figma 设计周期，后者需要大量的解释和到工作代码的转换。</p>
<p><strong>边缘案例发现和系统架构理解</strong></p>
<p>团队使用 Claude Code 来绘制错误状态、逻辑流和不同的系统状态，使他们能够在设计阶段识别边缘案例，而不是在开发后期才发现，从而从根本上提高了他们初始设计的质量。</p>
<p><strong>复杂的文案更改和法律合规</strong></p>
<p>对于像在整个代码库中移除“研究预览”消息这样的任务，他们使用 Claude Code 查找所有实例，审查周围的文案，与法务部门实时协调更改，并实施更新，这个过程只需要两个 30 分钟的电话会议，而不是一周的来回协调。</p>
<h3>团队影响</h3>
<p><strong>转变了核心工作流</strong></p>
<p>Claude Code 成为主要的设计工具，Figma 和 Claude Code 的使用时间占到了 80%。</p>
<p><strong>2-3 倍的执行速度</strong></p>
<p>以前需要与工程师大量来回沟通的视觉和状态管理更改现在可以直接实现。</p>
<p><strong>周期时间从数周缩短到数小时</strong></p>
<p>像发布 Google Analytics 消息这样需要一周协调的复杂项目，现在可以在两个 30 分钟的电话会议中完成。</p>
<p><strong>两种截然不同的用户体验</strong></p>
<p>开发者获得了一种“增强的工作流”（更快的执行），而非技术用户则获得了“天哪，我现在也能像开发者一样工作了！”的工作流。</p>
<p><strong>改善了设计与工程的协作</strong></p>
<p>Claude Code 改善了沟通和问题解决速度，因为设计师在不必与工程师紧密合作的情况下，就能理解系统的约束和可能性。</p>
<h3>来自产品设计团队的顶级建议</h3>
<p><strong>从工程师那里获得正确的设置帮助</strong></p>
<p>让工程团队的同事帮助进行初始的代码仓库设置和权限配置——对于非开发人员来说，技术入职具有挑战性，但一旦配置完成，它将为日常工作流带来变革。</p>
<p><strong>使用自定义记忆文件来引导 Claude 的行为</strong></p>
<p>创建具体的指令，告诉 Claude 你是一个编码经验很少的设计师，需要详细的解释和更小、增量的更改，这会显著提高 Claude 响应的质量，并使其不那么令人生畏。</p>
<p><strong>利用图像粘贴进行原型制作</strong></p>
<p>使用 Command+V 将截图直接粘贴到 Claude Code 中。它在读取设计和生成功能代码方面表现出色，对于将静态模型转化为工程师可以立即理解和构建的交互式原型非常有价值。</p>
<h2>Claude Code 在 RL 工程领域的应用</h2>
<p>RL 工程团队专注于 RL 中的高效采样和跨集群的权重传输。他们主要使用 Claude Code 编写中小型功能、进行调试，以及理解复杂的代码库，采用一种包含频繁检查点和回滚的迭代方法。</p>
<h3>主要 Claude Code 用例</h3>
<p><strong>有监督的自主功能开发</strong></p>
<p>团队让 Claude Code 编写大部分中小型功能的代码，同时提供监督，例如为权重传输组件实现认证机制。他们以交互方式工作，允许 Claude 主导，但在其偏离轨道时进行引导。</p>
<p><strong>测试生成与代码审查</strong></p>
<p>在自己实现更改后，团队会要求 Claude Code 添加测试或审查他们的代码。这种自动化的测试工作流在常规但重要的质量保证任务上节省了大量时间。</p>
<p><strong>调试和错误调查</strong></p>
<p>他们使用 Claude Code 调试错误，结果好坏参半。有时它能立即识别问题并添加相关测试，而其他时候它难以理解问题，但总的来说，在它起作用时提供了价值。</p>
<p><strong>代码库理解和调用栈分析</strong></p>
<p>他们工作流程中最大的变化之一是使用 Claude Code 快速获取相关组件和调用栈的摘要，取代了手动阅读代码或生成大量调试输出。</p>
<p><strong>Kubernetes 操作指导</strong></p>
<p>他们经常向 Claude 询问 Kubernetes 操作，这些操作通常需要大量的谷歌搜索或询问基础设施工程的同事，从而获得配置和部署问题的即时答案。</p>
<h3>开发工作流影响</h3>
<p><strong>启用了实验性方法</strong></p>
<p>他们现在使用一种“尝试并回滚”的方法，频繁提交检查点，以便测试 Claude 的自主实现尝试，并在需要时恢复，从而实现更具实验性的开发。</p>
<p><strong>加速了文档工作</strong></p>
<p>Claude Code 自动添加有用的注释，节省了大量的文档时间，尽管他们注意到它有时会在奇怪的地方添加注释或使用有问题的代码组织方式。</p>
<p><strong>有局限性的提速</strong></p>
<p>虽然 Claude Code 可以在“相对较少的时间”内实现中小型 PR，但他们承认，它只有大约三分之一的时间能在第一次尝试时成功，需要额外的指导或手动干预。</p>
<h3>来自 RL 工程团队的顶级建议</h3>
<p><strong>为特定模式定制你的 Claude.md 文件</strong></p>
<p>在你的 Claude.md 文件中添加指令，以防止 Claude 犯重复的工具调用错误，比如告诉它“运行 pytest 而不是 run，不要不必要地 cd &#8211; 只需使用正确的路径。” 这显著提高了一致性。</p>
<p><strong>使用检查点密集的工作流</strong></p>
<p>在 Claude 进行更改时定期提交你的工作，以便在实验不成功时轻松回滚。这使得开发能够采用更具实验性的方法，而没有风险。</p>
<p><strong>先尝试一次性完成，然后协作</strong></p>
<p>给 Claude 一个快速的提示，让它尝试完成整个实现。如果成功了（大约三分之一的时间），你就节省了大量时间。如果没有，那就切换到更具协作性的、引导式的方法。</p>
<h2>Claude Code 在法务领域的应用</h2>
<p>法务团队通过实验和了解 Anthropic 产品供应的愿望，发现了 Claude Code 的潜力。此外，一名团队成员有一个个人用例，涉及为家庭和工作创建可访问性工具原型，展示了该技术为非开发人员提供的力量。</p>
<h3>主要 Claude Code 用例</h3>
<p><strong>为家庭成员定制的可访问性解决方案</strong></p>
<p>团队成员为有医疗诊断导致说话困难的家庭成员构建了沟通助手。在短短一个小时内，一个人使用原生的语音到文本功能创建了一个预测性文本应用，该应用能建议回复并使用语音库说出它们，解决了语言治疗师推荐的现有可访问性工具中的空白。</p>
<p><strong>法务部门工作流自动化</strong></p>
<p>团队创建了原型“电话树”系统，以帮助团队成员在 Anthropic 与合适的律师联系，展示了法务部门如何在没有传统开发资源的情况下为常见任务构建自定义工具。</p>
<p><strong>团队协调工具</strong></p>
<p>经理们构建了 G Suite 应用，可以自动化每周的团队更新，并跨产品跟踪法律审查状态，使律师能够通过简单的按钮点击而不是电子表格管理来快速标记需要审查的项目。</p>
<p><strong>用于解决方案验证的快速原型制作</strong></p>
<p>他们使用 Claude Code 快速构建功能原型，可以向领域专家展示（比如向 UCSF 的专家展示可访问性工具），以验证想法并在投入更多时间之前识别现有解决方案。</p>
<h3>工作风格与影响</h3>
<p><strong>在 Claude.ai 中规划，在 Claude Code 中构建</strong></p>
<p>他们使用一个两步流程，首先在 Claude.ai 中进行头脑风暴和规划，然后转移到 Claude Code 进行实现，要求它放慢速度并逐步工作，而不是一次性输出所有内容。</p>
<p><strong>视觉优先的方法</strong></p>
<p>他们经常使用截图向 Claude Code 展示他们想要的界面外观，然后根据视觉反馈进行迭代，而不是用文字描述功能。</p>
<p><strong>原型驱动的创新</strong></p>
<p>他们强调克服分享“傻瓜式”或“玩具式”原型的恐惧，因为这些演示能激励他人看到他们以前没有想到的可能性。</p>
<h3>安全与合规意识</h3>
<p><strong>MCP 集成问题</strong></p>
<p>产品律师使用 Claude Code 立即识别深度 MCP 集成的安全影响，指出随着 AI 工具访问更敏感的系统，保守的安全态势将构成障碍。</p>
<p><strong>合规工具的优先级</strong></p>
<p>他们倡导随着 AI 能力的扩展迅速构建合规工具，认识到创新与风险管理之间的平衡。</p>
<h3>来自法务部门的顶级建议</h3>
<p><strong>首先在 Claude.ai 中进行详尽规划</strong></p>
<p>在转向 Claude Code 之前，使用 Claude 的对话界面充实你的整个想法。然后要求 Claude 将所有内容总结成一个分步的实现提示。</p>
<p><strong>增量和可视化地工作</strong></p>
<p>要求 Claude 放慢速度，一次实现一个步骤，这样你就可以复制粘贴而不会被淹没。大量使用截图来展示你想要的界面外观。</p>
<p><strong>尽管不完美也要分享原型</strong></p>
<p>克服隐藏“玩具”项目或未完成工作的冲动。分享原型有助于他人看到可能性，并激发通常不互动的部门之间的创新。</p>
<p><strong><a href="https://www.anthropic.com/claude-code">立即开始使用 Claude Code</a>吧。</strong></p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/gemini-cli-starting-guide-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2025, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2025/07/25/how-anthropic-teams-use-claude-code/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Go 1.19新特性前瞻</title>
		<link>https://tonybai.com/2022/06/12/go-1-19-foresight/</link>
		<comments>https://tonybai.com/2022/06/12/go-1-19-foresight/#comments</comments>
		<pubDate>Sun, 12 Jun 2022 09:06:20 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[atomic]]></category>
		<category><![CDATA[build-tag]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Cpp]]></category>
		<category><![CDATA[EDNS]]></category>
		<category><![CDATA[fd]]></category>
		<category><![CDATA[flag]]></category>
		<category><![CDATA[Function]]></category>
		<category><![CDATA[GC]]></category>
		<category><![CDATA[generics]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go1.18]]></category>
		<category><![CDATA[go1.19]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[Gopher]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[loong64]]></category>
		<category><![CDATA[memory-model]]></category>
		<category><![CDATA[Method]]></category>
		<category><![CDATA[mmap]]></category>
		<category><![CDATA[race]]></category>
		<category><![CDATA[receiver]]></category>
		<category><![CDATA[RussCox]]></category>
		<category><![CDATA[SetMemoryLimit]]></category>
		<category><![CDATA[sync]]></category>
		<category><![CDATA[sync/atomic]]></category>
		<category><![CDATA[TextVar]]></category>
		<category><![CDATA[thread-sanitizer]]></category>
		<category><![CDATA[内存模型]]></category>
		<category><![CDATA[函数]]></category>
		<category><![CDATA[原子操作]]></category>
		<category><![CDATA[同步]]></category>
		<category><![CDATA[垃圾回收]]></category>
		<category><![CDATA[数据竞态]]></category>
		<category><![CDATA[文件描述符]]></category>
		<category><![CDATA[文档]]></category>
		<category><![CDATA[方法]]></category>
		<category><![CDATA[泛型]]></category>
		<category><![CDATA[注释]]></category>
		<category><![CDATA[竞态]]></category>
		<category><![CDATA[语言规范]]></category>
		<category><![CDATA[龙芯]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=3584</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2022/06/12/go-1-19-foresight 美国时间2022年5月7日，Go 1.19版本开发分支进入新特性冻结(freeze)阶段，即只能修Bug，不能再向Go 1.19版本中增加新特性了。由于上一个版本Go 1.18因引入泛型改动较大，推迟了一个月发布，这直接导致了Go 1.19版本的开发周期被缩短。 虽然开发周期少了近一个月，但Go 1.19版本仍然会按计划在2022年8月份发布。而Go 1.19的第一个beta版也于今天凌晨发布了。Go 1.19版本都有哪些重要变化呢，我通过这篇文章带大家先睹为快。 注1：版本特性变化以最终发布为准！ 注2：本文仅是前瞻，不会过于深入细节。细节待Go 1.19正式发布后再聊。 泛型问题的fix 尽管Go核心团队在Go 1.18泛型上投入了很多精力，但Go 1.18发布后泛型这块依然有已知的天生局限，以及后续逐渐发现的一些问题，而Go 1.19版本将继续打磨Go泛型，并重点fix Go 1.18中发现的泛型问题。目前Go 1.19开发版本中大约有5-6个泛型问题待解决。之前谈到的可能放开一些泛型约束，在Go 1.19估计不会如期兑现了。 不过可以确定的是Go 1.19将包含Go语法规范中的一处关于泛型的修正，即由下面表述： The scope of an identifier denoting a type parameter of a function or declared by a method receiver is the function body and all parameter lists of the function.(译文：一个用于表示函数的类型参数或由方法接收器声明的类型参数的标识符的作用域范围包括函数体和函数的所有形式参数列表。) [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/go-1-19-foresight-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2022/06/12/go-1-19-foresight">本文永久链接</a> &#8211; https://tonybai.com/2022/06/12/go-1-19-foresight</p>
<p>美国时间2022年5月7日，Go 1.19版本开发分支进入新特性冻结(freeze)阶段，即只能修Bug，不能再向Go 1.19版本中增加新特性了。由于上一个版本<a href="https://tonybai.com/2022/04/20/some-changes-in-go-1-18">Go 1.18</a>因引入泛型改动较大，推迟了一个月发布，这直接导致了Go 1.19版本的开发周期被缩短。</p>
<p>虽然开发周期少了近一个月，但Go 1.19版本仍然会按计划在2022年8月份发布。而<a href="https://github.com/golang/go/releases/tag/go1.19beta1">Go 1.19的第一个beta版</a>也于今天凌晨发布了。Go 1.19版本都有哪些重要变化呢，我通过这篇文章带大家先睹为快。</p>
<blockquote>
<p>注1：版本特性变化以最终发布为准！<br />
  注2：本文仅是前瞻，不会过于深入细节。细节待Go 1.19正式发布后再聊。</p>
</blockquote>
<h3>泛型问题的fix</h3>
<p>尽管Go核心团队在Go 1.18泛型上投入了很多精力，但Go 1.18发布后泛型这块依然有已知的天生局限，以及后续逐渐发现的一些问题，而Go 1.19版本将<a href="https://github.com/golang/go/issues?q=is%3Aopen+is%3Aissue+label%3Agenerics+milestone%3AGo1.19">继续打磨Go泛型</a>，并重点fix Go 1.18中发现的泛型问题。目前<a href="https://github.com/golang/go/issues?q=is%3Aissue+label%3Agenerics+milestone%3AGo1.19+is%3Aopen">Go 1.19开发版本中大约有5-6个泛型问题待解决</a>。之前谈到的可能放开一些泛型约束，在Go 1.19估计不会如期兑现了。</p>
<p>不过可以确定的是Go 1.19将包含<a href="https://github.com/golang/go/issues/52038">Go语法规范中的一处关于泛型的修正</a>，即由下面表述：</p>
<blockquote>
<p>The scope of an identifier denoting a type parameter of a function or declared by a method receiver is the function body and all parameter lists of the function.(译文：一个用于表示函数的类型参数或由方法接收器声明的类型参数的标识符的作用域范围包括函数体和函数的所有形式参数列表。)</p>
</blockquote>
<p>改为下面更新版的表述：</p>
<blockquote>
<p>The scope of an identifier denoting a type parameter of a function or declared by a method receiver starts after the function name and ends at the end of the function body.(译文：一个用于表示函数的类型参数或由方法接收器声明的类型参数的标识符的作用域始于函数名，终止于函数体末尾。)</p>
</blockquote>
<p>这样一个改动，使得原本在当前版本Go编译器(Go 1.18.x)下编译报错的源码，在Go 1.19版本中可以正常编译通过：</p>
<pre><code>type T[T any] struct {}
func (T[T]) m() {} // error: T is not a generic type
</code></pre>
<h3><a href="https://github.com/golang/go/issues/50859">修订Go memory model</a></h3>
<p><a href="https://go.dev/ref/mem">Go memory model</a>是Go文档中最抽象的一篇，没有之一！随着Go的演进，原先的Go memory model描述有很多地方不够正式，也缺少对一些同步机制的说明，如atomic等。</p>
<p>这次修订，参考了Hans-J. Boehm和Sarita V. Adve在<a href="https://www.hpl.hp.com/techreports/2008/HPL-2008-56.pdf">“Foundations of the C++ Concurrency Memory Model，(PLDI 2008)”</a>中对C++ memory model的描述方式，对Go memory model做了更正式的整体描述，增加了对multiword竞态、runtime.SetFinalizer、更多sync类型、atomic操作以及编译器优化方面的描述。</p>
<h3>修订go doc comment格式</h3>
<p>Go内置了将comment直接提取为包文档的能力，这与其他语言通过第三方工具生成文档不同。go doc comment为Gopher提供了很大便利。但go doc comment设计于2009年，有些过时。对很多呈现形式的支持不够或缺少更为精确的格式描述，这次Russ Cox主导了go doc comment的修订，<a href="https://github.com/golang/go/issues/51082">增加了对超链、列表、标题、标准库API引用等格式支持</a>，修订后的go doc comment并非markdown语法，但从markdown语法中做了借鉴，同时兼容老comment格式。下面是Russ Cox提供的一些新doc comment的渲染后的效果图：</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-1-19-foresight-2.png" alt="" /><br />
<img src="https://tonybai.com/wp-content/uploads/go-1-19-foresight-3.png" alt="" /><br />
<img src="https://tonybai.com/wp-content/uploads/go-1-19-foresight-4.png" alt="" /></p>
<p>同时，Go团队还提供了go/doc/comment包，gopher使用它可以轻松解析go doc comment。</p>
<h3>runtime.SetMemoryLimit</h3>
<p>在Go 1.19中，一个新的runtime.SetMemoryLimit函数以及一个GOMEMLIMIT环境变量被引入。有了这个memory软限制，Go运行时将通过限制堆的大小，以及更积极地将内存返回给底层os，来试图维持这个内存限制，以尽量避免Go程序因分配heap过多，超出系统内存资源限制而被kill。</p>
<p>默认memory limit是math.MaxInt64。一旦通过SetMemoryLimit自行设定limit，那么Go运行时将尊重这个memory limit，通过调整GC回收频率以及及时将内存返还给os来保证go运行时掌控的内存总size在limit之下。</p>
<p>注意：limit限制的是go runtime掌控的内存总量，对于开发者自行从os申请的内存(比如通过mmap)则不予考虑。limit的具体措施细节可以参考<a href="https://github.com/golang/proposal/blob/master/design/48409-soft-memory-limit.md">该proposal design文档</a>。</p>
<p>另外要注意的是：该limit不能100%消除out-of-memory的情况。</p>
<h3>Go 1.19在启动时将默认提高打开文件的限值</h3>
<p>经调查，一些系统对打开的文件数量设置了一个人为的soft限制, 主要是为了与使用select和其硬编码的最大文件描述符（由 fd_set 的大小限制）的代码兼容。通常限制为1024，有的更小，比如256。这样即便是gofmt这样的简单程序，当它们并行地遍历一个文件树时，也很容易遇到打开文件描述符超量的错误。</p>
<p>Go不使用select，所以它不应该受这些限制的影响。于是对于导入os包的go程序，<a href="https://github.com/golang/go/issues/46279">Go将在1.19中默认提高这些限制值</a>到hard limit。</p>
<h3>Go 1.19 race detector将升级到v3版thread sanitizer</h3>
<p>升级后的<a href="https://github.com/golang/go/issues/49761">新版race detector</a>的race检测性能相对于上一版将提升1.5倍-2倍，内存开销减半，并且没有对goroutine的数量的上限限制。</p>
<blockquote>
<p>注：thread sanitizer检测数据竞态的工作原理：记录每一个内存访问的信息，并检测线程对这块内存的访问是否存在竞争。基于这种原理，我们也可以知道一旦开启race detect，Go程序的执行效率将受到很大影响，运行的开销将大幅增加。v3版thread sanitizer虽然得到了优化，但对程序的总体影响还是存在的并且依旧很大。</p>
</blockquote>
<h3>Go 1.19增加”unix” build tag</h3>
<p>Go 1.19将增加”unix”构建标签：</p>
<pre><code>//go:build unix
</code></pre>
<p>等价于</p>
<pre><code>//go:build aix || linux || darwin || dragonfly || freebsd || openbsd || netbsd || solaris
</code></pre>
<p>不过要注意，”*_unix.go”还保留原语义，不能被识别，以便向后兼容现有文件，<a href="https://github.com/golang/go/issues/20322#issuecomment-1057682650">尤其是go标准库之外的使用</a>。</p>
<h3>标准库的一些变化</h3>
<h4>net软件包将使用EDNS</h4>
<p>在Go 1.19中，<a href="https://github.com/golang/go/issues/51153">net软件包将使用EDNS来增加DNS数据包的大小</a>，以遵守现代DNS标准和实现。这应该有助于解决一些DNS服务器的问题。</p>
<h4>flag包增加TextVar函数</h4>
<p><a href="https://github.com/golang/go/issues/45754">Go flag包增加TextVar函数</a>，这样flag包便可以与任何实现了encoding.Text{Marshaler,Unmarshaler}的Go类型集成。比如：</p>
<pre><code>flag.TextVar(&amp;ipaddr, "ipaddr", net.IPv4(192, 168, 0, 1), "what server to connect to?") // 与net.IPv4类型
flag.TextVar(&amp;start, "start", time.Now(), "when should we start processing?") // 与time.Time类型
</code></pre>
<h3>其它</h3>
<ul>
<li>在linux上，Go正式支持64位龙芯cpu架构 (GOOS=linux, GOARCH=loong64)。</li>
<li>当Go程序空闲时，Go GC进入到周期性的GC循环的情况下(2分钟一次)，Go运行时现在会在idle的操作系统线程上安排更少的GC worker goroutine，减少空闲时Go应用对os资源的占用。</li>
<li>Go行时将根据goroutine的历史平均栈使用率来分配初始goroutine栈，避免了一些goroutine的最多2倍的goroutine栈空间浪费。</li>
<li>sync/atomic包增加了新的高级原子类型Bool, Int32, Int64, Uint32, Uint64, Uintptr和Pointer，提升了使用体验。</li>
<li>Go 1.19中Go编译器使用jump table重新实现了针对大整型数和string类型的switch语句，平均性能提升20%左右。</li>
</ul>
<h2>小结</h2>
<p>相对于Go 1.18，Go 1.19的确是一个“小版本”。但Go 1.19对memory model的更新、SetMemoryLimit的加入、go doc comment的修订以及对go runtime的持续打磨依然可以让gopher们产生一丝丝“小兴奋”，<strong>尤其是SetMemoryLimit的加入，是否能改善Go应用因GC不及时被kill的情况呢</strong>，让我们拭目以待。</p>
<p>Go 1.19的里程碑在<a href="https://github.com/golang/go/milestone/224">这里</a>，所有feature和fix大家可以在该里程碑中看到。</p>
<hr />
<p><a href="https://wx.zsxq.com/dweb2/index/group/51284458844544">“Gopher部落”知识星球</a>旨在打造一个精品Go学习和进阶社群！高品质首发Go技术文章，“三天”首发阅读权，每年两期Go语言发展现状分析，每天提前1小时阅读到新鲜的Gopher日报，网课、技术专栏、图书内容前瞻，六小时内必答保证等满足你关于Go语言生态的所有需求！2022年，Gopher部落全面改版，将持续分享Go语言与Go应用领域的知识、技巧与实践，并增加诸多互动形式。欢迎大家加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-tribe-zsxq-small-card.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/go-programming-from-beginner-to-master-qr.png" alt="img{512x368}" /></p>
<p><img src="http://image.tonybai.com/img/tonybai/go-first-course-banner.png" alt="img{512x368}" /><br />
<img src="http://image.tonybai.com/img/tonybai/imooc-go-column-pgo-with-qr.jpg" alt="img{512x368}" /></p>
<p><a href="https://tonybai.com/">我爱发短信</a>：企业级短信平台定制开发专家 https://tonybai.com/。smspush : 可部署在企业内部的定制化短信平台，三网覆盖，不惧大并发接入，可定制扩展； 短信内容你来定，不再受约束, 接口丰富，支持长短信，签名可选。2020年4月8日，中国三大电信运营商联合发布《5G消息白皮书》，51短信平台也会全新升级到“51商用消息平台”，全面支持5G RCS消息。</p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>Gopher Daily(Gopher每日新闻)归档仓库 &#8211; https://github.com/bigwhite/gopherdaily</p>
<p>我的联系方式：</p>
<ul>
<li>微博：https://weibo.com/bigwhite20xx</li>
<li>微信公众号：iamtonybai</li>
<li>博客：tonybai.com</li>
<li>github: https://github.com/bigwhite</li>
<li>“Gopher部落”知识星球：https://public.zsxq.com/groups/51284458844544</li>
</ul>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2022, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2022/06/12/go-1-19-foresight/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Go正走在成为下一个企业级编程语言的轨道上</title>
		<link>https://tonybai.com/2019/05/03/go-is-on-a-trajectory-to-become-the-next-enterprise-programming-language/</link>
		<comments>https://tonybai.com/2019/05/03/go-is-on-a-trajectory-to-become-the-next-enterprise-programming-language/#comments</comments>
		<pubDate>Fri, 03 May 2019 06:01:34 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[ARM]]></category>
		<category><![CDATA[BSD]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Compile]]></category>
		<category><![CDATA[Cpp]]></category>
		<category><![CDATA[CSP]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[error-handling]]></category>
		<category><![CDATA[etcd]]></category>
		<category><![CDATA[Git]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Go2]]></category>
		<category><![CDATA[gofmt]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[hotspot]]></category>
		<category><![CDATA[istio]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[JIT]]></category>
		<category><![CDATA[JVM]]></category>
		<category><![CDATA[KenThompson]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[macos]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[Opensource]]></category>
		<category><![CDATA[Ops]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[reader]]></category>
		<category><![CDATA[RobertGriesemer]]></category>
		<category><![CDATA[RobPike]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[terraform]]></category>
		<category><![CDATA[vendor]]></category>
		<category><![CDATA[vm]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[writer]]></category>
		<category><![CDATA[云计算]]></category>
		<category><![CDATA[企业级]]></category>
		<category><![CDATA[包管理]]></category>
		<category><![CDATA[多核]]></category>
		<category><![CDATA[并发]]></category>
		<category><![CDATA[并行]]></category>
		<category><![CDATA[开源]]></category>
		<category><![CDATA[文档]]></category>
		<category><![CDATA[架构师]]></category>
		<category><![CDATA[注释]]></category>
		<category><![CDATA[维护性]]></category>
		<category><![CDATA[编译]]></category>
		<category><![CDATA[翻译]]></category>
		<category><![CDATA[虚拟机]]></category>
		<category><![CDATA[错误处理]]></category>
		<category><![CDATA[面向对象]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=2711</guid>
		<description><![CDATA[发展演化了十年的Go语言已经被证明了是云计算时代的首选编程语言，但Go的用武之地显然不局限于此。Kevin Goslar近期在Hacker Noon发表了一篇名为：《Go is on a Trajectory to Become the Next Enterprise Programming Language》的文章，阐述了Go可能成为下一个企业编程语言的理由，这里是那篇文章的中文译文，分享给大家。 摘要 Go是一种专门为大规模软件开发而设计的编程语言。它提供了强大的开发体验并避免了现有编程语言存在的许多问题。这些因素使其成为最有可能在未来替代Java主导企业软件平台的候选者之一。对于那些寻求在未来几十年内构建大规模云基础架构的安全和前瞻性技术的公司和开源计划而言，我建议它们将Go视为其主要的编程语言。Go的优势如下： 基于现实世界的经验 专注于大型工程 专注于可维护性 保持简单明了 使事情显式且明显 很容易学习 仅提供了一种做事方式 支持简单地内置并发 提供面向计算的语言原语 使用OO &#8211; 好的部分 拥有现代化的标准库 强制执行标准化格式 有一个非常快的编译器 使交叉编译变得容易 执行得非常快 需要较小的内存占用 部署规模小 部署完全独立 支持vendor依赖 提供兼容性保证 鼓励提供良好的文档 商业支持的开源 请继续阅读有关上述每个优势点的更多详细信息。然而，在进入Go之前，你应该注意： 不成熟的库 即将到来的改变 没有“硬实时”支持 简介 Go是Google开发的一种编程语言，在过去几年中取得了很大的成功。大部分现代云计算，网络和DevOps平台都是Go语言编写的，例如：Docker、Kubernetes、Terraform、ETCD或istio等。许多公司也将它用于通用软件开发。Go所具备的功能让这些项目吸引了大量用户，而Go的易用性也使得这些项目有了很多的贡献者。 Go的优势来自于简单和经过验证的想法的结合，同时避免了其他语言中出现的许多问题。这篇博客文章概述了Go背后的一些设计原则和工程智慧，并展示它们是如何结合在一起的 &#8211; 它们使Go成为下一代大型软件开发平台的优秀候选者。许多编程语言在个别领域都比较强大，但是在将所有领域都结合起来时，没有其他语言能够如此一致地“得分”，特别是在大型软件工程方面。 基于现实世界的经验 Go是由经验丰富的软件行业资深人士创建的，他们长期以来一直感受到现有语言的缺点带来的痛苦。几十年前，Rob Pike和Ken Thompson在Unix，C和Unicode的发明中发挥了重要作用。在实现了用于JavaScript和Java的V8和HotSpot虚拟机之后，Robert Griesemer在编译器和垃圾收集方面拥有着数十年的经验。在太多次的不得不等待他们的谷歌规模的C++/Java代码库的编译过程的推动下，他们开始着手创建一门新的编程语言，这门语言中凝聚了他们通过编写半个世纪代码过程中所学到的一切。 专注于大型工程 [...]]]></description>
			<content:encoded><![CDATA[<p>发展演化了<a href="https://tonybai.com/2017/09/24/go-ten-years-and-climbing/">十年的Go语言</a>已经被证明了是云计算时代的首选编程语言，但<a href="https://tonybai.com/tag/go">Go</a>的用武之地显然不局限于此。Kevin Goslar近期在<a href="https://hackernoon.com/">Hacker Noon</a>发表了一篇名为：<a href="https://hackernoon.com/go-is-on-a-trajectory-to-become-the-next-enterprise-programming-language-3b75d70544e">《Go is on a Trajectory to Become the Next Enterprise Programming Language》</a>的文章，阐述了Go可能成为下一个企业编程语言的理由，这里是那篇文章的中文译文，分享给大家。</p>
<p><img src="https://tonybai.com/wp-content/uploads/go-enterprise-programming-lang-1.png" alt="img{512x368}" /></p>
<h2>摘要</h2>
<p>Go是一种专门为大规模软件开发而设计的编程语言。它提供了强大的开发体验并避免了现有编程语言存在的许多问题。这些因素使其成为最有可能在未来替代Java主导企业软件平台的候选者之一。对于那些寻求在未来几十年内构建大规模云基础架构的安全和前瞻性技术的公司和开源计划而言，我建议它们将Go视为其主要的编程语言。Go的优势如下：</p>
<ul>
<li>基于现实世界的经验</li>
<li>专注于大型工程</li>
<li>专注于可维护性</li>
<li>保持简单明了</li>
<li>使事情显式且明显</li>
<li>很容易学习</li>
<li>仅提供了一种做事方式</li>
<li>支持简单地内置并发</li>
<li>提供面向计算的语言原语</li>
<li>使用OO &#8211; 好的部分</li>
<li>拥有现代化的标准库</li>
<li>强制执行标准化格式</li>
<li>有一个非常快的编译器</li>
<li>使交叉编译变得容易</li>
<li>执行得非常快</li>
<li>需要较小的内存占用</li>
<li>部署规模小</li>
<li>部署完全独立</li>
<li>支持vendor依赖</li>
<li>提供兼容性保证</li>
<li>鼓励提供良好的文档</li>
<li>商业支持的开源</li>
</ul>
<p>请继续阅读有关上述每个优势点的更多详细信息。然而，在进入Go之前，你应该注意：</p>
<ul>
<li>不成熟的库</li>
<li>即将到来的改变</li>
<li>没有“硬实时”支持</li>
</ul>
<h2>简介</h2>
<p>Go是Google开发的一种编程语言，在过去几年中取得了很大的成功。大部分现代云计算，网络和DevOps平台都是Go语言编写的，例如：<a href="https://tonybai.com/tag/docker">Docker</a>、<a href="https://tonybai.com/tag/kubernetes">Kubernetes</a>、<a href="https://www.terraform.io/">Terraform</a>、<a href="https://coreos.com/etcd/">ETCD</a>或<a href="https://tonybai.com/2018/01/03/an-intro-of-microservices-governance-by-istio">istio</a>等。<a href="https://github.com/golang/go/wiki/GoUsers#united-states">许多公司</a>也将它用于通用软件开发。Go所具备的功能让这些项目吸引了大量用户，而Go的易用性也使得这些项目有了很多的贡献者。</p>
<p>Go的优势来自于简单和经过验证的想法的结合，同时避免了其他语言中出现的许多问题。这篇博客文章概述了Go背后的一些设计原则和工程智慧，并展示它们是如何结合在一起的 &#8211; 它们使Go成为下一代大型软件开发平台的优秀候选者。许多编程语言在个别领域都比较强大，但是在将所有领域都结合起来时，没有其他语言能够如此一致地“得分”，特别是在大型软件工程方面。</p>
<h2>基于现实世界的经验</h2>
<p>Go是由经验丰富的软件行业资深人士创建的，他们长期以来一直感受到现有语言的缺点带来的痛苦。几十年前，<a href="https://en.wikipedia.org/wiki/Rob_Pike">Rob Pike</a>和<a href="https://en.wikipedia.org/wiki/Ken_Thompson">Ken Thompson</a>在Unix，C和Unicode的发明中发挥了重要作用。在实现了用于JavaScript和Java的V8和HotSpot虚拟机之后，<a href="https://github.com/griesemer">Robert Griesemer</a>在编译器和垃圾收集方面拥有着数十年的经验。在太多次的不得不等待他们的谷歌规模的<a href="http://radar.oreilly.com/2012/09/golang.html">C++/Java代码库的编译过程</a>的推动下，他们开始着手创建一门新的编程语言，这门语言中凝聚了他们通过编写半个世纪代码过程中所学到的一切。</p>
<h2>专注于大型工程</h2>
<p>几乎任何编程语言都可以成功构建小型工程项目。当成千上万的开发人员在数十年的持续时间压力下在包含数千万行代码的大量代码库上进行协作时，真正痛苦的问题就会发生。这会导致以下问题：</p>
<ul>
<li>超长的编译时长会中断开发过程</li>
<li>代码库由几个人/团队/部门/公司拥有，混合了不同的编程风格</li>
<li>该公司雇佣了数千名工程师，架构师，测试人员，Ops专家，审计员，实习生等，他们需要了解代码库，但需要具有广泛的编码经验</li>
<li>依赖于许多外部库或运行时，其中一些不再以其最初的形式存在</li>
<li>每行代码在代码库的生命周期内平均被<a href="https://www.ybrikman.com/writing/2018/08/12/the-10-to-1-rule-of-writing-and-programming">重写了10次</a>，留下了疤痕，瑕疵和<a href="https://codeclimate.com/blog/are-you-experiencing-technical-drift">技术偏移</a></li>
<li>文档不完整</li>
</ul>
<p>Go专注于<a href="https://talks.golang.org/2012/splash.article">减轻这些大规模的工程难题</a>，有时是以使小型工程变得更加繁琐为代价，例如在这里和那里需要一些额外的代码。</p>
<h2>专注于可维护性</h2>
<p>Go强调尽可能多地将工作转交到自动代码维护工具中。Go工具链提供了最常用的功能，如格式化代码和自动package导入、查找符号的定义和用法、简单的重构以及代码味道的识别。由于标准化的代码格式化和单一的惯用方式，机器生成的代码更改看起来非常接近Go中人为生成的更改。并而使用类似的模式，使得人和机器的协作更加无缝。</p>
<h2>保持简单直接</h2>
<blockquote>
<p>初级程序员为简单问题创建简单的解决方案。高级程序员为复杂问题创建复杂的解决方案。伟大的程序员找到复杂问题的简单解决方案。-  <a href="http://www.chc-3.com/pub/beautifulsoftware_v10.htm">查尔斯康奈尔</a></p>
</blockquote>
<p>很多人都对Go不包含他们喜欢的其他语言概念感到惊讶。Go确实是一种非常小而简单的语言，只包含最少的正交和经过验证的概念。这鼓励开发人员以最少的认知开销编写最简单的代码，以便许多其他人可以理解并使用它。</p>
<h2>使事情显式而明显</h2>
<blockquote>
<p>良好的代码是显而易见的，避免聪明，模糊的语言功能，扭曲的控制流和间接性。</p>
</blockquote>
<p>许多语言都致力于使编写代码变得高效。然而，在其生命周期中，人们将花费大约（100倍）的时间阅读代码，而不是首先编写所需的代码。例如，审查，理解，调试，更改，重构或重用它。在查看代码时，通常只能看到并理解它的一小部分，通常没有对整个代码库的完整理解。为了解释这一点，Go将一切都显式化了。</p>
<p>一个例子是错误处理。让异常在各个点中断代码并使沿着调用链处理可能会更容易。Go需要<a href="https://tour.golang.org/methods/19">手动处理或返回每个错误</a>。这使得它可以准确地显示代码可以被中断的位置以及如何处理或包装错误。总的来说，这使得错误处理更容易编写，但更容易理解。</p>
<h2>简单易学</h2>
<p>Go非常小而且简单，可以在短短几天内研究整个语言及其基本概念。根据我们的经验，经过不超过一周的培训（与其他语言的以月为单位相比），初学者可以理解Go专家编写的代码，并为此做出贡献。为了方便大量人群，Go网站提供了所需的所有教程和深入的文章。这些教程在浏览器中运行，允许人们在将Go安装到本地计算机上之前学习和使用Go。</p>
<h2>一种做事方式</h2>
<p>Go语言通过个人自我表达赋予团队合作能力。</p>
<p>在Go（和Python）中，所有语言特征都是正交的并且彼此互补，通常做某事只有一种方法。如果您要求10位Python或Go程序员解决问题，您将获得10个相对类似的解决方案。不同的程序员在彼此的代码库中感觉更有家的感觉。在查看其他人的代码时，每分钟的<a href="https://www.osnews.com/story/19266/wtfsm">WTF</a>更少，而且人们的工作更好地融合在一起，从而形成一个人人都为之骄傲并且喜欢工作的一致性。这避免了大规模的工程问题，例如：</p>
<ul>
<li>开发人员将良好的工作代码视为“混乱”，并要求在他们可以使用之前重写它，因为他们不会像原作者那样思考。</li>
<li>不同的团队成员在该语言的不同子集中编写相同代码库的部分内容。</li>
</ul>
<p><img src="https://tonybai.com/wp-content/uploads/go-enterprise-programming-lang-2.jpeg" alt="img{512x368}" /><br />
来源：https：//www.osnews.com/story/19266/wtfsm</p>
<h2>简单，内置并发</h2>
<blockquote>
<p>Go专为现代多核硬件而设计。</p>
</blockquote>
<p>目前使用的大多数编程语言（Java，JavaScript，Python，Ruby，C，C ++）都是在20世纪80年代到2000年代设计的，当时大多数CPU只有一个计算核心。这就是为什么它们本质上是单线程的，并将并行化视为事后增加的边缘情况，通过诸如线程和同步点之类的附加组件实现，这些附加组件既麻烦又难以正确使用。第三方库提供了更简单的并发形式，如Actor模型，但总有多个选项可用，导致语言生态系统碎片化。今天的硬件拥有越来越多的计算内核，软件必须并行化才能在其上高效运行。Go是在多核CPU时代编写的，并且在语言中内置了简单，高级的<a href="https://en.wikipedia.org/wiki/Communicating_sequential_processes">CSP风格</a>的并发特性。</p>
<h2>面向计算的语言原语</h2>
<p>在基础层面上，计算机系统接收数据，处理它（通常经过几个步骤），并输出结果数据。例如，Web服务器从客户端接收HTTP请求，并将其转换为一系列数据库或后端调用。一旦这些调用返回，它就会将接收到的数据转换为HTML或JSON并将其输出给调用者。Go的内置语言原语直接支持这种范例：</p>
<ul>
<li>结构体代表数据</li>
<li>reader和writer代表流式IO</li>
<li>函数处理数据</li>
<li>goroutines提供（几乎无限制的）并发</li>
<li>通道用于管理并发处理步骤之间的数据</li>
</ul>
<p>由于所有计算原语都是由语言以直接的形式提供的，因此Go源代码可以更直接地表达服务器执行的操作。</p>
<h2>OO &#8211; 好的部分</h2>
<p><img src="https://tonybai.com/wp-content/uploads/go-enterprise-programming-lang-3.gif" alt="img{512x368}" /><br />
在基类中改变某些东西的副作用</p>
<p>面向对象非常有用。这几十年OO的应用是富有成效的，并且让我们了解它的哪些部分比其他部分可以更好地扩展。基于这些认知，Go采用面向对象的新方法。它保留了封装和消息传递等优点。Go避免了继承，因为它现在被认为是<a href="https://www.javaworld.com/article/2073649/why-extends-is-evil.html">有害的</a>，Go为组合提供<a href="https://golang.org/doc/effective_go.html#embedding">头等的支持</a>。</p>
<h2>现代标准库</h2>
<p>许多当前使用的编程语言（Java，JavaScript，Python，Ruby）是在互联网成为当今无处不在的计算平台之前设计的。因此，这些语言的标准库仅为未针对现代互联网优化的网络提供相对通用的支持。Go是十年前创建的，当时互联网已经全面展开。Go的标准库允许在没有第三方库的情况下创建更复杂的网络服务。这可以防止使用第三方库的常见问题：</p>
<ul>
<li>碎片化：实现相同功能的总有多种选择</li>
<li>膨胀：库通常实现的不仅仅是它们的用途</li>
<li>依赖地狱：库通常依赖于特定版本的其他库</li>
<li>质量未知：第三方代码可能具有可疑的质量和安全性</li>
<li>未知支持：第三方库的开发可以随时停止</li>
<li>意外更改：第三方库通常不像标准库那样进行严格的版本管理</li>
</ul>
<p>Russ Cox的<a href="https://research.swtch.com/deps">更多背景</a>信息。</p>
<h2>标准化格式</h2>
<blockquote>
<p>Gofmt的风格是没有人喜欢的，但gofmt是每个人的最爱。 &#8211; Rob Pike</p>
</blockquote>
<p>Gofmt是一种以标准化方式格式化Go代码的程序。它不是最漂亮的格式化方式，而是最简单，最不讨厌的方式。标准化的源代码格式化具有惊人的积极影响：</p>
<ul>
<li>重点讨论重要主题：它消除了围绕标签与空格，缩进深度，每行长度，空行，花括号放置等的一系列<a href="https://en.wikipedia.org/wiki/Law_of_triviality">无意义的争论</a>。</li>
<li>开发人员在彼此的代码库中感到宾至如归，因为其他代码看起来很像他们编写的代码。每个人都喜欢自由地按照自己喜欢的方式格式化代码，但如果其他人冒昧地按照他们自己喜欢的方式格式化>代码，那么每个人都讨厌它。</li>
<li>自动代码更改不会弄乱手写代码的格式，例如通过引入意外的空白更改。</li>
</ul>
<p>许多其他语言社区现在正在开发gofmt等价物。当构建为第三方解决方案时，通常会有几种竞争格式标准。例如，JavaScript世界提供<a href="https://prettier.io/">Prettier</a>和<a href="https://standardjs.com/">StandardJS</a>。可以一起使用其中之一或两者。许多JS项目都没有采用它们，因为这是一个额外的决定。Go的格式化程序内置于该语言的标准工具链中，因此只有一个标准，每个人都在使用它。</p>
<h2>快速编译</h2>
<p><img src="https://tonybai.com/wp-content/uploads/go-enterprise-programming-lang-4.png" alt="img{512x368}" /><br />
来源：https://xkcd.com/303</p>
<p>大型代码库的长编译时间是引发Go语言起源的一个微小的原因。Google主要使用C++和Java，与Haskell，Scala或Rust等更复杂的语言相比，它可以相对快速地编译。尽管如此，当编译大型代码库时，即使是少量的慢速也会把人激怒，编译工作流中断导致编译延迟。Go是从头开始设计的，以使编译更有效，因此编译器速度非常快，几乎没有编译延迟。这为Go开发人员提供了类似于脚本语言的即时反馈，并具有静态类型检查的额外好处。</p>
<h2>交叉编译</h2>
<p>由于语言运行时非常简单，因此它已被移植到许多平台，如macOS，Linux，Windows，BSD，ARM等。Go可以开箱即用于编译所有这些平台的二进制文件。这使得我们可以轻松地从一台机器来进行部署。</p>
<h2>快速执行</h2>
<p>Go有着接近C的速度。与JITed(即时编译)语言（Java，JavaScript，Python等）不同，Go二进制文件不需要启动或预热时间，因为它们作为已编译和完全优化的本机代码提供。Go垃圾收集器仅以<a href="https://twitter.com/brianhatfield/status/804355831080751104">微秒</a>的指令引入可忽略的暂停。在其快速的单核性能上面，Go使得利用所有的CPU内核<a href="https://tour.golang.org/concurrency/1">更容易</a>。</p>
<h2>小内存占用</h2>
<p>像JVM，Python或Node这样的运行时不仅仅在运行时加载程序代码。它们还会加载大型且高度复杂的基础架构，以便在每次运行时编译和优化程序。这使得它们的启动时间变慢并导致它们使用大量（数百MB）的RAM。Go进程的开销较小，因为它们已经完全编译和优化，只需要运行。Go还以<a href="https://dave.cheney.net/2014/06/07/five-things-that-make-go-fast">非常节省内存的方式存储数据</a>。这在内存有限且昂贵的云环境中以及在开发期间非常重要，在开发期间我们希望在单个机器上快速启动整个堆栈，同时为其他软件留下内存。</p>
<h2>小部署规模</h2>
<p>Go二进制文件的大小非常简洁。Go应用程序的Docker镜像通常比用Java或Node编写的等效文件<a href="https://derickbailey.com/2017/03/09/selecting-a-node-js-image-for-docker">小10倍</a>，因为它不需要包含编译器，JIT，并且需要更少的运行时基础结构。这在部署大型应用程序时很重要。想象一下，将一个简单的应用程序部署到100个生产服务器上 使用Node / JVM时，我们的docker仓库必须提供100个docker镜像@ 200 MB = 20 GB(总共)。这需要镜像仓库耗费一些时间来服务。想象一下，我们希望每天部署100次。使用Go服务时，Docker镜像仓库只需提供100个Docker镜像@ 20 MB = 2 GB。可以更快，更频繁地部署大型Go应用程序，从而允许重要更新更快地实现生产。</p>
<h2>自包含部署</h2>
<p>Go应用程序部署为包含所有依赖项的单个可执行文件。不需要安装特定版本的JVM，Node或Python运行时。不必将库下载到生产服务器上。不需要对运行Go二进制文件的机器进行任何更改。甚至不需要将Go二进制文件包装到Docker中来共享它们。您只需将Go二进制文件拖放到服务器上，无论该服务器上运行的是什么，它都会在那里运行。上述描述的唯一例外是使用net和os/user包时的动态链接glibc库时。</p>
<h2>vendor依赖关系</h2>
<p>Go故意避免使用第三方库的中央存储库。Go应用程序直接链接到相应的Git存储库，并将所有相关代码下载（vendor保存）到他们自己的代码库中。这有很多好处：</p>
<ul>
<li>我们可以在使用之前查看，分析和测试第三方代码。此代码与我们自己的代码一样，是我们应用程序的一部分，应符合相同的质量，安全性和可靠性标准。</li>
<li>无需永久访问存储依赖项的各个位置。可以一次性的从任何地方（包括私人Git仓库）获取您的第三方库，并永久拥有它们。</li>
<li>在checkout后编译代码库不需要进一步下载依赖项。</li>
<li>如果互联网上某处的代码存储库突然提供不同的代码，也不会造成surprises。</li>
<li>即使软件包存储库服务性能变慢或托管软件包不再存在，部署也不会中断。</li>
</ul>
<h2>兼容性保证</h2>
<p>Go团队<a href="https://golang.org/doc/go1compat">承诺</a>，现有的程序将继续适用于新版本语言。这使得即使是大型项目也可以轻松升级到更新编译器的版本，并从新版本带来的许多性能和安全性改进中受益。同时，由于Go二进制文件包含了他们需要的所有依赖项，因此可以在同一服务器计算机上并行运行使用不同版本的Go编译器编译的二进制文件，而无需进行复杂的设置多个版本的运行时或虚拟化。</p>
<h2>文档</h2>
<p>在大型工程中，文档对于使软件易于访问和维护非常重要。与其他功能类似，Go中的文档简单实用：</p>
<ul>
<li>它嵌入在源代码中，因此两者可以同时维护。</li>
<li>它不需要特殊的语法 &#8211; 文档只是普通的源代码注释。</li>
<li>可运行的单元测试通常是最好的文档形式，所以Go允许你将它们<a href="https://blog.golang.org/examples">嵌入到文档中</a>。</li>
<li>所有文档<a href="https://blog.golang.org/godoc-documenting-go-code">实用程序</a>都内置在工具链中，因此每个人都使用它们。</li>
<li>Go linter需要导出元素的文档，以防止“文档债务”的积累。</li>
</ul>
<h2>商业支持的开源</h2>
<p>当商业实体在公开场合发展时，一些最流行和最全面设计的软件就会发生。这种设置结合了商业软件开发的优势 &#8211; 一致性和优化，使系统健壮，可靠，高效 &#8211; 具有开放式开发的优势，如来自许多行业的广泛支持，来自多个大型实体和许多用户的支持，以及长期支持，即使商业支持停止。Go就是这样开发的。</p>
<h2>缺点</h2>
<p>当然，Go并不完美，每种技术选择总是有利有弊。在进入Go之前，这里有一小部分需要考虑的方面。</p>
<h2>未成熟</h2>
<p>虽然Go的标准库在<a href="https://blog.golang.org/h2push">支持HTTP/2服务器推送</a>等许多新概念方面处于行业领先地位，但与JVM生态系统中存在的相比，用于外部API的第三方Go库可能还不那么成熟。</p>
<h2>即将到来的变化</h2>
<p>Go团队知道几乎不可能改变现有的语言元素，因此只有在完全开发后才会添加新功能。在经历了10年稳定的故意阶段后，Go团队正在考虑对语言进行<a href="https://blog.golang.org/go2draft">一系列更大的改进</a>，作为Go 2.0之旅的一部分。</p>
<h2>没有硬实时</h2>
<p>虽然Go的垃圾收集器只引入了非常短的中断，但支持硬实时需要没有垃圾收集的技术，例如Rust。</p>
<h2>结论</h2>
<p>这篇博客文章给出了一些明智的背景知识，但往往没有那么明显的选择进入Go的设计，以及当他们的代码库和团队成数量级增长时，他们将如何从许多痛苦中拯救大型工程项目。总的来说，他们将Go定位为寻求Java之外的现代编程语言的大型开发项目的绝佳选择。</p>
<hr />
<p>我的网课“<a href="https://coding.imooc.com/class/284.html">Kubernetes实战：高可用集群搭建、配置、运维与应用</a>”在慕课网上线了，感谢小伙伴们学习支持！</p>
<p><a href="https://tonybai.com/">我爱发短信</a>：企业级短信平台定制开发专家 https://tonybai.com/<br />
smspush : 可部署在企业内部的定制化短信平台，三网覆盖，不惧大并发接入，可定制扩展； 短信内容你来定，不再受约束, 接口丰富，支持长短信，签名可选。</p>
<p>著名云主机服务厂商DigitalOcean发布最新的主机计划，入门级Droplet配置升级为：1 core CPU、1G内存、25G高速SSD，价格5$/月。有使用DigitalOcean需求的朋友，可以打开这个<a href="https://m.do.co/c/bff6eed92687">链接地址</a>：https://m.do.co/c/bff6eed92687 开启你的DO主机之路。</p>
<p>我的联系方式：</p>
<p>微博：https://weibo.com/bigwhite20xx<br />
微信公众号：iamtonybai<br />
博客：tonybai.com<br />
github: https://github.com/bigwhite</p>
<p>微信赞赏：<br />
<img src="https://tonybai.com/wp-content/uploads/wechat-zanshang-code-512x512.jpg" alt="img{512x368}" /></p>
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。</p>
<p style='text-align:left'>&copy; 2019, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2019/05/03/go-is-on-a-trajectory-to-become-the-next-enterprise-programming-language/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>组织Golang代码</title>
		<link>https://tonybai.com/2014/10/21/organize-golang-code/</link>
		<comments>https://tonybai.com/2014/10/21/organize-golang-code/#comments</comments>
		<pubDate>Tue, 21 Oct 2014 04:21:31 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[CodingStyle]]></category>
		<category><![CDATA[github]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GopherCon]]></category>
		<category><![CDATA[Make]]></category>
		<category><![CDATA[Makefile]]></category>
		<category><![CDATA[Naming]]></category>
		<category><![CDATA[Package]]></category>
		<category><![CDATA[包]]></category>
		<category><![CDATA[命名]]></category>
		<category><![CDATA[构建]]></category>
		<category><![CDATA[注释]]></category>
		<category><![CDATA[版本管理]]></category>

		<guid isPermaLink="false">http://tonybai.com/?p=1576</guid>
		<description><![CDATA[本月初golang官方blog(需要自己搭梯子)上发布了一篇文章，简要介绍了近几个月Go在一 些技术会议上（比如Google I/O、Gopher SummerFest等）的主题分享并伴有slide链接。其中David Crawshaw的&#8220;Organizing Go Code&#8221;对Golang的代码风格以及工程组 织的最佳实践进行的总结很是全面和到位，这里按Slide中的思路和内容翻译和摘录如下（部分伴有我个人的若干理解）。 一、包 (Packages) 1、Golang程序由package组成 所有Go源码都是包得一部分。 每个Go源文件都起始于一条package语句。 Go应用程序的执行起始于main包。 package main import &#34;fmt&#34; func main() { &#160;&#160;&#160; fmt.Println(&#34;Hello, world!&#34;) } 对小微型程序而言，你可能只需要编写main包内的源码。 上面的HelloWorld程序import了fmt包。 函数Println定义在fmt包中。 2、一个例子：fmt包 // Package fmt implements formatted I/O. package fmt // Println formats using the default formats for its // operands and writes to standard output. func Println(a [...]]]></description>
			<content:encoded><![CDATA[<p>本月初golang<a href="http://blog.golang.org">官方blog</a>(需要自己搭梯子)上发布了一篇<a href="http://blog.golang.org/io2014">文章</a>，简要介绍了近几个月Go在一 些技术会议上（比如Google I/O、Gopher SummerFest等）的主题分享并伴有slide链接。其中David Crawshaw的&ldquo;<a href="https://talks.golang.org/2014/organizeio.slide#1">Organizing Go Code</a>&rdquo;对Golang的代码风格以及工程组 织的最佳实践进行的总结很是全面和到位，这里按Slide中的思路和内容翻译和摘录如下（部分伴有我个人的若干理解）。</p>
<p><b>一、包 (Packages)</b></p>
<p><b>1、Golang程序由package组成</b></p>
<p>所有Go源码都是包得一部分。</p>
<p>每个Go源文件都起始于一条package语句。</p>
<p>Go应用程序的执行起始于main包。</p>
<p><font face="Courier New">package main</font></p>
<p><font face="Courier New">import &quot;fmt&quot;</font></p>
<p><font face="Courier New">func main() {<br />
	&nbsp;&nbsp;&nbsp; fmt.Println(&quot;Hello, world!&quot;)<br />
	}</font></p>
<p>对小微型程序而言，你可能只需要编写main包内的源码。</p>
<p>上面的HelloWorld程序import了fmt包。</p>
<p>函数Println定义在fmt包中。</p>
<p><b>2、一个例子：fmt包</b></p>
<p><font face="Courier New">// Package fmt implements formatted I/O.<br />
	package fmt</font></p>
<p><font face="Courier New">// Println formats using the default formats for its<br />
	// operands and writes to standard output.<br />
	func Println(a &#8230;interface{}) (n int, err error) {<br />
	&nbsp;&nbsp;&nbsp; &#8230;<br />
	}</font></p>
<p><font face="Courier New">func newPrinter() *pp {<br />
	&nbsp;&nbsp;&nbsp; &#8230;<br />
	}</font></p>
<p>Println是一个导出(exported)函数，它的函数名以大写字母开头，这意味着它允许其他包中的函数调用它。</p>
<p>newPrinter函数则并非导出函数，它的函数名以小写字母开头，它只能在fmt包内部被使用。</p>
<p><b>3、包的形态</b>(Shape)</p>
<p>包是有关联关系的代码的集合，包规模可大可小，大包甚至可以横跨多个源文件。</p>
<p>同一个包的所有源文件都放在一个单一目录下面。</p>
<p><font face="Courier New">net/http</font>包共由18个文件组成，导出了超过100个名字符号。</p>
<p><font face="Courier New">errors</font>包仅仅由一个文件组成，并仅导出了一个名字符号。</p>
<p><b>4、包的命名</b></p>
<p>包的命名应该短小且有含义。<br />
	不要使用下划线，那样会导致包名过长;<br />
	不要过于概况，一个util包可能包含任何含义的代码；</p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp; 使用io/ioutil，而不是io/util<br />
	&nbsp;&nbsp;&nbsp; 使用suffixarray，而不是suffix_array</font></p>
<p>包名是其导出的类型名以及函数名的组成部分。</p>
<p><font face="Courier New">buf := new(bytes.Buffer)</font></p>
<p>仔细挑选包名</p>
<p>为用户选择一个好包名。</p>
<p><b>5、对包的测试</b></p>
<p>通过文件名我们可以区分出哪些是测试用源文件。测试文件以_test.go结尾。下面是一个测试文件的样例：</p>
<p><font face="Courier New">package fmt</font></p>
<p><font face="Courier New">import &quot;testing&quot;</font></p>
<p><font face="Courier New">var fmtTests = []fmtTest{<br />
	&nbsp;&nbsp;&nbsp; {&quot;%d&quot;, 12345, &quot;12345&quot;},<br />
	&nbsp;&nbsp;&nbsp; {&quot;%v&quot;, 12345, &quot;12345&quot;},<br />
	&nbsp;&nbsp;&nbsp; {&quot;%t&quot;, true, &quot;true&quot;},<br />
	}</font></p>
<p><font face="Courier New">func TestSprintf(t *testing.T) {<br />
	&nbsp;&nbsp;&nbsp; for _, tt := range fmtTests {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if s := Sprintf(tt.fmt, tt.val); s != tt.out {<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.Errorf(&quot;&#8230;&quot;)<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
	&nbsp;&nbsp;&nbsp; }<br />
	}</font></p>
<p><b>二、代码组织(Code organization)</b></p>
<p><b>1、工作区介绍(workspace)</b></p>
<p>你的Go源码被放在一个工作区(workspace)中。</p>
<p>一个workspace可以包含多个源码库(repository)，诸如git，hg等。</p>
<p>Go工具知晓一个工作区的布局。</p>
<p>你无需使用Makefile，通过文件布局，我们可以完成所有事情。</p>
<p>若文件布局发生变动，则需重新构建。</p>
<p><font face="Courier New">$GOPATH/<br />
	&nbsp;&nbsp;&nbsp; src/<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; github.com/user/repo/<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mypkg/<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mysrc1.go<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mysrc2.go<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd/mycmd/<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; main.go<br />
	&nbsp;&nbsp;&nbsp; bin/<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mycmd</font></p>
<p><b>2、建立一个工作区</b></p>
<p><font face="Courier New">mkdir /tmp/gows<br />
	GOPATH=/tmp/gows</font></p>
<p>GOPATH环境变量告诉Go工具族你的工作区的位置。</p>
<p><font face="Courier New">go get github.com/dsymonds/fixhub/cmd/fixhub</font></p>
<p>go get命令从互联网网下载源代码库，并将它们放置在你的工作区中。</p>
<p>包的路径对Go工具来说很是重要，使用&quot;github.com&quot;意味着Go工具知道如何去获取你的源码库。</p>
<p><font face="Courier New">go install github.com/dsymonds/fixhub/cmd/fixhub</font></p>
<p>go install命令构建一个可执行程序，并将其放置在$GOPATH/bin/fixhub中。</p>
<p><b>3、我们的工作区</b></p>
<p><font face="Courier New">$GOPATH/<br />
	&nbsp;&nbsp;&nbsp; bin/fixhub&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # installed binary<br />
	&nbsp;&nbsp;&nbsp; pkg/darwin_amd64/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # compiled archives<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; code.google.com/p/goauth2/oauth.a<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; github.com/&#8230;<br />
	&nbsp;&nbsp;&nbsp; src/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # source repositories<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; code.google.com/p/goauth2/<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .hg<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; oauth&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # used by package go-github<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8230;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; github.com/<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; golang/lint/&#8230;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # used by package fixhub<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .git<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; google/go-github/&#8230;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # used by package fixhub<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .git<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dsymonds/fixhub/<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .git<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; client.go<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cmd/fixhub/fixhub.go&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # package main</font></p>
<p>go get获取多个源码库。<br />
	go install使用这些源码库构建一个二进制文件。</p>
<p><b>4、为何要规定好文件布局</b></p>
<p>在构建时使用文件布局意味着可以更少的进行配置。</p>
<p>实际上，它意味着无配置。没有Makefile，没有build.xml。</p>
<p>在配置上花的时间少了，意味着在编程上可以花更多的时间。</p>
<p>Go社区中所有人都使用相同的布局，这会使得分享代码更加容易。</p>
<p>Go工具在一定程度上对Go社区的建设起到了帮助作用。</p>
<p><b>5、你的工作区在哪？</b></p>
<p>你可以拥有多个工作区，但大多数人只使用一个。那么你如何设置GOPATH这个环境变量呢？一个普遍的选择是：</p>
<p><font face="Courier New">GOPATH=$HOME</font></p>
<p>这样设置会将src、bin和pkg目录放到你的Home目录下。（这会很方便，因为$HOME/bin可能已经在你的PATH环境变量中了）。</p>
<p><b>6、在工作区下工作</b></p>
<p><font face="Courier New">CDPATH=$GOPATH/src/github.com:$GOPATH/src/code.google.com/p</font></p>
<p><font face="Courier New">$ cd dsymonds/fixhub<br />
	/tmp/gows/src/github.com/dsymonds/fixhub<br />
	$ cd goauth2<br />
	/tmp/gows/src/code.google.com/p/goauth2<br />
	$</font></p>
<p>将下面shell函数放在你的~/.profile中：</p>
<p><font face="Courier New">gocd () { cd `go list -f &#39;{{.Dir}}&#39; $1` }</font></p>
<p><font face="Courier New">$ gocd &#8230;/lint<br />
	/tmp/gows/src/github.com/golang/lint<br />
	$</font></p>
<p><b>三、依赖管理</b></p>
<p><b>1、在生产环境中，版本很重要</b></p>
<p><font face="Courier New">go get</font>总是获取最新版本代码，即使这些代码破坏了你的构建。</p>
<p>这在开发阶段还好，但当你在发布阶段时，这将是一个问题。</p>
<p>我们需要其他工具。</p>
<p><b>2、版本</b><b>管理</b></p>
<p>我最喜欢的技术：vendoring。</p>
<p>当构建二进制程序时，将你关心的包导入到一个_vendor工作区。<br />
	<font face="Courier New">GOPATH=/tmp/gows/_vendor:/tmp/gows</font></p>
<p>注：<br />
	<font face="Courier New">&nbsp;&nbsp;&nbsp; 1、在build时，我们通过构建脚本，临时修改GOPATH（</font><font face="Courier New">GOPATH := ${PWD}/_vendor:${GOPATH}）， 并将_vendor放置在主GOPATH前面，利用go build解析import包路径解析规则，go build优先得到_vendor下的第三方包信息，这样即便原GOPATH下有不同版本的相同第三方库，go build也会优先导入_vendor下的同名第三方库。<br />
	&nbsp;&nbsp;&nbsp; 2、go的相关工具在执行类似test这样的命令时会忽略前缀为_或.的目录，这样_vendor下的第三方库的test等操作将不会被执行。</font></p>
<p>当构建库时，将你关心的包导入你的源码库。重命名import为：<br />
	<font face="Courier New">import &quot;github.com/you/proj/vendor/github.com/them/lib&quot;</font></p>
<p>长路径，不过对于自动化操作来说不算什么问题。写一个Go程序吧！</p>
<p>另外一种技术：gopkg.in。提供带版本的包路径：</p>
<p><font face="Courier New">gopkg.in/user/pkg.v3 -&gt; github.com/user/pkg (branch/tag v3, v3.N, or v.3.N.M)</font></p>
<p><b>四、命名</b></p>
<p><b>1、命名很重要</b></p>
<p>程序源码中充满着各种名字。名字兼具代价和收益。</p>
<p>代价：空间与时间<br />
	&nbsp;&nbsp;&nbsp; 当阅读代码时，名字需要短时记忆<br />
	&nbsp;&nbsp;&nbsp; 你只能适应这么多，更长的名字需要占据更多的空间。</p>
<p>收益：信息<br />
	&nbsp;&nbsp;&nbsp; 一个好名字不仅仅是一个指代对象，它还能够传达某种信息。<br />
	&nbsp;&nbsp;&nbsp; 使用尽可能最短的名字用于在上下文中携带合理数量的信息。</p>
<p>在命名上花些时间（值得的）。</p>
<p><b>2、命名样式</b></p>
<p>使用camelCase，不要用下划线。</p>
<p>本地变量名字应该短小，通常由1到2个字符组成。</p>
<p>包名同行是一个小写词。</p>
<p>全局变量应该拥有长度更长的名字。</p>
<p>不要结巴！<br />
	<font face="Courier New">&nbsp;使用bytes.Buffer，不要用bytes.ByteBuffer<br />
	&nbsp;使用zip.Reader，不要用zip.ZipReader<br />
	&nbsp;使用errors.New，不要用errors.NewError<br />
	&nbsp;使用r，不用bytesReader<br />
	&nbsp;使用i，不用loopIterator</font></p>
<p><b>3、文档化注释</b></p>
<p>文档化注释放在导出标示符的声明之前：</p>
<p><font face="Courier New">// Join concatenates the elements of elem to create a single string.<br />
	// The separator string sep is placed between elements in the resulting string.<br />
	func Join(elem []string, sep string) string {</font></p>
<p>godoc工具可以解析出这些注释并将其展示在Web上：</p>
<p><font face="Courier New">func Join<br />
	&nbsp;&nbsp;&nbsp; func Join (a []string, sep string) string</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp; Join concatenates the elements of&nbsp; a to create a single string. The separetor string sep is placed between elements in the resulting string.</font></p>
<p><b>4、写文档化的注释</b></p>
<p>文档化的注释应用使用英文句子和段落。<br />
	除了为预定义格式进行的缩进外，没有其他特殊格式。</p>
<p>文档化注释应该以要描述的名词开头。</p>
<p><font face="Courier New">// Join concatenates&hellip;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; good<br />
	// This function&hellip;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bad</font></p>
<p>包的文档应该放在包声明语句之前：</p>
<p><font face="Courier New">// Package fmt&hellip;<br />
	package fmt</font></p>
<p>在godoc.org上阅读Go世界的文档，比如：</p>
<p><font face="Courier New">godoc.org/code.google.com/p/go.tools/cmd/vet</font></p>
<p style='text-align:left'>&copy; 2014, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2014/10/21/organize-golang-code/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>也谈代码行统计</title>
		<link>https://tonybai.com/2013/07/24/thoughts-about-lines-of-code-statistics/</link>
		<comments>https://tonybai.com/2013/07/24/thoughts-about-lines-of-code-statistics/#comments</comments>
		<pubDate>Wed, 24 Jul 2013 10:12:50 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Blog]]></category>
		<category><![CDATA[Blogger]]></category>
		<category><![CDATA[Programmer]]></category>
		<category><![CDATA[代码统计]]></category>
		<category><![CDATA[博客]]></category>
		<category><![CDATA[学习]]></category>
		<category><![CDATA[工作]]></category>
		<category><![CDATA[思考]]></category>
		<category><![CDATA[比尔盖茨]]></category>
		<category><![CDATA[注释]]></category>
		<category><![CDATA[程序员]]></category>
		<category><![CDATA[绩效]]></category>

		<guid isPermaLink="false">http://tonybai.com/?p=1356</guid>
		<description><![CDATA[一直在纠结要不要就这个话题写点什么，之前梳理过一些思路，但感觉这个题目似乎没什么大意义。不过将东西憋在肚子里的滋味总是不好受的，最终我还是选择写出来一些，即便它真的没有什么意义^_^。 事情缘于近期领导让我负责的一个内部任务：制定组织内的代码行统计标准并实现标准化的工具。就是这个任务促使了我对代码行统计重新做了一番考量。 对代码行统计的理解 代码行统计这个活动不是软件开发过程中的关键路径活动，它对代码质量、开发进度以及软件价格几乎产生不了什么影响，应该算是个可有可无的东西。 就代码行统计这个活动本身而言，我个人的观点是没有代码行统计不表明不能开发出好软件；有了代码行统计，就一定能开发出高质量软件吗？ 不过有一种观点认为：世界的本质是数据。通过数据我们可以发现事物运行的规律。代码行统计则是软件工程中对&#8220;数据&#8221;要求的产物。过程的好坏需要有数据支 撑，因此代码行统计这个活动成为了人们实现&#8220;用数据说话&#8221;的一柄利器。在&#8220;数据为王&#8221;的今天，我们无论如何都不能忽视数据的作用。人们通过数据来反映软件 开发过程中的一些规律性的东西本身也没有什么不妥。另外代码是软件开发过程的最重要成果物，因此围绕着代码的性态，我们用工具做诸多分析，期望从得到的数 据中找寻出一些可以指导和改善我们后续工作的蛛丝马迹。代码行统计提供的多是基础数据，在与其他过程基础数据结合分析后，我们能得到更多的信息。 合理地使用场合 个人觉得下面几个场合对代码行统计的需求是合理的： * 统计代码总规模 &#160;&#160; 某个项目、某个模块或又某个版本的代码总规模。 * 代码&#8220;成分&#8221;统计 &#160;&#160; 统计空行、注释、代码的行数及占比、重复代码行数及占比等。 * 版本间代码变更差异统计 &#160;&#160; 两个有关联版本的数据对比统计，获取版本间的有效变更数据情况并作为基础数据提供给后续分析。 一些过程质量指标，诸如千行代码缺陷率等均是以上面这些代码行统计输出的基础数据为基础的。 &#8220;误用&#8221; 有合理的使用，就有&#8220;不合理&#8221;的使用 &#8211; &#8220;误用&#8221;。之所以加上引号，是因为至今人们对此见仁见智，尚无定论。以下列举两典型的&#8220;误用&#8221;。 * 通过代码行统计评估进度 有些组织在项目开始初期，就对成果的规模做了估计，比如10w行代码。然后在过程中使用代码统计工具对项目当前已实现的规模进行统计，并用统计出的数据与 初值的比值作为项目进度的评估参考。个人认为这是种典型的误用。盖茨说过：&#8220;用代码行数来衡量编程的进度，就如同用航空器零件的重量来衡量航空飞机的制造 进度一样&#8221;。且不提初期的估值有多么的不准确，就代码的行数本身而言，也受到各种因素的影响，比如设计方案、实现者的功力以及编码习惯等。同一个功能，A 实现需要100行代码；换成B就需要10行。 * 通过代码行统计评估程序员绩效 在一些外包公司或外包项目里，尤其是日本人的外包项目里，通过编写代码行的多少来评估程序员绩效的作法是很有市场的。我不能完全否定这种方法的正确性，因 为在日本外包项目中变态的日本人对代码的审核极其严格，并且有着苛刻的编码标准和风格，因此一些胡乱堆砌代码或使用奇技淫巧的代码都会被驳回，因此所有项 目开发者的效率似乎被约束到了一个平均线上。在这个前提下，产出的代码越多，似乎的确表明了这个开发者超出了平均效率，或至少牺牲了不少个人时间来完成项 目中的任务，精神可嘉，绩效被评高似乎也是合情合理的。但除此之外，用代码行多寡来评估程序员绩效显然是不受待见的。 考虑这个&#8220;误用&#8221;时，我也想模仿盖茨的话做个形象且深刻比喻，最初我写下的是这句话：&#8220;用代码行数多少来评估程序员的绩效，就好比用曲子的长短来评估音乐 家的水平，或又好比用画幅的大小来衡量画家的水准，或又好比用电影的时长 来掂量导演的功力！&#8221;。但仔细揣摩后发现这句话看起来挺像那么回事，但实际上却是不恰当的。什么是水准、水平或功力，这是衡量人的水平高低的；而绩效则是 一段时间范畴内工作成果的评估； 一个是长期的肯定，一个是阶段性的成绩。我显然是将水平和绩效(阶段性成绩)混为一谈了。高水平的开发者不一定每个周期都会取得高绩效，低水平的开发者也 不是无法取得高绩效的。因此这句话似乎应该改成：&#8220;用代码行数多少来评估程序员的绩效，就好比用这首曲子的长短来评估音乐家在这个阶段的水平，或又好比用 画幅的大小来衡量画家的这个阶段水准，或又 好比用电影的时长来掂量导演在这部电影上的功力！&#8221;。是不是读起来很别扭啊，反正我是这么觉得的。程序员的成果物是代码，代码好坏优劣对程序员绩效有着直 接影响（虽非充分必要条件），我们不妨替换一下本体来换种说法：&#8220;用代码行数多少来评估代码实现的好坏，就好比用曲子的长短来评估曲子的优劣，或又好比用 画幅的大小来衡量画作的高低，或又好比用电影的时长来掂量影片的良莠&#8221;！ 对用代码行数多少来评估程序员绩效这种事情，我是很反感的，但在国内许多公司里，这种现象却又屡见不鲜。但这种行为背后的动机何在呢？传统工厂中，衡量一 个worker的绩效是相对容易量化，也比较客观的，比如制鞋厂可以用制成鞋子的数量来确定 worker绩效；在汽车组装车间，组装汽车的数量可以作为作为工人们的绩效；在炼钢厂，班组炼出的钢铁的吨数可作为班组成员绩效等等。将代码行数作为程 序员绩效的参考指标也许是一个无奈的方法。之所以想用代码行数，是因为程序员工作中能量化的东西不多，代码行数首当其冲。组织为了尽量减少绩效评定时主观 的成分，增加客观的评价，代码行统计从此被误用了。 [...]]]></description>
			<content:encoded><![CDATA[<p>一直在纠结要不要就这个话题写点什么，之前梳理过一些思路，但感觉这个题目似乎没什么大意义。不过将东西憋在肚子里的滋味总是不好受的，最终我还是选择写出来一些，即便它真的没有什么意义^_^。</p>
<p>事情缘于近期领导让我负责的一个内部任务：制定组织内的代码行统计标准并实现标准化的工具。就是这个任务促使了我对<a href="http://tonybai.com/2010/12/24/an-effectual-method-based-on-svn-diff-for-code-quantity-statistics/">代码行统计</a>重新做了一番考量。</p>
<p><b>对代码行统计的理解</b></p>
<p>代码行统计这个活动不是软件开发过程中的关键路径活动，它对代码质量、开发进度以及软件价格几乎产生不了什么影响，应该算是个可有可无的东西。</p>
<p>就代码行统计这个活动本身而言，我个人的观点是没有代码行统计不表明不能开发出好软件；有了代码行统计，就一定能开发出高质量软件吗？</p>
<p>不过有一种观点认为：世界的本质是数据。通过数据我们可以发现事物运行的规律。代码行统计则是软件工程中对&ldquo;数据&rdquo;要求的产物。过程的好坏需要有数据支 撑，因此代码行统计这个活动成为了人们实现&ldquo;用数据说话&rdquo;的一柄利器。在&ldquo;数据为王&rdquo;的今天，我们无论如何都不能忽视数据的作用。人们通过数据来反映软件 开发过程中的一些规律性的东西本身也没有什么不妥。另外代码是软件开发过程的最重要成果物，因此围绕着代码的性态，我们用工具做诸多分析，期望从得到的数 据中找寻出一些可以指导和改善我们后续工作的蛛丝马迹。代码行统计提供的多是基础数据，在与其他过程基础数据结合分析后，我们能得到更多的信息。</p>
<p><b>合理地使用场合</b></p>
<p>个人觉得下面几个场合对代码行统计的需求是合理的：</p>
<p>* 统计代码总规模<br />
	&nbsp;&nbsp; 某个项目、某个模块或又某个版本的代码总规模。</p>
<p>* 代码&ldquo;成分&rdquo;统计<br />
	&nbsp;&nbsp; 统计空行、注释、代码的行数及占比、重复代码行数及占比等。</p>
<p>* 版本间代码变更差异统计<br />
	&nbsp;&nbsp; 两个有关联版本的数据对比统计，获取版本间的有效变更数据情况并作为基础数据提供给后续分析。</p>
<p>一些过程质量指标，诸如千行代码缺陷率等均是以上面这些代码行统计输出的基础数据为基础的。</p>
<p><b>&ldquo;误用&rdquo;</b></p>
<p>有合理的使用，就有&ldquo;不合理&rdquo;的使用 &#8211; &ldquo;误用&rdquo;。之所以加上引号，是因为至今人们对此见仁见智，尚无定论。以下列举两典型的&ldquo;误用&rdquo;。</p>
<p>* 通过代码行统计评估进度</p>
<p>有些组织在项目开始初期，就对成果的规模做了估计，比如10w行代码。然后在过程中使用代码统计工具对项目当前已实现的规模进行统计，并用统计出的数据与 初值的比值作为项目进度的评估参考。个人认为这是种典型的误用。盖茨说过：&ldquo;用代码行数来衡量编程的进度，就如同用航空器零件的重量来衡量航空飞机的制造 进度一样&rdquo;。且不提初期的估值有多么的不准确，就代码的行数本身而言，也受到各种因素的影响，比如设计方案、实现者的功力以及编码习惯等。同一个功能，A 实现需要100行代码；换成B就需要10行。</p>
<p>* 通过代码行统计评估程序员绩效</p>
<p>在一些外包公司或外包项目里，尤其是日本人的外包项目里，通过编写代码行的多少来评估程序员绩效的作法是很有市场的。我不能完全否定这种方法的正确性，因 为在日本外包项目中变态的日本人对代码的审核极其严格，并且有着苛刻的编码标准和风格，因此一些胡乱堆砌代码或使用奇技淫巧的代码都会被驳回，因此所有项 目开发者的效率似乎被约束到了一个平均线上。在这个前提下，产出的代码越多，似乎的确表明了这个开发者超出了平均效率，或至少牺牲了不少个人时间来完成项 目中的任务，精神可嘉，绩效被评高似乎也是合情合理的。但除此之外，用代码行多寡来评估程序员绩效显然是不受待见的。</p>
<p>考虑这个&ldquo;误用&rdquo;时，我也想模仿盖茨的话做个形象且深刻比喻，最初我写下的是这句话：&ldquo;用代码行数多少来评估程序员的绩效，就好比用曲子的长短来评估音乐 家的水平，或又好比用画幅的大小来衡量画家的水准，或又好比用电影的时长 来掂量导演的功力！&rdquo;。但仔细揣摩后发现这句话看起来挺像那么回事，但实际上却是不恰当的。什么是水准、水平或功力，这是衡量人的水平高低的；而绩效则是 一段时间范畴内工作成果的评估； 一个是长期的肯定，一个是阶段性的成绩。我显然是将水平和绩效(阶段性成绩)混为一谈了。高水平的开发者不一定每个周期都会取得高绩效，低水平的开发者也 不是无法取得高绩效的。因此这句话似乎应该改成：&ldquo;用代码行数多少来评估程序员的绩效，就好比用这首曲子的长短来评估音乐家在这个阶段的水平，或又好比用 画幅的大小来衡量画家的这个阶段水准，或又 好比用电影的时长来掂量导演在这部电影上的功力！&rdquo;。是不是读起来很别扭啊，反正我是这么觉得的。程序员的成果物是代码，代码好坏优劣对程序员绩效有着直 接影响（虽非充分必要条件），我们不妨替换一下本体来换种说法：&ldquo;用代码行数多少来评估代码实现的好坏，就好比用曲子的长短来评估曲子的优劣，或又好比用 画幅的大小来衡量画作的高低，或又好比用电影的时长来掂量影片的良莠&rdquo;！</p>
<p>对用代码行数多少来评估程序员绩效这种事情，我是很反感的，但在国内许多公司里，这种现象却又屡见不鲜。但这种行为背后的动机何在呢？传统工厂中，衡量一 个worker的绩效是相对容易量化，也比较客观的，比如制鞋厂可以用制成鞋子的数量来确定 worker绩效；在汽车组装车间，组装汽车的数量可以作为作为工人们的绩效；在炼钢厂，班组炼出的钢铁的吨数可作为班组成员绩效等等。将代码行数作为程 序员绩效的参考指标也许是一个无奈的方法。之所以想用代码行数，是因为程序员工作中能量化的东西不多，代码行数首当其冲。组织为了尽量减少绩效评定时主观 的成分，增加客观的评价，代码行统计从此被误用了。</p>
<p><b>代码行统计的高效使用</b></p>
<p>* 标准统一，工具一致</p>
<p>代码行统计工具有很多，因此执行这个活动时会出现不同人使用的代码行统计工具不一致的情况；并且不同工具对一些指标的定义也许有不同，这会导致收集到的数据存在含义不一致，精确度差的问题。因此高效使用代码行统计工具的一个前提就是（统计）标准统一，工具一致。</p>
<p>* 零干扰</p>
<p>一些传统的代码行统计方法是配置负责人收到统计任务时，将任务分发给各个模块的负责人，由各个模块负责人各自统计，然后反馈给配置负责人汇总。这种方式显 然不那么高效，而且容易引起一些对统计任务的反感情绪。高效的代码行统计最好能做到对开发人员&ldquo;零干扰&rdquo;。配置负责人可以通过&ldquo;自动化&rdquo;的静默方式收集代 码行数据。当然这需要对一些现成的开源工具做一些包装或二次开发才能做到，个人觉得这种投入是值得的，同时也能避免标准不一，工具不一致的情况。</p>
<p style='text-align:left'>&copy; 2013, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2013/07/24/thoughts-about-lines-of-code-statistics/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>聆听编程“古训”</title>
		<link>https://tonybai.com/2011/05/10/listen-to-old-maxim-respectfully/</link>
		<comments>https://tonybai.com/2011/05/10/listen-to-old-maxim-respectfully/#comments</comments>
		<pubDate>Tue, 10 May 2011 14:17:00 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Algorithm]]></category>
		<category><![CDATA[Blog]]></category>
		<category><![CDATA[Blogger]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Comment]]></category>
		<category><![CDATA[Programmer]]></category>
		<category><![CDATA[代码大全]]></category>
		<category><![CDATA[博客]]></category>
		<category><![CDATA[学习]]></category>
		<category><![CDATA[标准]]></category>
		<category><![CDATA[标准库]]></category>
		<category><![CDATA[注释]]></category>
		<category><![CDATA[程序员]]></category>
		<category><![CDATA[编码]]></category>
		<category><![CDATA[读书]]></category>
		<category><![CDATA[重构]]></category>
		<category><![CDATA[风格]]></category>

		<guid isPermaLink="false">http://tonybai.com/2011/05/10/%e8%81%86%e5%90%ac%e7%bc%96%e7%a8%8b%e2%80%9c%e5%8f%a4%e8%ae%ad%e2%80%9d/</guid>
		<description><![CDATA[<p><br />
市面上关于优秀编程风格和习惯养成的书籍还真不少，其中&#8220;叫好又叫座&#8221;的书诸如《代码大全》、《编程精粹:编写高质量C语言代码》、《编程匠艺》、《重构》以及《Clean Code》等。不过前些天我在网上下载了一本名为《The Elements of Programming Style》...</p>]]></description>
			<content:encoded><![CDATA[<p>市面上关于优秀编程风格和习惯养成的书籍还真不少，其中&ldquo;叫好又叫座&rdquo;的书诸如《<a href="http://tonybai.com/2008/11/13/coding-review-and-cc2e-and-assertion-and-others/" target="_blank">代码大全</a>》、《<a href="http://http://book.douban.com/subject/3406939/" target="_blank">编程精粹:编写高质量C语言代码</a>》、《<a href="book.douban.com/subject/3210669" target="_blank">编程匠艺</a>》、《<a href="http://tonybai.com/2006/03/28/c-refactoring/" target="_blank">重构</a>》以及《<a href="http://book.douban.com/subject/3032825/" target="_blank">Clean Code</a>》等。不过前些天我在网上下载了一本名为《<a href="http://book.douban.com/subject/1470267/" target="_blank">The Elements of Programming Style</a>》的电子书，看过此书后，我才知道开创编写优秀风格代码之路的鼻祖是谁（不知道是否还有比这本书更加古老的且系统地讲述优良编程元素的书籍了？）。</p>
<p>这本书的两位作者来头都大得很。<a href="http://en.wikipedia.org/wiki/Brian_Kernighan" target="_blank">Brian W. Kernighan</a>，K&#038;R C中的那个&ldquo;K&rdquo;，C语言的鼻祖之一。 <a href="http://en.wikipedia.org/wiki/P._J._Plauger" target="_blank">P. J. Plauger</a>，《C标准库》一书作者，同样是大师级人物，说不准你现在使用的<a href="http://book.douban.com/subject/3775842/" target="_blank">C标准库</a>还是Plauger当初操刀实现的呢^_^。这本书的出版年份为1978年，Wow，Older Than Me！距今有30多年了，在编程领域算是一本&ldquo;古书&rdquo;了。其第一版则更早，于1974年出版。这本书的中心思想是计算机程序编写不应该只满足于编译器或者某些个体的编程风格，还要满足人们对程序的&ldquo;可读性&rdquo;的要求。据说当时这本书的出版让全天下的程序员们恍然醒悟，从此大家便知道了优秀编程风格是什么样子的，优秀的代码是应该这么写的。</p>
<p>这本书我还没有全部看完，目前也只看完了前面的十几个条目和例子。本以为书中会用C语言做例子，没想到作者居然用了Fortran和PL/I，整本书&ldquo;充斥&rdquo;着用陌生的Fortran和PL/I语法编写的例子。后来我也想明白了：在那个年代，Fortran才是老大，<a href="http://book.douban.com/doulist/549603/" target="_blank">C语言</a>初出茅庐，还仅仅停留在Bell Lab中。不过也正因为如此，这本书看起来那叫一个费劲，让人头疼。于是我到网上搜出了这本书的<a href="http://www.cnblogs.com/hitszxin/archive/2008/04/03/1136726.html" target="_blank">所有条目列表</a>。完整地看完一遍这些条目后，我甚感吃惊，吃惊的是这本古书中的大多数条目对我们今天的代码编写依旧具有着非凡的指导意义，甚至可以理解为编程领域的公理（至少在目前以及可预见的时间段内都是生效的）。另外当你看完这些条目后，你会发现有些似曾相识的感觉，原因也很简单。我们看到的《代码大全》、《重构》等&ldquo;近现代&rdquo;书籍可能都或多或少的从这本古书中继承了一些内容，并结合现代编程思想加以扩展和升华了！</p>
<p>那《The Elements of Programming Style》这本&ldquo;古书&rdquo;是否还值得去读呢？毕竟我们已经有了像《代码大全》这样的百科全书了。我觉得至少应该过一遍这本书的条目列表，并且针对你感兴趣的重点条目去精读。三十多年前的古训也许更能还原出条目在当时所处的历史场景，这也许是当前一些书籍所不具备的。特别是如果你觉得《代码大全》太厚重，那么不妨可以先来聆听一下这本小书中的&ldquo;古训&rdquo;^_^。</p>
<p style='text-align:left'>&copy; 2011, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2011/05/10/listen-to-old-maxim-respectfully/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
