3 min read

Learning Phoenix's Intentional Boundaries

Contexts, boundary-making, and how intentional separation changes the way you reason about systems over time.
Learning Phoenix's Intentional Boundaries
Photo by Erin Larson / Unsplash

I’ve been learning Elixir and Phoenix recently, slowly and a bit unevenly, mostly by building small things and paying attention to what feels different from how I’m used to working. One of those differences that keeps resurfacing is Phoenix’s idea of contexts. It’s not new, and it’s certainly not unique to Phoenix, but the way it’s encouraged (and I'd say, insisted upon) has been surprisingly disruptive to how I normally think about application structure.

As I worked through examples, documentation and courses, I started to notice just how framework-centred my mental model of an application had become over the years. Not in a bad way, necessarily. Frameworks like Rails are beautifully designed, and I still think they have a very real place. But Phoenix made visible some assumptions I didn’t realise I was carrying.

In Phoenix, contexts sit in a space that feels deliberately indifferent to the web layer. They don’t care about controllers, HTTP or rendering. They describe a domain and expose an interface to it. That separation is explicit enough that you can’t really ignore it. You have to name the thing you’re modelling. You have to decide what belongs together. And you have to do it much earlier than you might otherwise like.

Over time, that insistence began to change the way I was reasoning about the system as a whole, not just how I was organising files.

Recently, I had a conversation that took this idea further than I had on my own. I was chatting with Brendon, the CTO at the company I work for, and telling him how much I was enjoying Phoenix’s approach to contexts. In response, he mentioned something he’d done during a Phoenix upgrade that blew me away.

He’d effectively deleted the entire web layer and re-initialised Phoenix from scratch, then wired the new framework code back into the existing system. The core logic and tests stayed. The framework came and went!

It wasn’t just that contexts encourage separation from the web layer; it was that they make this kind of move conceivable at all. The idea that you could throw away the framework-facing code and still have something recognisably intact underneath feels very significant to me. Especially now, when scaffolding and re-initialisation are dramatically cheaper with the help of AI.

In a Phoenix app structured around contexts, a surprising amount of the system still makes sense. The core logic, the tests, the language of the domain ... they don’t disappear just because the web layer does. That feeling, of the application still existing without its framework ... felt less like I was building inside a framework and more like the framework was something I was temporarily using to expose a system underneath it.

Contexts force you to draw boundaries intentionally. They might be wrong boundaries. But even if they are, the act of drawing them forces you to articulate what you think the system is, not just how it works. In systems where boundaries emerge mostly through accumulation, that articulation can be postponed indefinitely. Contexts don’t really let you do that.

I recall reading that one of the real challenges with contexts is naming. When you struggle to name a context, it’s usually not because naming is hard in the abstract, but because your understanding is still fuzzy. Interestingly, I've found tools like AI help a lot here, not by solving the problem for you, but by letting you iterate faster through bad names. The responsibility for meaning still sits with you, but the feedback loop tightens.

I don’t think contexts are “better architecture” in some absolute sense. What they seem to be is a constraint that nudges you toward thinking in systems rather than layers. They make certain kinds of coupling harder and certain kinds of clarity harder to avoid. That alone has been valuable to experience while learning.

I’m not writing this as a comparison piece or as a rejection of how I’ve worked before. Rails, in particular, still feels like a thoughtfully designed tool for a certain kind of work and a certain kind of mind. But learning Phoenix has started to shift what I now default to when I think about application structure. It’s made me more aware of how easily logic and framework concerns can become entangled, and how intentional boundaries change not just the code, but the way you reason about it over time.

I’m still learning. Most of this may evolve or soften as I build more things and make more mistakes. But for now, contexts have done something useful: they’ve interrupted my assumptions long enough for me to notice them. And that, on its own, feels like a worthwhile outcome of learning a new way to build software.