Back to all writing

I Finally Put All Three Apps on One Real Backend

IPMAT Series•Part 37•6 min read•By Mikhil

I Finally Put All Three Apps on One Real Backend

There is a strange point in a project where the architecture stops being something you can explain with arrows.

It has to exist.

For a while, I could describe the IPMAT ecosystem pretty cleanly.

Verbal handles Verbal.

IPMATHics handles Quant.

Strata understands the learner across both.

One account.

One history.

One shared preparation model underneath.

That sounded coherent.

The problem was that parts of it were still more true in diagrams than in the actual system.

The three products had grown at different times.

They had different assumptions.

Different migrations.

Different data paths.

Different ideas of what "progress" meant.

Eventually, I had to stop designing the shared backend and actually make one.

The old architecture had history

Verbal came first.

Naturally, its backend reflected Verbal.

Then Quant became serious enough to deserve its own product.

Then Strata became serious enough to need evidence from both.

Every new stage added legitimate requirements.

The problem was that legitimate requirements can still pile up into a messy system.

A migration written for one era does not automatically become the right migration for the next one.

A table that made perfect sense for a single app can become awkward when three products need to understand the same learner.

I did not want to solve that by starting from zero.

The existing Verbal history mattered.

The newer shared model mattered too.

So the backend had to move forward without pretending the past did not exist.

One account needed to become an actual platform rule

The basic requirement was simple:

One learner should have one identity across the ecosystem.

Not three accounts that happen to use the same email.

Not three local histories that sometimes sync.

One actual user identity.

That identity then has to own attempts, reviews, bookmarks, preferences, devices, content versions, sessions, mock history, recommendations and derived learner state.

The moment that became the standard, a lot of casual shortcuts stopped being acceptable.

The server needed more authority

A learning app becomes much easier to trust when the browser is not allowed to define reality by itself.

The client can report:

"I selected option B."

The server should decide whether B was correct.

The client can report:

"This is the event I am retrying."

The server should decide whether that event already exists.

The client can request access.

The server should decide whether the learner actually has it.

This does not make the client unimportant.

It makes the contract clearer.

The client handles experience.

The backend protects truth.

Content needed versions, not just rows

Suppose a learner answered something last week.

Then I fix the wording.

Maybe I improve the explanation.

Maybe the answer key changes because the original item was flawed.

What should last week's history mean now?

It should still refer to what the learner actually saw.

That means published content needs versions.

Questions.

Vocabulary.

Mocks.

Anything that contributes to learner evidence.

History should not mutate because the present improved.

Offline work made the backend less simple

I still want the apps to tolerate real life.

Weak internet.

Temporary disconnection.

App backgrounding.

A learner solving something and reconnecting later.

That means the backend cannot demand a perfect connection for every interaction.

But it also cannot accept unlimited unverifiable history forever.

So the system needs stable event IDs, ownership, retry rules, ordering and reconciliation when the device returns.

The words are boring.

The experience they create is simple:

Keep working.

We will catch up safely.

Security stopped being a final checklist

The shared backend forced me to think about permissions much earlier.

Who can read a question?

Who can read its answer?

Who can publish content?

Who can write learner evidence?

Who can derive Strata state?

Who can alter access?

The easiest backend is one where the browser can touch everything.

It is also the backend I do not want anywhere near real learners.

The shared system had to narrow those paths.

The migration itself became defensive

I started liking boring failure.

Unexpected schema?

Stop.

Something already installed in a way the migration does not understand?

Stop.

Transaction fails?

Rollback.

That is much better than "try to make it work."

When the foundation belongs to three products, partial success can be worse than failure.

A clean stop is useful.

A half-migrated learner history is not.

This was not the finish line

Getting the shared backend in place did not suddenly make the ecosystem complete.

That would have been a very convenient ending.

The client apps still had to speak the same contract.

Old local paths still existed.

Strata still needed trusted evidence to arrive correctly.

Real account switching still needed testing.

Real device behaviour still needed testing.

A backend can support a workflow before the workflow actually uses it.

That distinction became painfully important almost immediately.

But something fundamental changed

Before this, I could say:

"We are building toward one shared learner."

Now there was a real foundation capable of supporting that statement.

One identity.

Shared ownership.

Server authority.

Versioned evidence.

A place for Strata to read from.

A place for Verbal and IPMATHics to write to.

The architecture had somewhere real to live.

The backend became less visible as it became more important

There is no flashy screenshot for this part.

No new home screen.

No animation.

No chart.

Just a lot of rules that make the product harder to corrupt.

That is probably why infrastructure work is so easy to underestimate.

The learner will never see most of it.

They will notice when it fails.

This project started with 32 questions

Back then, "backend" barely mattered.

The app needed to remember what I did.

That was enough.

Now three products need to agree about who I am, what I did, what I saw, what counted, what changed, and what the system is allowed to conclude from it.

That is a very different product.

The shared backend did not make the ecosystem complete.

It made the next problems honest.

And I learned very quickly that putting three apps on one backend is not the same thing as connecting them.