feat(blog): 更新博客文章,分享全栈开发者的技术选型反思与经验

This commit is contained in:
joyzhao
2026-01-09 12:00:02 +08:00
parent 3d0a37a751
commit 63c97c9b00
3 changed files with 94 additions and 170 deletions

View File

@@ -1,78 +1,76 @@
---
layout: "@/layouts/BlogPostLayout.astro"
title: "Stop Being Held Hostage by 'Best Practices': Confessions of a Full-Stack Developer's Tech Stack Struggles"
description: "A full-stack developer's honest reflection on getting trapped by chasing 'best practices' while building a simple 30-endpoint project. This article explores the cognitive load of heavy frameworks, the Monorepo trap, and proposes a practical two-tier tech stack selection strategy."
date: "2026-01-08"
image: "https://images.unsplash.com/photo-1518770660439-4636190af475?q=80&w=1470&auto=format&fit=crop"
tags: ["Tech Stack", "Full-Stack Development", "Framework Selection", "Best Practices", "Developer Experience"]
tagId: ["tech-stack", "fullstack", "framework", "best-practices", "developer-experience"]
category: "Technology"
categoryId: "technology"
readTime: "6 min read"
---
# All This Tinkering... For What? Engineering Reflections from a Full-Stack Developer
> **Foreword:**
> I am a developer who transitioned from Frontend to Node.js Full-Stack. This article is simply a summary of my recent experiences and reflections while developing a project. Given my limited knowledge and perspective, the views expressed here may not be universally "correct" or represent industry standards. This is just a personal debrief after stepping into countless pitfalls, shared in the hope of exchanging ideas with the community and providing a reference for those facing similar dilemmas. If there are any inaccuracies, please feel free to correct me in the comments.
> *A log of the soul-crushing details I encountered during a recent project setup. This is a summary of my own internal tug-of-war—lessons learned the hard way through endless trial and error. It might not be the "correct" way, but every word was paid for in lost time.*
Recently, I set out to build a small bookmark-style tool with only about 30 endpoints. I thought it would take two weeks; instead, I spent over a month just "wrestling" with the tech stack.
I recently set out to build a simple bookmarking tool. It only needed about 30 endpoints, yet I spent nearly a month agonizing over the tech stack. I felt like I was wandering through a dark forest, sprinting toward every glimmer of light only to find a new pitfall waiting for me.
I felt like a hunter lost in a technical fog: wherever I saw a light (a new tool or an "expert" opinion), I rushed toward it, only to find a deeper pit hidden behind every glow.
### Step 1: Chasing the "Mainstream" and Feeling the Friction
## 1. Chasing Trends is the Start of Internal Friction
It started because I wanted to build a side project. In my day job, I use Vue; its second nature to me. But everywhere I looked online, the consensus was: **"Next.js + React + shadcn-ui"** is the gold standard. Great ecosystem, endless components, future-proof.
I started by following the crowd and chose **Next.js + NestJS + shadcn-ui**. I thought, "Since everyone says this is the 'Full-Stack Gold Standard,' I can't go wrong." The reality, however, gave me a swift wake-up call.
I told myself: *Dont get stuck in your comfort zone. Try something new.* So, I benched Vue and picked up React. I was immediately hit by **choice paralysis.**
In Next.js, I wasted a whole week just deciding on a data-fetching and state management solution (SWR vs. Zustand?). Once I finally started, I was overwhelmed by the complexity of Server Components (RSC) vs. Client Components—constantly defining `"use client"`, fixing mysterious Hydration errors, and manually managing state dependencies while optimizing endless callback functions.
It wasn't just a choice between A and B; it was a choice between five different ways to do everything. I spent more time reading docs and comparisons than writing logic:
I kept thinking: **I just want to write some simple business logic. Why am I spending 80% of my energy dealing with the overhead of the framework?**
* Do I fetch data with **SWR** or **TanStack Query**?
* Is global state better in **Zustand**, **Redux**, or **Jotai**?
* How do I bridge the gap between server and client data?
## 2. The Heavier the Framework, the Heavier the Cognitive Load
Instead of building features, I was researching "how to build." To make matters worse, I kept hitting those cryptic **"Hydration Errors."** One refresh, one error, and an hour gone trying to find the mismatch.
Later, I switched the frontend to Nuxt, which was indeed smoother. But on the backend, I stuck with **NestJS**, chasing so-called "standardization" and "enterprise engineering."
The most draining part was tagging everything with `"use client"`. I felt less like a developer and more like a clerk labeling boxes. I kept thinking: **"If most of this ends up running on the client anyway, why am I using a framework that splits the stack so aggressively and demands such high mental overhead?"**
But I only had 30 endpoints. The logic was incredibly simple. In NestJS, I was forced to write Controllers, Services, Modules, DTOs... the amount of code tripled. Even worse was the **ESM compatibility issue**. NestJS still clings to the CommonJS dream, leading to constant configuration errors when I tried to use modern ESM-only libraries. To run a simple TypeScript Worker thread, I had to spend hours researching ESM compilers.
### Step 2: Retreating to Comfort—and the Fear of Missing Out
The most frustrating part was **Swagger integration**. Most people prefer Zod for validation now, but Swagger is deeply coupled with the Class-Validator (Decorator) pattern. To get Swagger to recognize my Zod schemas and generate documentation, I had to manually write adapters and custom decorators.
I went back to my familiar Vue ecosystem and fired up Nuxt. Suddenly, I could breathe. My productivity skyrocketed.
**I felt like I wasn't building a product; I was repairing a broken tractor with incompatible parts.**
But the peace didn't last. Id see another post praising the Next.js ecosystem and start doubting myself: **"Am I choosing the wrong path? Am I falling behind?"** I gave Next.js another shot for a few days, and like clockwork, all those petty, nagging frustrations returned.
## 3. Monorepo: The "Tender Trap" for Indie Developers
Thats when I realized: **Tech isn't about "better" or "worse"; its about fit.** Trust your "handfeel." That sense of flow (or lack thereof) doesn't lie.
To pursue "code reuse," I even set up a **Monorepo**.
### Step 3: The "Professionalism" Trap
I thought: *Front-end and back-end sharing types, enums, and error codes—how elegant!* The reality: trying to get a pure ESM frontend to share a package with a non-pure ESM backend plunged me into a bottomless pit of build configurations. Due to the NestJS environment, I had to compile and export the shared package every time I made a change, making frequent debugging and code modification an absolute nightmare.
Once the frontend was settled, I moved to the backend. I chose **NestJS** because its billed as the most "Enterprise-grade" option. To make it even more "professional," I forced it into a **Monorepo**. I effectively built myself a prison:
Code that should have taken one minute to write took ten because I was busy dealing with cross-package debugging, TS type synchronization, and build logic.
* **Immense Friction:** Changing a single shared enum or type meant re-building, waiting for workspace syncs, and restarting services. A 10-second change turned into a 1-minute wait.
* **Compatibility Hell:** NestJS is still clinging to CommonJS. Trying to use a pure ESM library or running a TS Worker thread resulted in config errors that made me question my career choices.
* **Ceremony Over Substance:** For 30 endpoints, I was writing endless Controllers, Services, Modules, and DTOs. The lines of code tripled, but the core logic stayed exactly the same.
* **The Swagger Tax:** I wanted to use Zod for validation, but Swagger only recognizes decorators (class-validator). I ended up maintaining two nearly identical data definitions just to have an API doc.
**I finally realized: Monorepos are built to solve "organizational collaboration." For an indie developer, they are often a productivity killer.**
**I traded my immediate efficiency for the "advantages of a large team" that I don't actually have.** I wanted speed; the framework demanded "maintainability" for a scale I'll likely never hit.
## 4. Returning to Pragmatism: My "Two-Tier Strategy"
### Step 4: Stripping it Down and Losing My Way
At the end of all this exhaustion, I reflected: Is there a perfect framework? The answer is no; there is only the *suitable* one. Consequently, I have simplified my selection logic into two tiers:
I ditched the heavy frameworks for **Fastify**, thinking Id keep it lean. But I quickly discovered that the problem wasn't just "weight"—it was **"the anxiety of absolute freedom."**
* **Tier A: Rapid Validation (MVP / Personal Projects)**
**Stack: Nuxt All-in-One.** Don't even separate the frontend and backend. Nuxt's built-in Server API (Nitro) is more than enough for small to medium businesses. Types are naturally shared, and there are no CORS or build-sync headaches. At the validation stage, **"Speed" is a hundred times more important than "Elegance."**
Fastify is liberating, but for someone used to structure, freedom is a burden:
* **Tier B: Complex Business (Large Projects / Team Collaboration)**
**Stack: Nuxt + NestJS (Decoupled) + Monorepo.** Only when the business is complex enough to require strict layering, Dependency Injection (DI) for decoupling, and multi-person collaboration will I endure the "ceremony" and management costs of these heavy frameworks.
* "Where should this file go?"
* "How do I organize this into a plugin?"
* Logging, queues, auth—everything NestJS gave me for free now required me to find, integrate, and debug a third-party library.
I hated the rules of heavy frameworks, yet I craved their structure. My mindset was completely torn.
### The Epiphany: Tech is a Tool, Not a Totem
## 5. A Side Note: A New Hope in AdonisJS
One night, working late, I asked myself: **"Why am I doing this? I just wanted to build a simple tool."**
Just as I was summarizing these strategies, I stumbled upon a new framework—**AdonisJS**. Many developers describe it as the "Laravel of Node.js."
Everything clicked. I was applying "Long-term Enterprise Maintenance" standards to a "Solo Rapid Prototype." It was like trying to build an eight-lane highway just to go to the grocery store around the corner.
I took a quick look at its philosophy, and it seems to precisely hit the pain points I mentioned: it supports ESM natively, has a powerful built-in ORM and Auth solution, and doesn't require jumping through hoops with custom adapters just to get automated Swagger documentation.
I set a new, simple rule for myself:
This "Convention over Configuration" full-stack framework seems to balance development efficiency with engineering quality. I plan to use it in my next project and will share my findings once I have more experience.
1. **For Prototypes & Small Tools:** Use **Nuxt (or a monolithic framework) exclusively.** Frontend and backend in one repo, natural type synchronization, no CORS issues, no sync friction. The goal is to *ship*.
2. **For Complex Systems & Large Teams:** *Then* consider **Nuxt + NestJS + Monorepo.** The complexity is a fair trade for the architectural guardrails.
## 6. Conclusion: A Few Words of Advice
### A New Contender: AdonisJS
1. **There is no perfect framework, only the one that fits the moment.** Don't expect any "star" framework to solve all your problems; they all come with a cost.
2. **Do not easily try a tech stack you aren't familiar with during indie development or tight deadlines.** Unless you truly have the time and energy to burn. You think you're learning new tech, but you're actually burning your product's lifespan.
3. **Be wary of "Big Tech Best Practices."** Many tools built to solve pain points in giant corporations (like Monorepos or extreme layering) only create pain points in personal projects.
4. **Familiarity > Modernity.** Even if a framework is called "old school," if it's intuitive to you, lets you finish work early, and helps you write clearer logic with AI assistance, it is your "silver bullet."
During this spiral, I stumbled upon **AdonisJS**. Its often called the "Laravel of Node.js." From a quick look, it seems to hit the sweet spot: it has the structure I crave (ORM and Auth built-in) but feels modern and supports ESM natively. I might give it a spin next time.
**The best tech stack is the one that allows you to forget the technology itself and focus on creating value.**
### Final Thoughts
Finally, the solutions I've summarized are only what fits my personal habits and current understanding; they may not work for everyone. Everyone's business scenarios and technical backgrounds are different. **If you have better ideas or different solutions, I'd love to hear them in the comments so I can learn from you too.** If I've missed anything, please let me know. Thanks in advance!
1. **Dont blind-follow the hype:** What works for Vercel might not work for your weekend project.
2. **Trust your "handfeel":** If a tool makes you feel productive, that's worth more than any "State of JS" ranking.
3. **Ship first, optimize later:** For a solo project, an un-launched architecture is just expensive fan fiction.
4. **Watch out for "Config Friction":** If you spend more time in `.json` and `.config.js` files than in your logic, your tools are failing you.
This was my journey through the weeds. **My approach might be "wrong" or even a bit clumsy.** Id love to hear how you handle the "stack anxiety." **Do you have a go-to setup that just works, or are you still searching for the "perfect" balance?**