<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Full Stack Developer]]></title><description><![CDATA[Full Stack Developer]]></description><link>https://fullstackdev1.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 11 Sep 2026 16:23:19 GMT</lastBuildDate><atom:link href="https://fullstackdev1.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[I Deleted My Entire MERN Backend and Rebuilt It in 24 Hours]]></title><description><![CDATA[I did something that probably looked stupid from the outside.
I deleted my backend.
Not one file.
Not one feature.
The entire backend.
The API routes, controllers, authentication logic, database code,]]></description><link>https://fullstackdev1.hashnode.dev/i-deleted-my-entire-mern-backend-and-rebuilt-it-in-24-hours</link><guid isPermaLink="true">https://fullstackdev1.hashnode.dev/i-deleted-my-entire-mern-backend-and-rebuilt-it-in-24-hours</guid><category><![CDATA[MERN Stack]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Express.js]]></category><category><![CDATA[MongoDB]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Nausheen]]></dc:creator><pubDate>Sat, 05 Sep 2026 18:11:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69c147af30a9b81e3a58595d/0bb4d05d-7f00-4d2e-b1be-d799ecf5f86b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I did something that probably looked stupid from the outside.</p>
<p>I deleted my backend.</p>
<p>Not one file.</p>
<p>Not one feature.</p>
<p><strong>The entire backend.</strong></p>
<p>The API routes, controllers, authentication logic, database code, middleware, utility functions — everything was gone.</p>
<p>And the worst part?</p>
<p><strong>The application was already working.</strong></p>
<p>It wasn't perfect, but it worked.</p>
<p>Users could register.</p>
<p>Users could log in.</p>
<p>The frontend could communicate with the API.</p>
<p>Data was being stored in MongoDB.</p>
<p>So why did I delete it?</p>
<p>Because every time I wanted to change something, I was afraid of breaking something else.</p>
<p>That was the real problem.</p>
<p>My backend had stopped being code I understood and had slowly become code I was afraid to touch.</p>
<p>So I gave myself one rule:</p>
<blockquote>
<p><strong>24 hours. Rebuild the backend from scratch. No copying the old implementation.</strong></p>
</blockquote>
<p>This is what happened.</p>
<hr />
<h2>The Backend Worked. That Wasn't Enough.</h2>
<p>When you are building a project, there is a point where you stop asking:</p>
<blockquote>
<p>"Does this work?"</p>
</blockquote>
<p>and start asking:</p>
<blockquote>
<p>"Do I actually understand why this works?"</p>
</blockquote>
<p>I had reached that point.</p>
<p>My backend had grown feature by feature.</p>
<p>First came the routes.</p>
<p>Then controllers.</p>
<p>Then authentication.</p>
<p>Then database queries.</p>
<p>Then middleware.</p>
<p>Then error handling.</p>
<p>Then a few quick fixes.</p>
<p>Then another quick fix for the previous quick fix.</p>
<p>You probably know where this goes.</p>
<p>Eventually, the folder structure looked reasonable, but the logic inside it wasn't.</p>
<p>A typical request might travel through something like:</p>
<pre><code class="language-text">React
  ↓
API request
  ↓
Route
  ↓
Middleware
  ↓
Controller
  ↓
Database
  ↓
Response
  ↓
