As developers, we keep building experiments. A weekend AI project. A Three.js demo. A WebRTC experiment. A side project that eventually becomes a product.
The common instinct is to put everything into one repository under /experiments. That works—until it doesn't.
Here's how I structured my portfolio so that all projects live under a single domain while remaining completely independent deployments.
The Problem: The Monolith Trap
At first glance, a monorepo feels convenient. However, most side projects are unrelated.
When every project lives in a single repository, you face:
- Coupled Deployments: A React Three Fiber experiment has nothing in common with an AI voice application. Shipping one shouldn't require rebuilding the other.
- Bloated Build Times: As the repository grows, CI/CD pipeline durations increase.
- Maintenance Friction: You lose the freedom to easily archive, rewrite, or delete a single experiment without affecting the rest of the site.
Why Independent Deployments Exist
Keeping projects in separate repositories gives you:
- Independent Deployments & Tech Stacks: Project A can be Next.js, Project B can be Vite, and Project C can be vanilla HTML.
- Smaller Build Times: Each project is small and fast to deploy.
- Isolation: Each project can evolve at its own pace.
Architecture: The Mental Model
The goal is to maintain independent deployments while users feel like everything belongs to one cohesive portfolio.
Instead of sending visitors to different domains (e.g. mimic-meet.vercel.app), requests are routed through the main website.
Client → Main Domain (Reverse Proxy) → Independent Deployment
For example:
akshaykalekar.tech/experiment/mimic-meet/
is actually served from:
https://mimic-meet.vercel.app/
Core Concept: Reverse Proxy / URL Masking
The main application proxies requests to another deployment while keeping the browser URL unchanged. The user never leaves the domain.
This approach is chosen because the projects are independent and don't need to share runtime state.
Trade-offs: Decision Weapons
Senior engineering is about choosing the right trade-offs.
The "Yes" (Pros)
- True Isolation: Complete freedom to nuke or rebuild individual projects.
- Optimized Performance: Smaller, faster builds for independent apps.
- Unified Brand Identity: Users stay on your primary domain.
The "No" (Cons)
- Asset Path Complexity: You must carefully handle relative paths.
- Routing Overhead: Requires setting up proxy rules (e.g.,
rewritesin Next.js). - No Shared State: Projects cannot easily share runtime state.
The Biggest Challenge: Asset Paths and Trailing Slashes
The architecture itself is straightforward. The surprising challenge is asset paths.
Since the application is no longer hosted at the root of a domain, some assets fail to load correctly. Instead of requesting /assets/index.js, the application needs to resolve paths relative to its deployed location.
The solution? Trailing slashes.
You might notice that the project URL consistently ends with a slash:
/experiment/mimic-meet/
instead of
/experiment/mimic-meet
Initially, trying to support both versions introduces unnecessary redirect logic, edge cases, and additional routing complexity.
Now, every project consistently uses a trailing slash. It's one small URL convention that eliminates an entire class of routing problems. Sometimes the simplest solution is accepting a tiny URL difference instead of writing more code to hide it.
Alternatives Considered
- Multi-Zones (Next.js): Multi-Zones let multiple Next.js applications appear as one larger application. It's a great option when several Next.js apps need to coexist under one domain.
- Micro-Frontends: Useful when multiple teams work on different parts of the same application and want independent deployments while composing a single UI. Unnecessary complexity for standalone experiments.
Scenarios: When to Use / Avoid
MUST: Use when...
- Portfolio Sites: Showcasing disparate side projects.
- Polyglot Architectures: Mixing Next.js, Vite, and plain HTML applications.
- Independent Lifecycles: Projects that have different maintenance schedules.
FORGET: Avoid when...
- Tightly Coupled UIs: If applications need to share a global state.
- Single Framework Apps: If everything is tightly integrated naturally.
Interactive Experiment: Try Mimic Meet
Experience a standalone project served seamlessly through the main domain via a reverse proxy.