context.TODO

July 7, 2025

We’re almost done going through the context package. What would you like me to cover next? If you have a suggestion, hit reply, and let me know!

func TODO

func TODO() Context

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).

context.TODO() if functionally identical to context.Background(). We can look at the implementation of both, and see that the only difference is the return value on the (undocumented) String() method:

// An emptyCtx is never canceled, has no values, and has no deadline.
// It is the common base of backgroundCtx and todoCtx.
type emptyCtx struct{}

func (emptyCtx) Deadline() (deadline time.Time, ok bool) {
	return
}

func (emptyCtx) Done() <-chan struct{} {
	return nil
}

func (emptyCtx) Err() error {
	return nil
}

func (emptyCtx) Value(key any) any {
	return nil
}

type backgroundCtx struct{ emptyCtx }

func (backgroundCtx) String() string {
	return "context.Background"
}

type todoCtx struct{ emptyCtx }

func (todoCtx) String() string {
	return "context.TODO"
}

// 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.
func Background() Context {
	return backgroundCtx{}
}

// 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).
func TODO() Context {
	return todoCtx{}
}

So why bother with two virtually identical functions?

Documentation.

While they’re functionally identical, context.TODO serves as a clear indication that it’s not the ideal context to be used, and should be replaced with a different context at some point.

You’re most likely to use context.TODO when refactoring an old code base that doesn’t understand context yet. Although you will also sometimes use it in greenfield code—espeically if you like to write your code bottom-up, or when making foundational changes (such as going from an in-memory store, to something backed by a database).

Let’s say you have this function, which fetches a user from an in-memory map:

// User returns the user with the specified ID.
func (s *store) User(id int64) (*User, error) {
	user, ok := s.users[id]
	if !ok {
		return nil, errors.New("user not found")
	}
	return user, nil
}

And you’re modifying it to use a PostgreSQL database:

// User returns the user with the specified ID.
func (s *store) User(id int64) (*User, error) {
	var user User
	if err := s.pgx.Query(context.TODO(), `
		SELECT username, password
		FROM users
		WHERE id=$1
	`, id).Scan(&user.Username, &user.Password)
	return &user, err
}

This is a case for context.TODO(), as you may not want to modify the function signature of User now (or ever!). This leaves a clear indication for a future developer (possibly you) to come back and decide how to get a meaningful context value to that Query call.

If you use context.Background() instead, it’s an indication that you’ve made the choice that the background context should be used, and that it’s correct not to use a received context.


Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Get daily content like this in your inbox!

Subscribe