React
</code></pre>
<p>That architecture isn't complicated.</p>
<p><strong>My implementation was.</strong></p>
<p>I had duplicated logic.</p>
<p>Some controllers were doing too much.</p>
<p>Some validation happened in multiple places.</p>
<p>Authentication-related code was scattered around.</p>
<p>And whenever I needed to modify a feature, I first had to understand what previous-me had done.</p>
<p>That was the signal.</p>
<p>I didn't need another feature.</p>
<p>I needed to understand my backend.</p>
<hr />
<h1>The Decision</h1>
<p>I opened the backend directory.</p>
<p>I stared at it for a few minutes.</p>
<p>Then I deleted it.</p>
<p>No backup.</p>
<p>No copy-paste.</p>
<p>No opening the old implementation in another window.</p>
<p>Just a clean backend directory.</p>
<p>The first few minutes felt great.</p>
<p>Then reality hit.</p>
<p>I had to rebuild everything.</p>
<p>From zero.</p>
<hr />
<h1>Hour 1: Setting Up the Project Again</h1>
<p>I started with the basics.</p>
<p>The first goal wasn't authentication.</p>
<p>It wasn't CRUD.</p>
<p>It wasn't some fancy architecture.</p>
<p>It was simply:</p>
<blockquote>
<p><strong>Get the server running.</strong></p>
</blockquote>
<p>I created the project and installed the dependencies I actually needed.</p>
<p>The basic structure looked something like this:</p>
<pre><code class="language-text">backend/
│
├── controllers/
├── middleware/
├── models/
├── routes/
├── services/
├── utils/
│
├── app.js
├── server.js
├── package.json
└── .env
</code></pre>
<p>I deliberately avoided creating 20 folders on day one.</p>
<p>One of the mistakes I made previously was confusing a lot of folders with good architecture.</p>
<p>It isn't.</p>
<p>Architecture isn't about having more folders.</p>
<p>It's about having clear responsibilities.</p>
<hr />
<h1>Hour 2: Connecting MongoDB</h1>
<p>Next came the database.</p>
<p>The basic requirement was simple:</p>
<pre><code class="language-text">Node.js
   ↓
MongoDB
</code></pre>
<p>I kept the database connection separate from the application startup logic.</p>
<p>Something along these lines:</p>
<pre><code class="language-javascript">import mongoose from "mongoose";

const connectDB = async () =&gt; {
    try {
        await mongoose.connect(process.env.MONGO_URI);
        console.log("MongoDB connected");
    } catch (error) {
        console.error("Database connection failed");
        process.exit(1);
    }
};

export default connectDB;
</code></pre>
<p>Then the application could start through a clear entry point:</p>
<pre><code class="language-javascript">import app from "./app.js";
import connectDB from "./config/database.js";

const PORT = process.env.PORT || 5000;

await connectDB();

app.listen(PORT, () =&gt; {
    console.log(`Server running on port ${PORT}`);
});
</code></pre>
<p>This looks boring.</p>
<p>And that's exactly what I wanted.</p>
<p>The boring parts should stay boring.</p>
<hr />
<h1>Hour 4: Designing the Data Models Again</h1>
<p>This time, I didn't immediately start writing schemas.</p>
<p>I first asked:</p>
<p><strong>What data does my application actually need?</strong></p>
<p>That question sounds obvious.</p>
<p>But when you're rushing to build a feature, it's surprisingly easy to create a database structure based on what the current screen needs instead of what the application actually represents.</p>
<p>I went through each entity.</p>
<p>What fields are required?</p>
<p>Which fields are optional?</p>
<p>Which values should be unique?</p>
<p>Which relationships exist?</p>
<p>What should happen if a record is deleted?</p>
<p>This took longer than I expected.</p>
<p>But it saved me from writing unnecessary logic later.</p>
<hr />
<h1>Hour 6: Authentication</h1>
<p>This was the part I was most interested in rebuilding.</p>
<p>My previous authentication system worked.</p>
<p>But I couldn't explain every part of it confidently.</p>
<p>That bothered me.</p>
<p>So I rebuilt it step by step.</p>
<p>The flow became much clearer:</p>
<pre><code class="language-text">Register
   ↓
Validate input
   ↓
Hash password
   ↓
Save user
</code></pre>
<p>And login:</p>
<pre><code class="language-text">Login
   ↓
Find user
   ↓
Compare password
   ↓
Generate token
   ↓
Return response
</code></pre>
<p>Then protected routes:</p>
<pre><code class="language-text">Request
   ↓
Authorization header
   ↓
Verify token
   ↓
Attach user
   ↓
