An API and a report generator can share a repository without sharing a runtime. Put them in one process and a large report can compete with an ordinary request for memory and database connections. Split them and the team gains two deployments, two sets of configuration, and a queue to operate.

A deployable is an artifact with its own release, runtime configuration, resource budget, and failure surface. Splitting one codebase into ten such artifacts can improve isolation and independent scaling. It also creates ten sets of build, dependency, secret, identity, health, and rollout work. Keeping everything in one artifact reduces that operational surface but can couple unrelated workloads and releases.

That trade-off is where deployable design starts.

Start With Boundaries Inside the Code

Take a product with an API, search indexing, email delivery, document processing, and the report generator. It can ship in several shapes:

ShapeWhat it buysWhat it costs
One modular applicationSimple release path, easy local changes, direct callsShared process resources and deployment cadence
API plus one background workerShort requests and separate work capacityQueue, job state, and worker operations
Several specialized servicesIndependent scaling, dependencies, and failure domainsMore contracts, deployments, identities, and coordination

Even when everything ships together, those capabilities should have clear internal interfaces. A module boundary lets a team understand ownership and change impact without paying for a network boundary. If the boundary later needs its own process, the separation has already done useful work.

Ask What Must Vary Independently

The strongest reason to split is usually a need for a different operating envelope. A report generator might need high memory and long execution, while an API needs low latency and short requests. A bursty image processor might scale by queued jobs; an authentication path might need cautious deployment and strict availability. Combining them in one process means one set of runtime limits must serve contradictory needs.

The proposed split has to answer practical questions:

  • Does it need a different scale, CPU or memory shape, concurrency limit, or timeout?
  • Does it require a distinct trust or secret boundary?
  • Can it fail without taking an unrelated user path down?
  • Can its team release and roll back it independently in practice?
  • Does the new network contract have a stable identity and versioning story?

If the answers are mostly no, a new service may be naming a module with infrastructure.

Blast Radius Has More Than One Direction

One large deployable has a wide blast radius when a bad release or memory leak affects all its capabilities. Many small deployables introduce network hops, credentials, retries, and queues that can fail independently. The split trades one kind of blast radius for another.

A separate worker keeps a slow report out of API request threads. If both use the same database connection limit, the report can still starve the API. A search service can deploy independently while making every search request depend on its availability. Resource budgets and fallback behaviour decide how useful the boxes on the diagram really are.

Count Maintenance, Not Only Compute

It is tempting to justify consolidation with a cloud bill. Sometimes that is right: duplicate minimum instances or poorly configured autoscaling can cost real money. But small services may spend very little on compute. Their larger cost can be the repeated lockfiles, images, CI jobs, service identities, secrets, alerts, and deployment rules required to maintain them.

That changes the business case. A merge may simplify upgrades and reduce operational mistakes without making a dramatic dent in runtime spend. Conversely, a costly service should be measured and tuned on its own; merging unrelated low-cost services into it may make cost control harder.

A consolidation proposal needs four figures beside it: steady runtime cost, peak capacity, maintenance touch points, and user paths affected by a failed release. The last two are harder to price, but they often determine whether the change is worthwhile.

Shared Artifact, Separate Execution

One tested artifact can expose several entry points and run as separate deployments or queue targets where resource isolation matters. That reduces dependency and build duplication while preserving distinct operating boundaries. A code or dependency change may still require a coordinated rollout across its consumers.

flowchart TB
    source[Shared source tree] --> image[One tested artifact]
    image --> api[API deployment]
    image --> worker[Worker deployment]
    api --> database[(Shared database)]
    queue[Job queue] --> worker
    worker --> database

The API and worker can have different memory limits, concurrency, scaling rules, and timeouts. They still share the image and the database. That combination is often more useful than forcing artifact, process, and data ownership into the same boundary.

Likewise, several modules can remain in one process while keeping their data ownership explicit. The important question is which boundary needs independent failure and resource control. Artifact, process, network, and data boundaries do not have to coincide.

Separate Deployment Does Not Mean Separate Data

The shared database in that picture is an important constraint. If the API and worker deploy on different days, a schema change has to work with both versions during the overlap. A worker consuming a queued message may see a payload written by yesterday’s API. Independence at the process boundary creates a compatibility obligation at the data boundary.

I would introduce a new field in a form old consumers can ignore, deploy readers that understand it, and only then rely on writers producing it. The same sequence applies to a queue message or a database column. This is where a nominally independent service can reveal how coupled it still is.

Release cadence is another test. If two deployables always change together, share an owner, and require the same rollback, their independence may be adding ceremony. If one can change and recover while the other continues serving users, the boundary has earned some of its cost.

Split With Evidence

Split when a shared boundary repeatedly causes a measurable problem: incompatible resource profiles, independent release pressure, a trust boundary, or an incident whose blast radius the split would materially reduce. Before extraction, define the data contract, failure behaviour, retry policy, observability, and rollback path. Those are the costs the code diagram hides.

Consolidate when several small deployables share dependencies and release timing, their isolation has little demonstrated value, and duplicated operations take more effort than their boundaries save. Check the noisy workload against the shared runtime and database budget before merging.

A deployable boundary earns its place when it gives a workload a useful independent limit: on resources, release risk, trust, or failure. The System Evolution Lab lets you test how those reasons change with load and failures.