Shared Code, Libraries, and Reuse

Describe safe ways to share validation, utilities, clients, and domain logic without creating heavy shared-dependency problems or deployment bottlenecks.

Reuse in serverless systems is valuable, but it is easy to overdo. Teams often start with the right goal: avoid copying validation logic, configuration loaders, client setup, or domain rules across many handlers. Then they overcorrect and build a large shared library that every function depends on, even when the functions should remain independently changeable. The result is tighter coupling hidden behind the language of reuse.

The strongest reuse is selective. Share code that is genuinely common and stable. Do not centralize behavior just because duplication feels untidy.

What Is Usually Safe to Share

The safest shared code usually includes:

  • schema validation helpers
  • typed client wrappers for common managed services
  • authentication or authorization utility layers
  • logging and tracing helpers
  • small domain utilities that are stable across several handlers

These are cross-cutting concerns that benefit from consistency.

What Should Be Shared More Carefully

Be more cautious with:

  • business workflows
  • large orchestration helpers
  • broad “platform SDKs” that try to wrap every service
  • domain logic that changes at different speeds for different handlers

The problem is not sharing itself. The problem is forcing several functions to move together when they should be allowed to evolve independently.

    flowchart TD
	    A["Candidate shared code"] --> B{"Stable and cross-cutting?"}
	    B -->|Yes| C["Good shared utility"]
	    B -->|No| D{"Business-specific and change-heavy?"}
	    D -->|Yes| E["Keep closer to owning function or domain module"]
	    D -->|No| F["Review carefully before centralizing"]

What to notice:

  • stability matters as much as duplication
  • business-specific shared modules often become hidden coupling points
  • the safest reuse usually sits below the business workflow layer

A Good Reuse Shape

This is a healthy pattern for a small serverless codebase:

 1src/
 2  functions/
 3    create-order.ts
 4    reserve-inventory.ts
 5    send-email.ts
 6  shared/
 7    config.ts
 8    logger.ts
 9    auth.ts
10    validation/
11      order-schema.ts
12  domain/
13    orders/
14      normalize-order.ts

This structure works because:

  • shared code is narrow and stable
  • domain code still lives close to the domain
  • each function keeps its own execution boundary clear

Example: Shared Validation and Logging

 1// shared/validation/order-schema.ts
 2export function assertCreateOrder(input: unknown): asserts input is CreateOrderInput {
 3  if (!input || typeof input !== "object") {
 4    throw new Error("invalid-order-request");
 5  }
 6}
 7
 8// shared/logger.ts
 9export function logInfo(message: string, fields: Record<string, unknown>) {
10  console.log(JSON.stringify({ level: "info", message, ...fields }));
11}
1// functions/create-order.ts
2import { assertCreateOrder } from "../shared/validation/order-schema";
3import { logInfo } from "../shared/logger";
4
5export async function handle(event: unknown) {
6  assertCreateOrder(event);
7  logInfo("creating-order", { customerId: event.customerId });
8  // business-specific order logic here
9}

What this demonstrates:

  • validation and logging are shared because they are stable and cross-cutting
  • the business action still stays local to the handler or owning domain module

Signs Reuse Has Gone Too Far

Warning signs include:

  • every function imports a huge internal framework package
  • a shared helper change triggers unrelated redeployments or regression risk
  • domain ownership becomes unclear because “the shared library team owns it”
  • handlers depend on common abstractions that are more complex than the raw services they wrap

This is especially common when a team creates a heavy internal platform layer too early.

Reuse and Deployment Independence

In serverless systems, reuse should not silently destroy deployment independence. If one shared package changes frequently and every function depends on it, then all those functions are more coupled than they appear in the infrastructure diagram. This is not always wrong, but it should be explicit.

The strongest pattern is usually:

  • small shared utilities for consistency
  • business logic owned close to the relevant function or domain
  • explicit boundaries around shared internal packages that truly deserve to exist

Design Review Question

A team created one internal platform-core package that wraps logging, auth, validation, event publishing, database access, workflow execution, notifications, and several business helpers. Almost every function depends on it. Is that healthy reuse?

Usually not. The stronger answer is that the package has probably become a hidden coupling layer. Some of those concerns may belong in shared utilities, but combining them all into one heavy dependency makes change harder, blurs ownership, and reduces the independence that serverless boundaries were supposed to provide.

Check Your Understanding

Loading quiz…
Revised on Thursday, April 23, 2026