Controller
</code></pre>
<p>The important part wasn't writing the JWT code.</p>
<p>The important part was understanding what happened <strong>between the request and the controller</strong>.</p>
<p>For example, middleware could extract the token and verify it before allowing the request to continue:</p>
<pre><code class="language-javascript">const protect = async (req, res, next) =&gt; {
    try {
        const token = req.headers.authorization?.split(" ")[1];

        if (!token) {
            return res.status(401).json({
                message: "Authentication required"
            });
        }

        const decoded = jwt.verify(
            token,
            process.env.JWT_SECRET
        );

        req.user = decoded;

        next();
    } catch (error) {
        return res.status(401).json({
            message: "Invalid or expired token"
        });
    }
};
</code></pre>
<p>Suddenly, protected routes became much easier to reason about.</p>
<pre><code class="language-javascript">router.get(
    "/profile",
    protect,
    getProfile
);
</code></pre>
<p>The code wasn't necessarily revolutionary.</p>
<p><strong>My understanding was.</strong></p>
<hr />
<h1>Hour 10: Routes and Controllers</h1>
<p>This was where I noticed one of my biggest previous mistakes.</p>
<p>I had allowed controllers to become mini applications.</p>
<p>A controller shouldn't have to know everything.</p>
<p>So I tried to keep the responsibilities clearer:</p>
<pre><code class="language-text">Route
  ↓
Middleware
  ↓
Controller
  ↓
Service
  ↓
Database
</code></pre>
<p>For example:</p>
<pre><code class="language-javascript">router.post(
    "/users",
    validateUser,
    createUser
);
</code></pre>
<p>The controller handles the HTTP-level concerns.</p>
<pre><code class="language-javascript">const createUser = async (req, res) =&gt; {
    try {
        const user = await userService.createUser(req.body);

        res.status(201).json(user);
    } catch (error) {
        res.status(500).json({
            message: error.message
        });
    }
};
</code></pre>
<p>And the service handles the actual business logic.</p>
<pre><code class="language-javascript">const createUser = async (data) =&gt; {
    // business logic
    // database interaction
};
</code></pre>
<p>This separation wasn't about making the project look "enterprise."</p>
<p>It made the code easier to change.</p>
<p>That's what mattered.</p>
<hr />
<h1>Hour 13: I Broke Everything</h1>
<p>This was probably the most useful part of the rebuild.</p>
<p>Because the backend was new, I intentionally tried to break it.</p>
<p>I sent:</p>
<ul>
<li><p>Missing fields</p>
</li>
<li><p>Invalid IDs</p>
</li>
<li><p>Invalid credentials</p>
</li>
<li><p>Expired tokens</p>
</li>
<li><p>Duplicate users</p>
</li>
<li><p>Empty requests</p>
</li>
<li><p>Unauthorized requests</p>
</li>
<li><p>Requests to nonexistent resources</p>
</li>
</ul>
<p>And yes...</p>
<p>It broke.</p>
<p>A lot.</p>
<p>But this time, I knew where to look.</p>
<p>When authentication failed, I knew whether the problem was:</p>
<pre><code class="language-text">Route
↓
Middleware
↓
Token
↓
Controller
</code></pre>
<p>When a database operation failed, I knew which layer was responsible.</p>
<p>That was a huge difference from my old backend.</p>
<hr />
<h1>Hour 16: Error Handling</h1>
<p>One thing I had underestimated before was error handling.</p>
<p>A backend isn't only responsible for successful requests.</p>
<p>A good backend also needs to explain failure consistently.</p>
<p>Instead of returning completely different structures everywhere:</p>
<pre><code class="language-javascript">{
    "error": "Something went wrong"
}
</code></pre>
<p>or:</p>
<pre><code class="language-javascript">{
    "message": "User not found"
}
</code></pre>
<p>or:</p>
<pre><code class="language-javascript">{
    "msg": "Invalid request"
}
</code></pre>
<p>I started thinking about a consistent response format.</p>
<p>For example:</p>
<pre><code class="language-javascript">{
    "success": false,
    "message": "User not found"
}
</code></pre>
<p>The exact format isn't the important part.</p>
<p><strong>Consistency is.</strong></p>
<p>When the frontend consumes your API, predictable responses make everything easier.</p>
<hr />
<h1>Hour 18: Testing the API</h1>
<p>I didn't want to discover problems only after connecting the frontend.</p>
<p>So I tested the API independently.</p>
<p>For every major endpoint, I asked:</p>
<h3>Does the happy path work?</h3>
<pre><code class="language-text">POST /api/users
</code></pre>
<h3>What happens with invalid data?</h3>
<pre><code class="language-text">POST /api/users
{
    "email": ""
}
</code></pre>
<h3>What happens without authentication?</h3>
<pre><code class="language-text">GET /api/profile
</code></pre>
<h3>What happens with an invalid token?</h3>
<pre><code class="language-text">Authorization: Bearer invalid-token
</code></pre>
<h3>What happens when the resource doesn't exist?</h3>
<pre><code class="language-text">GET /api/users/does-not-exist
</code></pre>
<p>These tests weren't complicated.</p>
<p>But they forced me to think about the API as a system rather than a collection of endpoints.</p>
<hr />
<h1>Hour 21: Connecting the React Frontend</h1>
<p>Only after the backend felt stable did I connect the frontend.</p>
<p>This time, I paid attention to the contract between frontend and backend.</p>
<p>For example:</p>
<pre><code class="language-text">Frontend
POST /api/login
        ↓
