The Problem: Tool Sprawl
When Neorgon hit 6 tools, we had a problem. Each tool lived in its own git repo, with its own deployment pipeline, its own set of dependencies, and its own way of doing things.
Adding a new tool meant:
- Copy-pasting a previous tool
- Hunting for the latest patterns
- Updating 3 different READMEs
- Praying you didn't forget the OG image generation
It took half a day of tedious work. We needed a better way.
๐ก The Requirements
- โ Zero shared dependencies between tools
- โ Independent deploys (no lockstep releases)
- โ Consistent patterns and conventions
- โ 2-hour new tool scaffolding time
- โ Easy to extract tools later if needed
Solution: The Monorepo That Isn't
Most monorepos use shared dependencies and orchestrated builds. We did the opposite: independent everything, centralized decisions.
Directory Structure
Personal/
โโโ _template/ # Source of truth for patterns
โ โโโ index.html # Single-file template
โ โโโ app/ # Modular app template
โโโ neorgon-site/ # The hub (uses _template)
โโโ json-builder-site/ # Independent tool
โโโ presentation-sage/ # Independent tool
โโโ ...31 tools total
Key insight: Each tool is completely self-contained. They don't import from each other. They don't share node_modules. They're independent apps that happen to live in the same Git repo.
The Template System
Everything flows from _template/. It's not a library-it's a living pattern:
// Every tool follows the same structure:
passport-site/
โโโ index.html # Main page (80-150 lines)
โโโ css/
โ โโโ style.css # All styles in one file
โโโ js/ # Modular ES modules (if needed)
โโโ app.js # Entry point (<50 lines)
โโโ state.js # Shared state
โโโ render.js # DOM rendering
โโโ events.js # Event handlers
โโโ utils.js # Helpers
Single-File vs Modular
We have two templates:
- Single-file (
_template/index.html) - For simple pages under 1,000 lines - Modular (
_template/app/) - For interactive apps over 1,000 lines
No middle ground. Every tool uses one or the other. This eliminates decision fatigue.
The 2-Hour New Tool Formula
Here's exactly how we scaffold a new tool:
โฑ๏ธ Step 1: Copy Template (15 minutes)
cp -r _template/app new-tool-site
cd new-tool-site
๐จ Step 2: Customize (45 minutes)
- Update
index.htmltitle/meta - Add tool-specific CSS variables
- Implement core functionality in
js/ - Test locally with
python3 -m http.server
๐ธ Step 3: Generate Assets (20 minutes)
- Create OG image in OG Studio
- Generate favicon.ico
- Make tool icon in design tool
๐ฏ Step 4: Hub Integration (40 minutes)
- Add card to
neorgon-site/index.html - Update category in
search.js - Add preview to
previews.js - Test search/filter integration
Key Patterns That Make It Work
1. No Build Step
Every tool runs directly from source:
# No webpack, no npm install, no compilation
python3 -m http.server 8800
open http://localhost:8800/new-tool/
This means:
- โ Deploy by pushing to GitHub Pages
- โ Debug in browser DevTools (source = actual code)
- โ No build failures
- โ Instant preview on save
2. Zero Shared Dependencies
Contrast this with typical monorepos:
{
"dependencies": {
"react": "^18.0.0", // All tools locked to same version
"lodash": "^4.17.21" // Shared across everything
}
}
# Each tool includes libraries directly
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
# Or vendor them
js/vendor/sortable.min.js
Trade-off: 50KB duplicated across sites vs. 2 hours of dependency hell when upgrading React.
We chose duplication.
3. Consistent State Management
Every tool uses the same pattern:
// Mutable shared state object
export const state = {
currentTool: null,
settings: {
theme: 'dark',
sound: true
}
};
// No Redux, no context providers
// Just import and mutate (with caution)
It's not pure. It's not functional. But it means every tool's state works the same way, and you can debug it by inspecting window._state.
4. Standardized Exports
Every tool exposes the same helpers:
// Available in every tool's console
window.exportData() // Download current state
window.importData(json) // Load previous state
window.reset() // Reset to defaults
window._state // Inspect current state
// For hub integration
window._toolReady = true // Signal loaded
What We Avoided
โ Lerna/NX
We don't need orchestrated builds. Each tool deploys independently.
โ Shared Components
No heavy shared component library. We keep a tiny @neorgon/ui package for cross-site footers only. Copy-paste is still simpler than versioning UI logic.
โ Microservices
Each tool is a static site. No servers, no Docker, no Kubernetes.
Escape Hatches
Every architectural decision includes an escape route:
๐ช Extracting a Tool
If a tool needs to stand alone:
cd new-tool-site
git init
git remote add origin https://github.com/new/tool
git push -u origin main
No refactoring needed. It's already self-contained.
๐ Adding Shared Logic
If 3+ tools need the same function:
// Copy to each tool first
// If it stabilizes, create js/shared/ in the tool
git mv js/shared-utils.js js/shared/utils.js
// Import from relative path
Only extract when it hurts.
Real Numbers
After 31 tools, here's what the monorepo looks like:
- ๐ 31 tool directories
- ๐ 1,200 total files
- ๐พ 4.2MB total (including assets)
- โก Average tool size: 230KB
- ๐ New tool time: 1.8 hours average
- ๐ Shared bugs: ~5% (mostly CSP issues)
The Template Checklist
Before a tool graduates from template to production, it must:
- โ
Work with
python3 -m http.server(no build) - โ Pass Lighthouse audit (95+ performance)
- โ
Support
prefers-reduced-motion - โ Load in 2 seconds on 3G
- โ Have OG image, favicon, and meta tags
- โ
Include console helpers (
exportData,reset) - โ Hub card added with correct category
Lessons Learned
1. Start Simple, Stay Simple
We almost used Turborepo. Glad we didn't. We don't need caching or pipelines-cp -r is faster.
2. Duplication is Sometimes Better
Those 50KB of duplicated libraries? Worth 100 hours of dependency management.
3. Documentation is the Real Dependency
The template isn't the source of truth-CLAUDE.md is. It explains why we made each choice.
4. Extract Last, Not First
We waited until we had 12 tools before creating _template/. The pattern emerged from repetition.
When to Use This Architecture
โ Perfect Fit For:
- Many small, unrelated projects
- Rapid prototyping
- Learning/experimentation repos
- Independent deployment requirements
โ Wrong Fit For:
- Large, interconnected applications
- Shared business logic
- Coordinated releases
- Enterprise teams