<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: naveenreddy devireddy</title>
    <description>The latest articles on DEV Community by naveenreddy devireddy (@naveenreddy_devireddy).</description>
    <link>https://dev.to/naveenreddy_devireddy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3943364%2F72e62d81-9181-4f8e-b2ba-4f412448fb3d.png</url>
      <title>DEV Community: naveenreddy devireddy</title>
      <link>https://dev.to/naveenreddy_devireddy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/naveenreddy_devireddy"/>
    <language>en</language>
    <item>
      <title>The Black Box Nobody Would Touch: Scaling Undocumented Legacy Code as a 3-Year Dev</title>
      <dc:creator>naveenreddy devireddy</dc:creator>
      <pubDate>Thu, 21 May 2026 06:50:09 +0000</pubDate>
      <link>https://dev.to/naveenreddy_devireddy/the-black-box-nobody-would-touch-scaling-undocumented-legacy-code-as-a-3-year-dev-21hg</link>
      <guid>https://dev.to/naveenreddy_devireddy/the-black-box-nobody-would-touch-scaling-undocumented-legacy-code-as-a-3-year-dev-21hg</guid>
      <description>&lt;p&gt;Three years into my career, I raised my hand for a job nobody else wanted to touch.&lt;/p&gt;

&lt;p&gt;I'd inherited a system with &lt;strong&gt;zero documentation&lt;/strong&gt; — the people who built it were long gone, and nobody fully understood how it worked. The kind of codebase everyone quietly hopes lands on someone else's plate.&lt;/p&gt;

&lt;p&gt;Then the brief came down: scale it to &lt;strong&gt;5,000 concurrent users&lt;/strong&gt;, turn it into &lt;strong&gt;multi-tenant SaaS&lt;/strong&gt;, and support &lt;strong&gt;per-client authentication&lt;/strong&gt; — some tenants on Active Directory, some on LDAP, some with their own mechanism.&lt;/p&gt;

&lt;p&gt;The existing system couldn't do any of that — and changing it felt risky enough that the work kept getting put off.&lt;/p&gt;

&lt;p&gt;I took the challenge. Here's what the black box actually looked like, and the method that got us through it — with one Senior DevOps, in about two months.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we inherited
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One server doing everything.&lt;/strong&gt; Node.js served the frontend pages &lt;em&gt;and&lt;/em&gt; ran the backend. Active Directory was tightly woven into the auth flow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two databases, two philosophies.&lt;/strong&gt; MySQL for transactions, MongoDB for reporting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data got into Mongo three different ways.&lt;/strong&gt; Sometimes a Python script pushed it. Sometimes the API wrote to it directly. It had been done different ways over time, with no shared convention.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No docs, no original authors, no map.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've ever opened a repo and felt your stomach drop, you know the feeling. The instinct in that moment is to rewrite it. &lt;strong&gt;That instinct is almost always wrong&lt;/strong&gt; — a rewrite of a system you don't understand just recreates the same unknowns, slower, while the business waits.&lt;/p&gt;

&lt;p&gt;So we didn't rewrite. We &lt;em&gt;tamed&lt;/em&gt; it. Here's the method.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Understand the system from the outside, not the code
&lt;/h2&gt;

&lt;p&gt;You can read a million lines of undocumented code and still not know what actually happens in production. So I stopped trying to read my way to understanding and started &lt;strong&gt;observing&lt;/strong&gt; instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logs and traffic&lt;/strong&gt; — what endpoints actually get hit, and how often.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The databases, live&lt;/strong&gt; — what queries are really running.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The process and the network&lt;/strong&gt; — what's listening, what talks to what.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Map the &lt;em&gt;behavior&lt;/em&gt;, not the source. Within days you have a real picture of the system the code comments never gave you.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Find the seams
&lt;/h2&gt;

&lt;p&gt;Once you can see how requests flow, you look for &lt;strong&gt;seams&lt;/strong&gt; — the natural boundaries where you can change something without unraveling the whole sweater. For us the important ones were the authentication boundary, the heavy reporting read paths, and the frontend vs. backend responsibilities that happened to live in the same process.&lt;/p&gt;

&lt;p&gt;You don't need to understand every line — you need to understand the edges.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Scale the box from the outside &lt;em&gt;before&lt;/em&gt; touching its insides
&lt;/h2&gt;

