ArchitectureTechnicalBest Practices

Understanding Shipd's Modular Architecture

Deep dive into how Shipd's modular architecture works and why it's perfect for building scalable SaaS applications.

Shipd Team
5 min read

Understanding Shipd's Modular Architecture

One of Shipd's core strengths is its modular architecture. But what does that mean, and why should you care?

What is Modular Architecture?

Modular architecture means your application is built from independent, self-contained modules that can be added or removed without affecting the rest of your codebase.

Traditional Approach

┌─────────────────────────────────┐
│   Monolithic SaaS Template      │
│                                 │
│  ✓ Auth    ✓ Payments          │
│  ✓ Blog    ✓ Analytics         │
│  ✓ Docs    ✓ Support           │
│  ✓ Email   ✓ Everything else   │
│                                 │
│  (You get it all, whether       │
│   you need it or not)           │
└─────────────────────────────────┘

Shipd's Modular Approach

┌──────────┐
│   Base   │  ← Minimal foundation
└────┬─────┘
     │
     ├─── [Auth Module]
     ├─── [Payments Module]
     ├─── [Blog Module]      ← Add only what you need
     ├─── [Analytics Module]
     └─── [Docs Module]

Benefits of Modular Architecture

1. Start Small, Scale Smart

Begin with just the essentials:

npx shipd init my-app --modules auth,payments

Add features as you grow:

npx shipd append --features blog,analytics

2. Cleaner Codebase

Only include code you actually use. No bloat, no unnecessary dependencies, no confusing features you'll never touch.

3. Easier Maintenance

Each module is self-contained with:

  • Its own documentation
  • Isolated dependencies
  • Clear interfaces
  • Independent testing

4. Faster Development

Skip features you don't need:

  • Building a B2B tool? Skip the blog
  • Internal tool? Skip marketing pages
  • MVP? Start minimal, add later

How Modules Work

Each Shipd module is a complete, production-ready feature package.

Module Structure

features/
└── blog/
    ├── app/
    │   └── blog/
    │       ├── page.tsx          # Blog listing
    │       └── [slug]/
    │           └── page.tsx      # Blog post
    ├── lib/
    │   └── blog.ts              # Blog utilities
    ├── components/
    │   └── blog/                # Blog components
    ├── content/
    │   └── blog/                # MDX content
    ├── package.json             # Module dependencies
    └── README.md                # Module docs

Module Dependencies

Modules can depend on other modules:

// blog module config
{
  "name": "blog",
  "dependencies": [],
  "optionalDependencies": ["analytics"],
  "conflicts": []
}

Module Injection

Modules integrate seamlessly using injection points:

// app/layout.tsx
import type { Metadata } from "next";
import "./globals.css";
/*APPEND_IMPORTS*/  // ← Modules inject imports here

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        {/*APPEND_PROVIDERS*/}  // ← Modules inject providers
          {children}
        {/*APPEND_PROVIDERS_END*/}
        {/*APPEND_COMPONENTS*/}  // ← Modules inject components
      </body>
    </html>
  );
}

When you add the analytics module:

// Automatically becomes:
import { Analytics } from "@/lib/analytics";
/*APPEND_IMPORTS*/

// ...
  {/*APPEND_COMPONENTS*/}
  <Analytics />

Available Modules

Core Modules

ModuleDescriptionDependencies
authUser authentication with Better Authdatabase
databasePostgreSQL with Drizzle ORM-
paymentsPolar.sh integrationauth, database
emailTransactional emails with Resend-

Feature Modules

ModuleDescriptionDependencies
blogMDX-powered blog-
docsDocumentation site-
analyticsPostHog analytics-
dashboardAdmin dashboardauth

Creating Custom Modules

You can create your own modules:

# Create a custom module
npx shipd create-module my-feature

# This generates:
features/
└── my-feature/
    ├── app/           # Pages
    ├── lib/           # Utilities
    ├── components/    # Components
    ├── config.json    # Module config
    └── README.md      # Documentation

Module Configuration

{
  "name": "my-feature",
  "version": "1.0.0",
  "description": "My custom feature",
  "dependencies": ["auth"],
  "files": {
    "app/my-feature": "app/my-feature",
    "lib/my-feature.ts": "lib/my-feature.ts"
  },
  "injections": {
    "app/layout.tsx": {
      "/*APPEND_IMPORTS*/": "import { MyFeature } from '@/lib/my-feature';"
    }
  },
  "envVars": [
    "MY_FEATURE_API_KEY"
  ]
}

Best Practices

1. Start Minimal

Begin with the absolute minimum:

npx shipd init --modules auth,database,payments

2. Add Features Incrementally

Don't add modules "just in case":

# Wait until you actually need it
npx shipd append --features blog

3. Keep Modules Independent

Design your custom modules to be self-contained and reusable.

4. Document Integration Points

If your module needs manual setup, document it clearly:

## Manual Integration

After installing, update your navigation:

\`\`\`tsx
<Nav>
  <NavLink href="/my-feature">My Feature</NavLink>
</Nav>
\`\`\`

Comparison with Other Approaches

vs. Monolithic Templates

Shipd ModularMonolithic Template
Add what you needGet everything
Clean codebaseLots of unused code
Easy to maintainComplicated
Fast buildsSlow builds

vs. Building from Scratch

Shipd ModularFrom Scratch
Minutes to set upWeeks to set up
Production-readyNeed to harden
Best practicesYour implementation
MaintainedUnmaintained

Future Modules

We're constantly adding new modules:

  • Waitlist - Collect emails pre-launch
  • Referrals - Built-in referral system
  • A/B Testing - Experiment framework
  • Customer Support - Help desk integration
  • Stripe - Alternative payment provider

Conclusion

Modular architecture gives you the best of both worlds:

Fast setup like a template ✅ Flexibility like building from scratch ✅ Clean code without bloat ✅ Easy maintenance with isolated modules

Start with what you need, add features as you grow, and keep your codebase clean.


Ready to experience modular architecture? Run npx shipd init and build your SaaS the smart way.