One codebase tracked in revision control, many deploys
// ❌ Bad: Multiple repos for same logical app
conference-web/
conference-api/
conference-workers/
// ✅ Good: Single repo, different entry points
conference-marketplace/
├── src/
│ ├── web/
│ │ └── server.js
│ ├── api/
│ │ └── server.js
│ └── workers/
│ └── page-generator.js
├── shared/
│ ├── config.js
│ └── utils.js
└── package.jsonDifferent deployments from same codebase:
{
"scripts": {
"start:web": "node src/web/server.js",
"start:api": "node src/api/server.js",
"start:worker": "node src/workers/page-generator.js"
}
}Explicitly declare and isolate dependencies
// package.json with exact engines
{
"type": "module",
"engines": {
"node": ">=20.0.0"
},
"dependencies": {
"express": "^4.18.2",
"helmet": "^7.0.0"
}
}// src/config.js - Clean ESM imports
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
const packageJson = JSON.parse(
readFileSync(join(process.cwd(), 'package.json'), 'utf8')
);
export const appVersion = packageJson.version;Store config in the environment
// src/config.js
import { z } from 'zod';
const configSchema = z.object({
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
PORT: z.coerce.number().default(3000),
DATABASE_URL: z.string().url(),
REDIS_URL: z.string().url(),
STRIPE_SECRET_KEY: z.string().min(1),
});
export const config = configSchema.parse(process.env);// src/web/server.js
import { config } from '../config.js';
import express from 'express';
const app = express();
app.listen(config.PORT, () => {
console.log(`Server running on port ${config.PORT}`);
});Treat backing services as attached resources
// src/services/cache.js
import Redis from 'ioredis';
export class CacheService {
#client;
constructor(url) {
this.#client = new Redis(url);
}
async get(key) {
const value = await this.#client.get(key);
return value ? JSON.parse(value) : null;
}
async set(key, value, ttl = 3600) {
await this.#client.setex(key, ttl, JSON.stringify(value));
}
async close() {
await this.#client.disconnect();
}
}// src/services/database.js
import pg from 'pg';
export class DatabaseService {
#pool;
constructor(connectionString) {
this.#pool = new pg.Pool({ connectionString });
}
async query(text, params) {
const client = await this.#pool.connect();
try {
const result = await client.query(text, params);
return result.rows;
} finally {
client.release();
}
}
async close() {
await this.#pool.end();
}
}Service factory pattern for your conference marketplace:
// src/services/index.js
import { config } from '../config.js';
import { CacheService } from './cache.js';
import { DatabaseService } from './database.js';
export const createServices = () => ({
cache: new CacheService(config.REDIS_URL),
database: new DatabaseService(config.DATABASE_URL),
});Strictly separate build and run stages
// package.json
{
"type": "module",
"scripts": {
"build": "parcel build src/web/index.html --dist-dir dist/web",
"build:api": "node scripts/build-api.js",
"start": "node dist/server.js",
"dev": "node --watch src/web/server.js"
}
}// scripts/build-api.js
import { build } from 'esbuild';
import { readdir } from 'node:fs/promises';
const entryPoints = await readdir('src/api', { withFileTypes: true });
const apiFiles = entryPoints
.filter(dirent => dirent.isFile() && dirent.name.endsWith('.js'))
.map(dirent => `src/api/${dirent.name}`);
await build({
entryPoints: apiFiles,
bundle: true,
platform: 'node',
target: 'node20',
format: 'esm',
outdir: 'dist/api',
external: ['pg', 'ioredis'], // Keep external deps
});Execute as stateless processes
// ❌ Bad: In-memory state
const userSessions = new Map();
export const loginHandler = (req, res) => {
const sessionId = crypto.randomUUID();
userSessions.set(sessionId, { userId: req.body.userId });
res.json({ sessionId });
};
// ✅ Good: External state storage
export const createLoginHandler = (cache) => async (req, res) => {
const sessionId = crypto.randomUUID();
await cache.set(`session:${sessionId}`, { userId: req.body.userId });
res.json({ sessionId });
};// src/web/server.js
import express from 'express';
import { createServices } from '../services/index.js';
import { createLoginHandler } from '../handlers/auth.js';
const app = express();
const services = createServices();
app.post('/login', createLoginHandler(services.cache));
// Graceful shutdown
process.on('SIGTERM', async () => {
await services.cache.close();
await services.database.close();
process.exit(0);
});Export services via port binding
// src/web/server.js
import express from 'express';
import { config } from '../config.js';
const app = express();
// Self-contained HTTP service
const server = app.listen(config.PORT, () => {
console.log(`Conference marketplace on port ${config.PORT}`);
});
// Export for testing
export { app, server };// src/api/server.js - Different service, different port
import express from 'express';
import { config } from '../config.js';
const app = express();
const apiPort = config.API_PORT || 3001;
app.listen(apiPort, () => {
console.log(`API server on port ${apiPort}`);
});Scale via process model
// src/web/server.js - Web process
import express from 'express';
import { createServices } from '../services/index.js';
const app = express();
const { cache, database } = createServices();
app.get('/conference/:slug', async (req, res) => {
const conference = await database.query(
'SELECT * FROM conferences WHERE slug = $1',
[req.params.slug]
);
res.json(conference[0]);
});// src/workers/page-generator.js - Worker process
import { Queue, Worker } from 'bullmq';
import { createServices } from '../services/index.js';
import { config } from '../config.js';
const { cache, database } = createServices();
const pageQueue = new Queue('page-generation', {
connection: { host: config.REDIS_URL }
});
const worker = new Worker('page-generation', async (job) => {
const { conferenceSlug } = job.data;
const conference = await database.query(
'SELECT * FROM conferences WHERE slug = $1',
[conferenceSlug]
);
// Generate and cache page
const page = generateConferencePage(conference[0]);
await cache.set(`page:${conferenceSlug}`, page);
}, {
connection: { host: config.REDIS_URL }
});
export { pageQueue, worker };Fast startup, graceful shutdown
// src/web/server.js
import express from 'express';
import { createServices } from '../services/index.js';
const app = express();
let services;
// Lazy service initialization for fast startup
const getServices = () => {
if (!services) {
services = createServices();
}
return services;
};
app.get('/conference/:slug', async (req, res) => {
const { database } = getServices();
const conference = await database.query(/* ... */);
res.json(conference[0]);
});
const server = app.listen(config.PORT);
// Graceful shutdown
const shutdown = async (signal) => {
console.log(`Received ${signal}, shutting down gracefully`);
server.close(() => {
console.log('HTTP server closed');
});
if (services) {
await services.cache.close();
await services.database.close();
}
process.exit(0);
};
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));Keep environments similar
// src/config.js - Same config shape everywhere
export const config = {
database: {
url: process.env.DATABASE_URL,
ssl: process.env.NODE_ENV === 'production',
pool: {
min: process.env.DB_POOL_MIN || 2,
max: process.env.DB_POOL_MAX || 10,
}
},
redis: {
url: process.env.REDIS_URL,
retryDelayOnFailover: 100,
}
};# Same container for all environments
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "src/web/server.js"]Treat logs as event streams
// src/lib/logger.js
import { createLogger, format, transports } from 'winston';
export const logger = createLogger({
level: process.env.LOG_LEVEL || 'info',
format: format.combine(
format.timestamp(),
format.json()
),
transports: [
new transports.Console()
]
});// src/web/server.js
import { logger } from '../lib/logger.js';
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
logger.info({
method: req.method,
url: req.url,
status: res.statusCode,
duration: Date.now() - start,
userAgent: req.get('User-Agent')
});
});
next();
});One-off admin tasks
// scripts/migrate.js
import { createServices } from '../src/services/index.js';
const { database } = createServices();
const migrations = [
'CREATE TABLE IF NOT EXISTS conferences (id SERIAL PRIMARY KEY, slug TEXT UNIQUE)',
'CREATE INDEX IF NOT EXISTS idx_conferences_slug ON conferences(slug)',
];
for (const migration of migrations) {
await database.query(migration);
console.log(`Executed: ${migration}`);
}
await database.close();
process.exit(0);// scripts/seed-conferences.js
import { readFile } from 'node:fs/promises';
import { createServices } from '../src/services/index.js';
const { database } = createServices();
const seedData = JSON.parse(
await readFile('data/conferences.json', 'utf8')
);
for (const conference of seedData) {
await database.query(
'INSERT INTO conferences (slug, name, date) VALUES ($1, $2, $3) ON CONFLICT (slug) DO NOTHING',
[conference.slug, conference.name, conference.date]
);
}
console.log(`Seeded ${seedData.length} conferences`);
await database.close();{
"scripts": {
"db:migrate": "node scripts/migrate.js",
"db:seed": "node scripts/seed-conferences.js",
"cache:clear": "node scripts/clear-cache.js"
}
}// src/web/server.js
import express from 'express';
import { config } from '../config.js';
import { createServices } from '../services/index.js';
import { logger } from '../lib/logger.js';
const app = express();
const services = createServices();
app.get('/conference/:slug', async (req, res) => {
try {
// Try cache first
const cached = await services.cache.get(`page:${req.params.slug}`);
if (cached) {
logger.info({ event: 'cache_hit', slug: req.params.slug });
return res.send(cached);
}
// Generate fresh page
const conference = await services.database.query(
'SELECT * FROM conferences WHERE slug = $1',
[req.params.slug]
);
if (!conference.length) {
return res.status(404).send('Conference not found');
}
const page = generateConferencePage(conference[0]);
await services.cache.set(`page:${req.params.slug}`, page, 3600);
logger.info({
event: 'page_generated',
slug: req.params.slug,
seo_ready: true
});
res.send(page);
} catch (error) {
logger.error({
event: 'page_error',
slug: req.params.slug,
error: error.message
});
res.status(500).send('Internal error');
}
});
const server = app.listen(config.PORT, () => {
logger.info({ event: 'server_started', port: config.PORT });
});
// Graceful shutdown
process.on('SIGTERM', async () => {
server.close();
await services.cache.close();
await services.database.close();
logger.info({ event: 'server_shutdown' });
});