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

Today was not meant to be educational.

It was meant to be *safe*.

I work as a **full-time software engineer at** [**Hoichoi**](https://hoichoi.tv), building backend systems that power a production platform used by millions — you probably know it as [**https://hoichoi.tv**](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 &gt; 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.
