必须用 context.Background 的场景是程序启动入口点,如 main 函数、HTTP handler 顶层或 CLI 初始调用;context.TODO() 仅用于上下文逻辑未确定且已留待办追踪的临时开发状态。

context.Background 和 context.TODO 都返回空 context,但用途完全不同
它们底层都是 emptyCtx 类型,没有任何值、超时或取消能力,区别只在语义和使用意图。选错会导致代码可读性下降,甚至让协作者误判上下文是否“真没传”还是“忘了传”。
什么时候必须用 context.Background
仅用于程序启动入口点,即真正没有父 context 可继承的顶层位置。典型场景包括:main 函数、HTTP 服务器的 handler 入口(当框架未注入 context 时)、CLI 命令的初始调用。
-
http.ListenAndServe启动后,每个请求应使用框架传入的 request context,而非再调用context.Background() - 测试中若需构造一个“干净无携带信息”的 context,
context.Background()是合理选择 —— 因为它明确表示“这里本就不该有继承关系” - 不要在库函数内部默认 fallback 到
context.Background(),这会掩盖调用方遗漏传 context 的问题
TODO 不是占位符,而是“我还没想好用哪个 context”
context.TODO() 的唯一合法用途:你正在写一段代码,但当前无法确定该处该用哪个 context(比如 API 尚未设计好、依赖模块还没接入、或多人协作中上下文传递链还没对齐),且你已留下待办事项(如 TODO 注释、issue 或代码评审备注)来追踪这件事。
- 它不是给“懒得传 context”的人用的;用它代替
context.Background()来绕过静态检查,属于滥用 - CI 或 code review 中看到
context.TODO(),应视为必须解决的问题,而不是可忽略的临时状态 - Go 官方文档明确说:“
TODOis for code that is not yet implemented” —— 指的是 context 使用逻辑未实现,不是指功能未写完
常见错误:把 TODO 当成 Background 的别名
这类错误不会导致 panic 或编译失败,但会让代码意图模糊,尤其在审查长调用链时,无法区分“此处确实不该有 context”和“这里应该有但被漏了”。更隐蔽的风险是:后续有人基于 TODO 做 context.WithValue 扩展,结果发现值根本没被传播下去。
- 错误示例:
ctx := context.TODO(); doSomething(ctx)放在 HTTP handler 内部,而 handler 参数已有req.Context()—— 正确做法是直接用req.Context() - 错误示例:在封装数据库操作的函数里,默认用
context.TODO(),却不提供 context 参数 —— 这等于主动放弃超时与取消能力 - 静态分析工具(如
staticcheck)会标记未使用的context.TODO(),但不会报错;靠人眼识别才是关键防线
最易被忽略的一点:这两个函数都不该出现在公共函数签名中作为默认值(比如 func Do(ctx context.Context) {} 默认用 context.Background())。接口设计上,要么强制传 ctx,要么由调用方决定用 Background 还是 TODO —— 库作者不越界做语义判断。











