The Context Object Pattern: Stop Passing 11 Arguments Everywhere
I recently wrote about a great creational design pattern, the builder pattern. It’s fantastic for cleaning up messy object construction, especially when you’ve got tons of optional keyword arguments that make your __init__ look like a Walmart receipt.
But what about functions?
When you’re scripting, orchestrating a pipeline, or calling multiple functions in a sequence, writing a builder just to group parameters feels like overkill. And unlike object creation, what you really want is a clean, consistent way to pass shared metadata or configuration through each step.
So what options do we have — besides crying into our keyboards?
The Context Object Pattern
The context object pattern says:
Instead of passing ten separate keyword arguments to every function, create one object that carries all the shared state.
This is particularly useful when:
- Multiple parameters naturally belong together as "a thing"
- You want type validation or structure around what gets passed
- You’re tired of updating 12 function signatures every time a requirement changes
Let’s walk through an example.
Chad the Data Engineer and the Pipeline of Many Arguments
Chad just wanted to write a simple pipeline. But as he coded, he kept thinking: "Wait, I just need one more parameter…" And one became two. Then seven. Then it spiraled into this:
This is… a lot. Hard to read, painful to maintain, and guaranteed to break the moment you add one more parameter.
And you’ll notice: all the same metadata is passed repeatedly. Exactly the sort of thing a context object cleans up.
Enter the Context Object
Here’s how we clean it up: define a single object containing all the shared context.
Now your functions take one parameter instead of eleven. And your pipeline suddenly becomes readable again.
Take a breath. Your eyes have stopped bleeding.
Why This Pattern Rocks
Because the pipeline context is defined in one place:
- Your code is more maintainable. Add a new parameter? Update one class, not ten signatures.
- Debugging gets easier. There’s one object to inspect, not a scattered mess of arguments.
- It scales naturally. More pipelines? Reuse or extend your context class.
This pattern also shines for things like configuration, shared metadata, runtime state, loggers, clients, caches, and anything else you want in one bundle. Popular libraries use it too: Django passes around the request object for a reason.
A Quick Word of Caution
Like any pattern, this one can be abused.
If your context object grows into a junk drawer of unrelated fields ("Yes, this object contains region, a logger, and also a list of coupons for some reason"), you’ve recreated the same problem, just in a different shape.
The goal is to define coherent context that logically belongs together — not to shove everything into one object "just because we can."
In Short
Don't be like Chad. Write better code with the context object pattern.
As always, you can find the code examples at my jacobwritescode repo!
Happy coding! 😁