Skip to main content

Command Palette

Search for a command to run...

Fixing Hoichoi's Device Management: A Hidden SOLID Principle in Action

Updated
4 min read

Today was not meant to be educational.

It was meant to be safe.

I work as a full-time software engineer at Hoichoi, building backend systems that power a production platform used by millions — you probably know it as https://hoichoi.tv.

And when you’re working in a system of that scale, your first priority is not elegance.
It’s not breaking things.

Ironically, that’s exactly how I ended up using a core principle of SOLID — completely by accident.


The Context: Massive Backend, Partial Understanding

The Hoichoi backend is big.

  • Multiple services

  • Node.js with NestJS

  • Golang services for performance-critical paths

  • Shared auth, sessions, device management

  • Years of business logic layered on top of each other

On this particular day, I was working on Device Management.

The rule sounds simple:

One device → one active session

But production bugs rarely respect simplicity.


The Bug: One Device, Multiple Sessions (Thanks to OTP)

The issue was subtle but dangerous.

Every time a user:

  • Logged in

  • Verified OTP

The system was:

  • Creating a new session

  • Treating it as a new device

  • Even though it was the same physical device

Result:

  • One phone

  • Multiple sessions

  • Device limits breaking silently

This is the kind of bug that doesn’t crash servers —
it corrupts business logic.


The Hidden Villain: Hashed Device IDs

After tracing the flow, the culprit became clear.

We were relying on:

  • A hashed deviceId coming from SuperTokens

And that meant:

  • Every OTP verification

  • Generated a new hash

  • Which the system interpreted as a new device

So from the backend’s perspective:

“Looks new. Create a new session.”

Technically correct.
Logically disastrous.


The Dangerous Option: Modify the Core Logic

At first glance, the obvious fix was:

“Let’s change the session-creation logic.”

But that logic:

  • Is shared across flows

  • Is relied upon by multiple services

  • Handles edge cases I don’t fully own

  • Is already fragile in places

Touching it directly would mean:

  • Expanding the blast radius

  • Introducing regression risk

  • Owning bugs I didn’t create

So I stopped myself.


The Better Question: Where Is This Parameter Generated?

Instead of asking:

“How do I fix this function?”

I asked:

“Where does this function get its input from?”

That question saved me.

Tracing backwards, I found the exact point where:

  • The deviceId is constructed

  • Before it ever reaches the core session logic

That’s when the plan formed.


The Fix: Pass Raw deviceId, Don’t Touch the Core

I made a deliberate decision:

  • ❌ Do not modify existing session logic

  • ❌ Do not refactor shared code

  • ❌ Do not fight SuperTokens internals

Instead:

  • ✅ Pass the raw, stable deviceId

  • ✅ At the parameter-generation layer

  • ✅ Before hashing ever caused ambiguity

So the downstream logic:

  • Stayed untouched

  • Continued doing what it always did

  • But now received consistent identity data

Same device → same session.
Problem solved.


The Realization: This Was Open/Closed Principle

Only after the fix was deployed did it hit me.

I didn’t modify existing behavior.
I extended the system by changing inputs at the boundary.

That’s textbook Open/Closed Principle:

Open for extension, closed for modification

But in real production systems, OCP doesn’t look like fancy inheritance diagrams.

It looks like:

  • Choosing where to intervene

  • Respecting existing contracts

  • Reducing risk instead of showing cleverness


Why This Matters in Real Backend Engineering

Books teach SOLID in isolation.

Production teaches it under pressure.

In real systems:

  • You don’t own all the context

  • You don’t understand every side effect

  • You don’t get unlimited refactor time

Good engineers don’t ask:

“How do I rewrite this?”

They ask:

“What is the smallest, safest place to change?”

That mindset is architecture — whether you name it or not.


What This Bug Taught Me (For Real)

  1. Identity bugs are more dangerous than crashes

  2. Inputs are safer extension points than internals

  3. Design principles emerge naturally at scale

  4. SOLID is not academic — it’s defensive engineering

  5. Production safety > theoretical purity


Final Thought

I didn’t plan to apply SOLID today.

I planned to:

  • Fix a device bug

  • Avoid breaking authentication

  • Sleep without pager anxiety

Turns out, when systems get big and unforgiving,
good survival instincts look exactly like good design principles.

And that’s how I accidentally practiced SOLID —
while just trying to keep Hoichoi stable.

More from this blog

A

AnkanHub

16 posts