The Function-as-a-Monolith Anti-Pattern

Explain oversized functions with too many responsibilities, large dependency graphs, and tangled business logic. Show why this recreates monolith pain inside serverless.

The function-as-a-monolith anti-pattern happens when one serverless handler accumulates too many responsibilities, too many triggers, and too many dependencies. It may start as a shortcut: one function for orders, one function for account management, one function for “all billing work.” Over time, that handler grows into a mini-application hidden inside a single deployment unit.

This recreates monolith pain inside a serverless shape. The code becomes hard to reason about, cold starts get worse, permissions grow broad, and every small change carries risk because unrelated paths are bundled together. The result is not really serverless simplicity. It is a monolith wearing a function name.

    flowchart LR
	    A["API request"] --> B["Large all-purpose function"]
	    C["Queue event"] --> B
	    D["Storage event"] --> B
	    B --> E["Database"]
	    B --> F["Billing API"]
	    B --> G["Email service"]
	    B --> H["Object storage"]

What to notice:

  • one function now handles several unrelated entry points
  • the dependency graph grows because the handler tries to do everything
  • changes in one path increase risk for the others

Why Teams Fall Into It

This anti-pattern usually appears because:

  • it feels faster to add one more branch than to create a new bounded function
  • teams want fewer deployable units
  • the domain boundaries were never made explicit
  • one team owns everything and postpones separation indefinitely

That logic is understandable in the short term. The problem is that the cost compounds invisibly until the function becomes the hardest thing in the system to change safely.

How It Shows Up

The warning signs are usually obvious in hindsight:

  • large handler files with many conditional branches
  • several event shapes entering the same code path
  • broad IAM or service permissions
  • large dependency bundles
  • long startup time because every library is loaded for every path
 1export async function handler(event: any) {
 2  if (event.httpPath?.startsWith("/orders")) {
 3    return handleOrders(event);
 4  }
 5
 6  if (event.queueName === "billing-jobs") {
 7    return processBilling(event);
 8  }
 9
10  if (event.objectKey?.startsWith("uploads/")) {
11    return processUpload(event);
12  }
13
14  if (event.type === "send-email") {
15    return sendNotification(event);
16  }
17
18  throw new Error("Unsupported event");
19}

What this demonstrates:

  • the function boundary is defined by convenience, not responsibility
  • unrelated concerns now share one runtime, one dependency graph, and often one identity
  • testability and rollout safety degrade because everything changes together

What a Healthier Shape Looks Like

A better design does not split functions mechanically into tiny pieces. It separates them by real responsibility and permission shape. That often means:

  • one function per meaningful trigger or use case
  • shared business logic in libraries where appropriate
  • separate identities for separate responsibilities
  • smaller bundles with clearer cold-start behavior

The goal is not maximum fragmentation. It is bounded ownership.

Common Mistakes

  • equating “fewer functions” with “simpler architecture”
  • sharing one broad identity because the handler already does many things
  • ignoring startup and permission costs while adding one more branch
  • postponing separation until the code is already hard to split

Design Review Question

A team has one function that handles order creation, payment callbacks, export jobs, and customer notifications because “it all touches orders anyway.” Deployments now feel risky and cold starts are getting worse. What should the design review challenge first?

The stronger answer is the function boundary. The issue is not only code size. It is that unrelated triggers, permissions, and dependencies are being deployed and authorized together. The review should identify distinct responsibilities and separate them into bounded handlers with narrower identity and dependency scope.

Check Your Understanding

Loading quiz…
Revised on Thursday, April 23, 2026