Backend
        ↓
Validate credentials
        ↓
Generate token
        ↓
Return response
        ↓
Frontend stores authentication state
</code></pre>
<p>A full-stack application isn't really two separate applications.</p>
<p>The frontend and backend have to agree on:</p>
<ul>
<li><p>URLs</p>
</li>
<li><p>HTTP methods</p>
</li>
<li><p>Request bodies</p>
</li>
<li><p>Response structures</p>
</li>
<li><p>Authentication</p>
</li>
<li><p>Error responses</p>
</li>
<li><p>Status codes</p>
</li>
</ul>
<p>A mismatch in any of these can turn into a frustrating debugging session.</p>
<hr />
<h1>Hour 23: The Moment I Realized the Rebuild Worked</h1>
<p>I opened the application.</p>
<p>Registered a user.</p>
<p>Logged in.</p>
<p>Created data.</p>
<p>Fetched it.</p>
<p>Updated it.</p>
<p>Deleted it.</p>
<p>Logged out.</p>
<p>Logged in again.</p>
<p>Everything worked.</p>
<p>And then I realized something strange.</p>
<p>The new backend had fewer lines of code.</p>
<p>But more importantly...</p>
<p><strong>I could explain it.</strong></p>
<p>If someone asked:</p>
<blockquote>
<p>"Where is authentication handled?"</p>
</blockquote>
<p>I knew.</p>
<p>If someone asked:</p>
<blockquote>
<p>"Where does this request go?"</p>
</blockquote>
<p>I could trace it.</p>
<p>If someone asked:</p>
<blockquote>
<p>"Where should I add this business rule?"</p>
</blockquote>
<p>I knew where it belonged.</p>
<p>That was worth more than the code I had deleted.</p>
<hr />
<h1>What I Would Do Differently Next Time</h1>
<p>The biggest lesson wasn't:</p>
<blockquote>
<p>"Delete your backend and rebuild it."</p>
</blockquote>
<p>Please don't interpret the article that way.</p>
<p>Deleting working code is risky.</p>
<p>I did it because I was deliberately trying to learn from a project I had already built.</p>
<p>For a production application, I would absolutely prefer:</p>
<ul>
<li><p>Git branches</p>
</li>
<li><p>Tests</p>
</li>
<li><p>Incremental refactoring</p>
</li>
<li><p>Backups</p>
</li>
<li><p>Code reviews</p>
</li>
<li><p>Monitoring</p>
</li>
<li><p>A migration plan</p>
</li>
</ul>
<p>I wouldn't randomly delete production code because I felt like rewriting it.</p>
<p>The useful lesson is different.</p>
<p><strong>If your code works but you don't understand it, you have a problem.</strong></p>
<hr />
<h1>5 Things I Learned From Rebuilding My Backend</h1>
<h2>1. Working code isn't necessarily good code</h2>
<p>A backend can return the correct response while still being difficult to maintain.</p>
<p>"Works" is only one measurement.</p>
<p>You should also ask:</p>
<blockquote>
<p>Can I understand it?</p>
</blockquote>
<blockquote>
<p>Can I modify it?</p>
</blockquote>
<blockquote>
<p>Can I test it?</p>
</blockquote>
<blockquote>
<p>Can another developer work with it?</p>
</blockquote>
<hr />
<h2>2. Don't create abstractions too early</h2>
<p>I used to think more abstraction automatically meant better architecture.</p>
<p>It doesn't.</p>
<p>Sometimes a simple function is better than creating:</p>
<pre><code class="language-text">Controller
Service
Repository
Factory
Adapter
Helper
Manager
</code></pre>
<p>for a feature that has 30 lines of logic.</p>
<p>Create abstractions when they solve a real problem.</p>
<p>Not because an architecture diagram looks impressive.</p>
<hr />
<h2>3. Authentication becomes easier when you understand the request lifecycle</h2>
<p>Instead of memorizing JWT tutorials, understand the flow:</p>
<pre><code class="language-text">Client
 ↓
