General

  • One File, One Purpose
  • No Swiss Army components, util files or classes
  • Keeps things nice and simple and, more importantly Easy to Test!
  • If your file > say, 500 lines - Red Flag 🚩

Frontend

A web app really only has two kinds of components:

  • Common – reused, well-traveled, and politely introduced everywhere.
  • Single Use – page-specific, introverted, and perfectly happy never leaving their room.

Keeping these two groups separate makes your frontend calmer, clearer, and much easier to reason about.

Shared components live together in a shared or common folder and are exported intentionally.
Page-specific components live inside their page folder, stay private, and never get exported.

Think of it like public vs private in a class:

  • Public APIs should be deliberate and stable
  • Private details should stay cozy and out of sight
  • Nobody should be importing a page-only component from three directories away

Your future self will appreciate the boundaries.


Shared Components

Shared components are:

  • Reused across multiple pages
  • Generic enough to earn their keep
  • Exported from a central folder

They form the design vocabulary of your app.

Page Components

Page components are:

  • Tightly coupled to one page
  • Free to change without breaking the world
  • Not exported outside their folder

If a page component wants to escape and be reused, that’s its audition for becoming shared.


Barrel Files: Clean Imports, No Spaghetti

Barrel files (index.ts) keep imports:

  • Short
  • Intentional
  • Free from relative-path gymnastics

No spaghetti imports. Just linguine.

Tests should live right next to the file they validate—like a loyal sidekick who never gets lost wandering a separate tests/ kingdom.


Frontend Folder Structure

src/
  app/
    philosophy/
      Philosophy.page.tsx
      Philosophy.page.test.tsx
      components/
        PhilosophyHero.tsx
        PhilosophyHero.test.tsx
        PhilosophyGrid.tsx
        PhilosophyGrid.test.tsx
        index.ts
  common/
    Button/
      Button.tsx
      Button.scss
      Button.test.tsx
      index.ts
    Card/
      Card.tsx
      Card.scss
      Card.test.tsx
      index.ts
    index.ts

Backend: One Endpoint, One Foler

In an ExpressJS app, each endpoint deserves its own folder.

That means:

  • One folder per logical endpoint
  • One file per HTTP method
  • One clear responsibility per file

Each endpoint exports an Express router, which gets composed together in server.ts like a tidy neighborhood association. No cul-de-sacs. No mystery routes.

Tests live beside their handlers so the evidence is always in the room with the crime.


Backend Rules of the Road

  • Routes are public
  • Helpers stay private
  • No wandering endpoints after curfew
  • If a file grows too large, it’s trying to do too much

Predictable structure beats clever abstractions every time.


Backend Folder Structure

src/
  server.ts
  server.test.ts
  users/
    index.ts
    index.test.ts
    user.get.ts
    user.get.test.ts
    user.post.ts
    user.post.test.ts
  posts/
    index.ts
    index.test.ts
    post.get.ts
    post.get.test.ts
    post.post.ts
    post.post.test.ts

General: TypeScript Path Aliases Are Tiny Teleporters

TypeScript path aliases turn long, brittle relative imports into calm, readable statements of intent.

Give each top-level folder its own shortcut and watch imports go from long-winded novel to snappy haiku.

This has the added bonus of making them much easier to move around.

Example tsconfig.json

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@app/*": ["src/app/*"],
      "@common/*": ["src/common/*"],
      "@data/*": ["src/data/*"]
    }
  }
}

Used consistently, aliases:

  • Reduce refactor pain
  • Clarify ownership boundaries
  • Make code easier to scan

Structure isn’t bureaucracy—it’s kindness to the next person who reads your code.

Back to Philosophy