Choosing between context.Background() and context.TODO()

Avatar of the author Willem Schots
14 Apr, 2023
~4 min.
RSS

If you have used the context package in the standard library, you might have noticed that there are two constructors that create an empty context: context.Background() and context.TODO().

So which one should you use when you need a new context?

Let’s check the docs

First, context.Background():

Background returns a non-nil, empty Context. It is never canceled, has no values, and has no deadline. It is typically used by the main function, initialization, and tests, and as the top-level Context for incoming requests.

Then, context.TODO():

TODO returns a non-nil, empty Context. Code should use context.TODO when it’s unclear which Context to use or it is not yet available (because the surrounding function has not yet been extended to accept a Context parameter).

So both return a “non-nil, Empty Context” and should function the same from a technical perspective. However, they differ in their intent and what they communicate to the people reading your code.

Are you or your team struggling with aspects of Go?

Book a 30 minute check-in with me.

I can help with questions like:

  • How should we structure our Go web app?
  • Is this package a good choice for us?
  • How do I get my team up to speed quickly?
  • Can I pick your brain on {subject}?
Learn more

Looking forward to meeting you.

- Willem

Functional reasons

If your code requires an empty context for functional reasons, you should use context.Background(). It is the “standard” way of creating an empty context.

Like the docs said, common cases where you might want to use an empty context are in your main function or in (unit) tests. context.Background() is the right function to use in these cases.

Work in progress

I don’t think it’s a coincidence that it is spelled the same as a typical TODO comment. You should use context.TODO() as a “marker” for code that should to be fixed at some point.

The situation described in the documentation is quite common: You want to call a function that requires a context as input, but the function you’re calling it from does not accept a context yet.

Let’s look at an example.

package main

import "context"

func main() {
	a()
}

func a() {
	b(context.TODO())
}

func b(ctx context.Context) {
	// ...
}

Function a calls function b, but function a has not been extended to accept a context yet. So function a calls b with a context.TODO() context.

This signals to other developers that there is work to be done here. You might not always want to do this work immediately, because extending a function can require a bit of care:

  • If the extended function is an exposed function (begins with a capital letter), adding a parameter would break all users of that function.
  • If the callers of the extended function don’t accept contexts yet, they in turn need to be extended as well. Leading to cascade of changes.

Never pass a nil context

Choosing the wrong constructor will not lead to your application blowing up. Both context.Background() and context.TODO() function in the same way.

However there is one thing that would result in issues: passing nil as a context. Even the docs warn against it:

Do not pass a nil Context, even if a function permits it. Pass context.TODO if you are unsure about which Context to use.

It can be a bit nefarious, becase the compiler will not warn you if you do provide nil as a context.

Eventually this will result in panics, for example, when you try to derive a new context:

package main

import "context"

func main() {
	context.WithCancel(nil)
}

Which will result in:

panic: cannot create context from nil parent

Or when you attempt to call a method on the nil context itself:

package main

import "context"

func main() {
	var ctx context.Context = nil
	ctx.Err()
}

Resulting in:

panic: runtime error: invalid memory address or nil pointer dereference

So never pass a nil context, unless you like tracing down these kind of panics.

Outro

As you just saw, the only way things can really go wrong is by providing a nil context, so if you use context.Background() or context.TODO() you will be fine. If you have any questions or comments feel free to reach out to me.

And, if you want to read more posts like this, sign up for my newsletter below and receive the latest posts in your inbox.

🎓

Subscribe to my Newsletter and Keep Learning.

Gain access to more content and get notified of the latest articles:

I send emails every 1-2 weeks and will keep your data safe. You can unsubscribe at any time.

Hello! I'm the Willem behind willem.dev

I created this website to help new Go developers, I hope it brings you some value! :)

You can follow me on Twitter/X, LinkedIn or Mastodon.

Thanks for reading!