Request
 ↓
Authorization header
 ↓
Authentication middleware
 ↓
Token verification
 ↓
req.user
 ↓
Controller
</code></pre>
<p>Once that makes sense, authentication stops feeling like magic.</p>
<hr />
<h2>4. Error handling is part of the API design</h2>
<p>A successful request is only half the story.</p>
<p>Your API also needs predictable behavior when things go wrong.</p>
<p>That means thinking about:</p>
<pre><code class="language-text">400 → Bad input
401 → Not authenticated
403 → Not authorized
404 → Resource doesn't exist
409 → Conflict
500 → Server error
</code></pre>
<p>The exact implementation can vary.</p>
<p>The important thing is to design failure cases intentionally.</p>
<hr />
<h2>5. The best code is code you can change</h2>
<p>This was the biggest lesson for me.</p>
<p>When I finished the rebuild, I wasn't proud because I had written a backend in 24 hours.</p>
<p>I was proud because I could finally look at the project and think:</p>
<blockquote>
<p>"I know where everything belongs."</p>
</blockquote>
<p>That feeling is different.</p>
<hr />
<h1>Would I Delete My Backend Again?</h1>
<p>Probably not.</p>
<p>At least, not literally.</p>
<p>The next time I see a messy backend, I'll probably create a branch and refactor it instead.</p>
<p>But I'm glad I did it once.</p>
<p>Because deleting the code removed something I didn't realize I was carrying:</p>
<p><strong>dependency on my own previous implementation.</strong></p>
<p>I had been treating my old code like something I couldn't touch.</p>
<p>After rebuilding it, I realized that code isn't sacred.</p>
<p>If you understand the problem well enough, you can rebuild the solution.</p>
<p>And sometimes rebuilding from scratch teaches you more than adding another feature ever will.</p>
<hr />
<h1>Final Thought</h1>
<p>If you're working on a MERN project right now and your backend feels messy, don't immediately delete everything.</p>
<p>Open one feature.</p>
<p>Trace its entire lifecycle:</p>
<pre><code class="language-text">Request
 ↓
Route
 ↓
Middleware
 ↓
Controller
 ↓
Service
 ↓
Database
 ↓
Response
</code></pre>
<p>Then ask yourself:</p>
<p><strong>"Can I explain every step?"</strong></p>
<p>If the answer is no, that's probably the part you should learn next.</p>
<p>Because becoming a better developer isn't about writing more code.</p>
<p>It's about needing less code to solve the same problem.</p>
<p>And sometimes, the fastest way to understand your code...</p>
<p>is to imagine that tomorrow you have to rebuild it from zero.</p>
]]></content:encoded></item></channel></rss>