&lt;p&gt;The key insight, and the part I'd tell every junior engineer: &lt;strong&gt;you can scale a system you don't fully understand by changing what's &lt;em&gt;around&lt;/em&gt; it, not what's &lt;em&gt;inside&lt;/em&gt; it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We went from one server to four:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0veyxdq3kyb7amig96ax.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0veyxdq3kyb7amig96ax.png" alt=" " width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Concretely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Split frontend and backend onto their own servers&lt;/strong&gt; — 2 for frontend, 2 for backend. Each frontend ran &lt;strong&gt;nginx as a reverse proxy that load-balanced across &lt;em&gt;both&lt;/em&gt; backend servers&lt;/strong&gt;, so traffic spread evenly and either backend could drop without taking the app down. (They also had different scaling profiles; co-locating them on one box was the original bottleneck.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ran the Node backend under &lt;a href="https://pm2.keymetrics.io/" rel="noopener noreferrer"&gt;PM2&lt;/a&gt; in cluster mode&lt;/strong&gt; — 4 workers per server, so a single Node process stopped being a single point of CPU contention.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Added read replicas for both MySQL and MongoDB.&lt;/strong&gt; Reporting reads went to replicas; transactional writes stayed on the primaries. This alone took enormous pressure off the system without changing a line of business logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this required understanding the messy internals. It's infrastructure and topology — and it bought us the headroom for 5,000 concurrent users.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Isolate the one risky internal change behind an abstraction
&lt;/h2&gt;

&lt;p&gt;The part we &lt;em&gt;did&lt;/em&gt; have to change was auth — going multi-tenant meant Active Directory could no longer be hardcoded. Each tenant needed its own mechanism: AD, LDAP, or custom.&lt;/p&gt;

&lt;p&gt;Instead of touching AD logic scattered everywhere, we introduced a single seam: a pluggable auth provider, chosen per tenant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// One interface, one adapter per mechanism.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authProviders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;ad&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./auth/activeDirectory&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;ldap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./auth/ldap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./auth/custom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAuthProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;authProviders&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authType&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`No auth provider for &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authType&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tenant&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;resolveTenant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// by subdomain / header&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAuthProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tenant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// ...issue the session / token&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz22xn317kxpyr5snkuri.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz22xn317kxpyr5snkuri.png" alt=" " width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every adapter implements the same &lt;code&gt;authenticate(credentials, config)&lt;/code&gt; contract. The old AD logic became &lt;em&gt;one&lt;/em&gt; adapter behind the interface instead of an assumption baked through the codebase. Adding a new tenant's auth no longer means surgery on the core — just one small, isolated adapter.&lt;/p&gt;

&lt;p&gt;That's the legacy-taming move in miniature: &lt;strong&gt;don't refactor the whole thing — wrap the risky part in a boundary and change behavior there.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Change incrementally, verify behavior every step
&lt;/h2&gt;

&lt;p&gt;Because nobody understood the system fully, every change went out small and observable. Capture how it behaves now, change one thing, confirm nothing drifted, repeat. Slow is smooth, smooth is fast — especially when the safety net is observability, not people who remember how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it landed
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;One server → &lt;strong&gt;four&lt;/strong&gt; (2 frontend, 2 backend), backend under &lt;strong&gt;PM2 cluster (4 workers each)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read replicas&lt;/strong&gt; on both MySQL and MongoDB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pluggable per-tenant auth&lt;/strong&gt; — AD, LDAP, or custom — instead of hardcoded Active Directory.&lt;/li&gt;
&lt;li&gt;Capacity for &lt;strong&gt;5,000 concurrent users&lt;/strong&gt;, multi-tenant ready.&lt;/li&gt;
&lt;li&gt;Delivered in &lt;strong&gt;~2 months&lt;/strong&gt;, with one Senior DevOps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the part I didn't expect: doing this &lt;em&gt;as a 3-year engineer&lt;/em&gt; — taking the thing no one else would touch and bringing it through — &lt;strong&gt;changed my standing in the company.&lt;/strong&gt; It taught me that the scary, undocumented, "don't touch it" systems are exactly where you earn trust the fastest.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell my 3-year-old self
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Don't rewrite what you don't understand.&lt;/strong&gt; Tame it first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observe the running system before you read the code.&lt;/strong&gt; Behavior is the real documentation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can scale a black box from the outside&lt;/strong&gt; — topology, clustering, replicas — long before you refactor its insides.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrap risky change in a seam.&lt;/strong&gt; Adapters let you change behavior without understanding everything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The systems nobody wants to touch are the biggest career opportunities.&lt;/strong&gt; Raise your hand.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;Have you inherited a black box like this? What did you do first — read the code, or watch it run?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Written as a general lesson on taming legacy systems — no company, client, or proprietary specifics, just the approach.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>legacy</category>
      <category>architecture</category>
      <category>node</category>
      <category>career</category>
    </item>
  </channel>
</rss>
