<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>工程实践 on 山行小记</title>
    <link>https://ruijingan.pages.dev/tags/%E5%B7%A5%E7%A8%8B%E5%AE%9E%E8%B7%B5/</link>
    <description>Recent content in 工程实践 on 山行小记</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <copyright>@2025</copyright>
    <lastBuildDate>Mon, 11 Apr 2022 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://ruijingan.pages.dev/tags/%E5%B7%A5%E7%A8%8B%E5%AE%9E%E8%B7%B5/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Go 项目里 error 到底该怎么处理</title>
      <link>https://ruijingan.pages.dev/posts/2022/go-error-handling/</link>
      <pubDate>Mon, 11 Apr 2022 00:00:00 +0000</pubDate>
      <guid>https://ruijingan.pages.dev/posts/2022/go-error-handling/</guid>
      <description>&lt;p&gt;团队接了一个新项目，定代码规范的时候在 error 处理上吵了整整一个下午。这篇文章整理一下我们最终达成的共识，以及为什么。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;先说反模式。&lt;/strong&gt; 常见的两种流派都有问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;warp 派&amp;rdquo;：每一层都 &lt;code&gt;fmt.Errorf(&amp;quot;xxx: %w&amp;quot;, err)&lt;/code&gt; 包一层，最后日志里出现五行重复的 &lt;code&gt;query user: query user: query user: record not found&lt;/code&gt;。信息量为零，日志长度翻倍。&lt;/li&gt;
&lt;li&gt;&amp;ldquo;裸传派&amp;rdquo;：err 原样往上传，顶层打日志的时候只知道出错了，不知道错在哪个业务环节。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;我们现在的做法&lt;/strong&gt;，说起来很朴素：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;只有&amp;quot;当前层新增了语义&amp;quot;的时候才包装。&lt;/strong&gt; 比如数据层返回 &lt;code&gt;record not found&lt;/code&gt;，到 service 层要变成 &lt;code&gt;ErrUserNotFound&lt;/code&gt;，这是有语义提升的，值得包。纯粹透传的调用直接 &lt;code&gt;return err&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;哨兵错误 + errors.Is 为主，自定义错误类型按需用。&lt;/strong&gt; 90% 的场景一个 &lt;code&gt;var ErrXxx = errors.New(&amp;quot;xxx&amp;quot;)&lt;/code&gt; 就够了，别上来就定义结构体。真需要携带上下文（比如重试次数、限流详情）再上类型断言 &lt;code&gt;errors.As&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;打日志和返回 error 只做一件事。&lt;/strong&gt; 这是我们的铁律：中间层不要既打日志又往上返回，否则同一次错误在日志里出现 N 遍，定位的时候全是噪音。要么就地处理并打日志（返回 nil），要么不打日志继续上传。顶层（通常是 HTTP handler 或 cron 入口）统一打一次。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第三方库的 error 要在边界收口。&lt;/strong&gt; 库的 error 类型不该泄漏到业务代码里，否则哪天换库就是全项目翻新。在 repository 层把 gorm 的错误翻译成领域错误，业务层只认识领域错误。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;另外一个和 error 无关但经常一起争论的问题：panic 什么时候用。我们的边界也很清晰——只在程序初始化阶段（连不上数据库这种&amp;quot;起不来就没意义&amp;quot;的场景）panic，运行期的错误一律返回。以及一个共识：&lt;code&gt;recover&lt;/code&gt; 只允许出现在最顶层的中间件里，业务代码里出现 recover 一律打回重写。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
