existence
← All posts
EngineeringApril 2026 · 3 min read

Journey Creating E-Core-Framework v2.

A deep dive into the journey of building E-Core-Framework v2: the challenges, breakthroughs, and lessons learned while reimagining our core architecture from the ground up.

S
SiyabongaAuthor
XLinkedIn
8 Years

We shipped v1 of e_core to solve our own problem. Six apps, one team, and no appetite for rebuilding auth, routing, and theming a seventh time. It worked, right up until it didn't. This is the honest version of what happened between v1 and v2, and the parts we'd change if we started again tomorrow.

Where v1 started

The original brief was small: extract the pieces every Existence app copy-pasted and give them one home. Auth, a theme layer, a handful of UI primitives. v1 was a single package, and for the first year that was exactly right: one import, everything included, no decisions to make.

The trouble is that "everything included" is a promise you have to keep forever. Every app that adopted v1 adopted all of it, whether it needed the theme engine or not.

We shipped the framework we needed a year too late to have needed it.

What broke as we scaled

By the time four apps depended on the same core, a one-line change in the auth layer meant a coordinated release across every consumer. The single package had become a single point of failure.

  • A patch to routing guards forced a version bump for apps that never touched routing.
  • The workaround, pinning versions per app, quietly became the technical debt we lived with for months.
  • The moment it stopped being sustainable: a security fix that took nine days to roll out because of the release chain.

The decision that followed was the one we'd avoided for a year: break the monolith apart.

Rethinking the layer boundaries

v2 is fifteen packages across seven layers. The rule we held to: a package may depend on the layer below it and nothing above. Auth doesn't know routing exists; routing doesn't import UI. You install @e_core/auth and you get auth, not a framework.

The principle underneath all of it: independent packages, clean separation, use only what you need.

$ e_core migrate --to v2
Scanning 15 packages…
Auth layer mapped
Routing guards rewritten
Theme tokens migrated
Done in 41.2s. 3 manual steps remain, see report.

The rewrite we almost didn't ship

Three months in, the rewrite looked like a mistake. Two codebases to maintain, a migration tool that only half-worked, and a team quietly wondering whether "good enough" v1 had been the right answer all along.

The turning point was small: the first app migrated end-to-end in an afternoon, and its bundle dropped by a third because it no longer shipped the layers it never used. Proof beats argument.

15 packages · 7 layers
18 months in the making
6+ apps migrated to v2

Migration, in practice

Migrating a real app meant running e_core migrate, letting it rewrite the mechanical changes, and handling the handful it flagged for a human. The guardrail that made it safe: the tool never touched a file it couldn't fully understand, it left a note instead.

What surprised us most was how people had used v1. Half the "manual steps" were undoing clever workarounds teams had built around v1's limitations, limitations v2 no longer had.

What we'd do differently

We'd draw the layer boundaries on day one, not day four hundred. Most of v1's pain wasn't the code: it was that the seams were in the wrong places, and seams are expensive to move once apps depend on them.

v3 thinking is already about the next seam: making a layer swappable without a migration at all. Quiet, present, one clear line at a time.

S
SiyabongaWriting for Existence

Notes, as they happen.

One email when we publish. No digest, no drip, just the post.