Shared Database vs Database Per Service: Data Ownership in Microservices

Overview This compares two data architecture patterns for microservices: a shared database where multiple services read and write the same schema, versus database per service where each service owns an isolated data store. The choice determines how tightly services are coupled, how transactions and queries span service boundaries, and how independently teams can deploy and scale. Comparison Diagram Shared DatabaseDatabase Per ServiceService AService BService CSharedDBService ADB AService BDB BService CDB CSingle point of coupling & contentionIsolated data, independent scaling Comparison Table Aspect Shared Database Database Per Service Schema ownership One schema shared and often co-owned by multiple teams Each service exclusively owns and evolves its own schema Write path Any service can write directly to shared tables Writes go only through the owning service’s API Cross-service queries Simple SQL joins across tables in one database Requires API calls, data replication, or an aggregation layer Distributed transactions Native ACID transactions across affected tables Needs sagas or eventual consistency to span services Schema migrations Any change risks breaking other services using the table Migrations are local and safe to run independently Independent scaling Database becomes a shared bottleneck under load Each store can be scaled or tuned to its own service’s needs Technology choice All services locked into one database engine Each service can pick the best-fit database technology Failure isolation A database outage or lock contention affects every service An outage is contained to the owning service’s data Key Differences Shared database allows cheap cross-table joins but couples every consuming service to one schema Database per service enforces service autonomy at the cost of needing sagas for cross-service transactions Schema changes in a shared database require coordinating multiple teams, while per-service schemas change independently A shared database creates a single failure domain; per-service databases contain outages to one service Polyglot persistence — choosing different database engines per need — is only possible with database per service When to Use Each Shared Database ...

September 6, 2026 · 3 min · 445 words · jeonck

Stateful vs Stateless: Where the Session Lives

Overview This comparison covers whether a server or protocol retains session state between requests, or treats every request as a fully self-contained unit with no memory of prior ones. The choice determines how you scale, fail over, and route traffic across instances. Comparison Diagram StatefulStatelessClientServer Asession: id=42must returnto same serverServer Bno session dataClientcarries tokenServerServerany server canhandle the requestStateful: server pins session context and routing depends on itStateless: request carries all context, any node can serve it Comparison Table Aspect Stateful Stateless Request context Server retains prior interaction data across requests Each request carries all context needed to process it Session storage Held in server memory or local session store None on server; state lives in client token or database Routing requirement Requests must reach the same server (sticky sessions) Any server instance can handle any request Scaling model Vertical or sticky-session horizontal scaling only Trivial horizontal scaling, load balance freely Failure recovery Server crash loses in-memory session unless replicated Server crash has no session impact, retry hits any node Client design Client can be thin, server tracks progress Client or token must resend full context each call Typical examples Database connections, WebSocket sessions, FTP REST APIs, HTTP with JWT, DNS lookups Key Differences Stateful servers keep session memory; stateless servers keep none between calls Stateless systems need no sticky routing, simplifying load balancers Stateful failover requires session replication to avoid data loss Stateless designs push state into the client or token instead of the server Horizontal scaling is near-free for stateless architectures When to Use Each Stateful ...

September 6, 2026 · 2 min · 353 words · jeonck

Monolith vs Microservices: One Deployable vs Many

Overview A monolith packages an application’s entire codebase and functionality into a single deployable unit running as one process, while microservices split that same functionality into independently deployable services that communicate over a network. The choice shapes how teams build, deploy, scale, and recover from failures, so it matters far beyond just code organization. Comparison Diagram MonolithAuthOrdersInventoryPaymentsSingle processSingle deployMicroservicesAPI GatewayAuthOrdersInventoryPaymentsseparate databasesIndependent servicesIndependent deploys Comparison Table Aspect Monolith Microservices Codebase structure Single repository, one shared codebase for all functionality Multiple repositories, one per service with its own codebase Deployment unit Whole application built and shipped as one artifact Each service built, versioned, and shipped independently Inter-module communication In-process function calls within the same runtime Network calls via HTTP, gRPC, or messaging between services Data storage Typically one shared database for the whole app Each service usually owns its own database or schema Scaling Scale the entire application even if only one part is hot Scale only the specific services that need more capacity Fault isolation A crash or memory leak in one module can take down the app A failing service degrades its own function without necessarily crashing others Technology stack One language and framework across the whole application Each service can use the language/framework best suited to it Team ownership and releases One team or a coordinated release train ships the whole app together Independent teams own and release their services on their own schedules Key Differences A monolith runs as a single process, while microservices are distributed processes talking over the network Microservices trade in-process call reliability for network latency and partial failure handling Independent deployability lets microservices teams ship on separate release cadences, which a monolith can’t offer Splitting services adds real operational overhead — service discovery, monitoring, and distributed tracing Data ownership per service enables polyglot persistence but sacrifices easy cross-entity transactions When to Use Each Monolith ...

September 6, 2026 · 2 min · 425 words · jeonck

Last-Write-Wins vs Merging: Resolving Conflicting Writes

Overview When two replicas accept concurrent writes to the same key, a system must reconcile them. Last-Write-Wins picks a single winner by timestamp and discards the rest, while Merging combines both writes into a new value using domain-specific or CRDT logic. Comparison Diagram Last-Write-WinsMergingWrite At=10, val=1Write Bt=12, val=2val = 2(highest timestamp wins)Write A silently discardedWrite At=10, val=1Write Bt=12, val=2{A:1, B:2}(both values combined)No data lost, app may reconcile Comparison Table Aspect Last-Write-Wins Merging Conflict trigger Fires when two writes to the same key arrive with overlapping validity, regardless of content Fires the same way, but treats both writes as valid inputs rather than competitors Resolution mechanism Compares timestamps (or version numbers) and keeps the highest one Applies a merge function, CRDT join, or three-way diff to combine both values Data/metadata required A reliable clock or monotonic counter per write Version vectors, causal history, or a semantically defined merge operation Application involvement None — resolution is automatic and content-agnostic Requires the app or data structure to define what ‘combining’ means Outcome for the losing write Discarded entirely, no trace remains Incorporated into the final merged state, nothing is dropped Consistency guarantee Deterministic convergence, but the winner may be arbitrary relative to causality Deterministic convergence that also respects the semantics of both updates Performance overhead Minimal — a single comparison per conflict Higher — merge logic, extra metadata, and sometimes multi-way comparisons Failure mode Silent data loss under clock skew or concurrent writes at the same timestamp Unresolvable merge conflicts that surface to the application or user Key Differences LWW resolves conflicts purely by comparing timestamps, keeping only one write. Merging combines concurrent writes using a merge function or CRDT join instead of picking a single winner. LWW can cause silent data loss when clocks skew or writes race within the same tick. Merging needs semantic knowledge of the data type to combine values correctly. LWW adds negligible overhead per write; merging trades that simplicity for correctness under concurrency. When to Use Each Last-Write-Wins ...

September 6, 2026 · 3 min · 447 words · jeonck

Optimistic vs Pessimistic Locking: Detect-at-Commit vs Lock-Before-Access

Overview Both are concurrency-control strategies for preventing lost updates when multiple transactions touch the same data, but they differ in when they deal with conflict. Optimistic locking assumes collisions are rare and only performs a version check at commit time, while pessimistic locking assumes collisions are likely and takes an exclusive lock before any read or write proceeds. Comparison Diagram Optimistic Pessimistic Record (v1) no lock taken Txn A Txn B v1 → v2 committed v1 ≠ v2 conflict, retry conflict caught at commit time Record blocked Txn A holds lock Txn B waiting commits & releases lock acquires lock then runs conflict prevented up front Comparison Table Aspect Optimistic Locking Pessimistic Locking Access phase No lock taken; any transaction can read or begin writing the row immediately Lock acquired (e.g. SELECT FOR UPDATE) before the transaction reads or writes the row Concurrent access Other transactions freely read and write the same row in parallel Other transactions attempting the same row must wait for the lock holder to finish Conflict detection timing Deferred until commit, via a version number, timestamp, or hash comparison Not needed as a separate step; the lock physically prevents overlapping access On conflict Commit is rejected; the transaction is rolled back and typically retried No conflict occurs; the waiting transaction simply blocks until the lock is released Implementation mechanism Application-level version column checked in the UPDATE’s WHERE clause Database-level row or table locks managed by the lock manager Throughput under low contention High; no blocking overhead when collisions are rare Lower; locking overhead is paid even when no real conflict would occur Behavior under high contention Retry storms and wasted work as many transactions repeatedly fail and re-run Orderly queueing keeps correctness but serializes work and limits parallelism Deadlock risk None, since no locks are ever held Possible when transactions acquire multiple locks in inconsistent order Key Differences Optimistic locking detects conflicts at commit time; pessimistic locking prevents them via upfront locking. Optimistic locking never blocks other transactions, it only forces a retry on collision. Pessimistic locking holds an exclusive lock for the transaction’s full duration, serializing access to the row. Optimistic locking carries no deadlock risk because it never holds locks. Pessimistic locking trades raw throughput for predictability when contention is heavy. When to Use Each Optimistic Locking ...

September 6, 2026 · 3 min · 522 words · jeonck

Serializable vs Weaker Isolation: Strict Ordering vs Controlled Anomalies

Overview Serializable isolation guarantees that concurrent transactions produce a result equivalent to some serial order, eliminating every possible race condition. Weaker isolation levels like Read Committed or Snapshot trade that guarantee for higher throughput, deliberately allowing certain read/write anomalies that application code must tolerate or guard against. Comparison Diagram SerializableWeaker IsolationT1T2time (T1 fully precedes T2)No overlap in effect =equivalent to a serial runT1T2time (T1 and T2 overlap)Overlap window =dirty/non-repeatable reads,phantoms, or write skew possible Comparison Table Aspect Serializable Weaker Isolation Isolation guarantee Result equivalent to some serial execution of all transactions Allows specific interleavings; guarantee varies by level (Read Committed, Repeatable Read, Snapshot) Concurrency control Full conflict serializability via strict two-phase locking, serializable snapshot isolation (SSI), or predicate locks Row-level locks or MVCC snapshots that only block on narrower conflict sets (e.g. write-write) Anomalies prevented Dirty reads, non-repeatable reads, phantom reads, and write skew all eliminated Only a subset prevented; phantoms and write skew commonly remain possible Contention behavior Higher rate of lock waits, deadlocks, or serialization-failure aborts under concurrent access Readers rarely block writers (MVCC) or lower lock scope, so contention is reduced Throughput impact Lower throughput and higher latency as concurrency increases, especially with hot rows Higher throughput and better scalability under contention Application responsibility Application can assume correctness; only needs to retry on serialization-failure errors Application must reason about anomalies and add explicit checks (e.g. version columns, SELECT FOR UPDATE) Conflict detection timing Detected either at lock-acquisition time or at commit time (optimistic serializable schemes) Detected only for the narrower conflicts the level covers, often just at commit for MVCC writes Default in major databases Rarely the default; must be explicitly requested (e.g. SET TRANSACTION ISOLATION LEVEL SERIALIZABLE) Default in most systems out of the box (PostgreSQL/Oracle default to Read Committed, MySQL InnoDB to Repeatable Read) Key Differences Serializable enforces true serial equivalence; weaker levels only rule out a defined subset of anomalies. Weaker isolation relies on MVCC snapshots or narrow locks, cutting contention compared to serializable’s broader locking or SSI conflict tracking. Under Serializable, correctness bugs shift into retry loops on abort; under weaker levels, they shift into missed application-level checks. Write skew is the classic anomaly Serializable closes that even Snapshot Isolation leaves open. Choosing a level is a runtime trade-off between guaranteed correctness and throughput, not a one-time schema decision. When to Use Each Serializable ...

September 6, 2026 · 3 min · 526 words · jeonck

Primary vs Replica Reads: Strong Consistency vs Read Scaling

Overview In a replicated database, read queries can be routed to the primary node or to one of the read replicas. The choice trades guaranteed data freshness for the ability to scale read throughput and reduce load on the write path. Comparison Diagram ClientPrimaryhandles all writesReplicaread-only copyasync replication (lag)writeread (fresh)read (maybe stale)single node, no lagscales out horizontally Comparison Table Aspect Primary Reads Replica Reads Read target Always the single primary node Any of one or more read replicas Consistency guarantee Read-your-writes, strongly consistent Eventual consistency, may lag behind writes Replication lag exposure None, reads the current write state directly Exposed to lag, from milliseconds to seconds Contention with writes Reads compete with writes for CPU, locks, and I/O Writes on primary don’t directly compete with replica reads Read throughput scaling Bounded by single node capacity Scales horizontally by adding more replicas Latency profile Consistent, no wait for replication to catch up Can be lower if replica is geographically closer, but variable under lag Failover behavior Node failure requires promotion and brief write/read outage Load balancer can reroute to another healthy replica Typical use case Financial transactions, read-after-write flows, admin views Analytics, reporting, public APIs, dashboards Key Differences Primary reads guarantee read-your-writes consistency; replica reads may return stale data due to lag. Replica reads scale horizontally by adding read replicas; primary reads are bottlenecked by a single node. Primary reads compete with write traffic for resources; replica reads isolate read load via replication. Replication lag on replicas ranges from milliseconds to seconds depending on network and write volume. Failover on the primary causes brief unavailability; replica failures are masked by load balancing across peers. When to Use Each Primary Reads ...

September 6, 2026 · 2 min · 391 words · jeonck

Many Indexes vs Write Speed: Read Optimization vs Insert Throughput

Overview Every index you add speeds up specific queries by letting the database jump straight to matching rows instead of scanning the whole table. But each one is a second (or third, or thirteenth) structure that must be updated on every insert, update, and delete, so piling on indexes steadily erodes write speed. The right balance depends on whether your workload is dominated by reads or writes. Comparison Diagram Many IndexesFew IndexesWRITEWRITEIDXIDXIDXIDXIDX5 index updates per writeDISKWrite latencyHigher latency, faster readsIDXIDX2 index updates per writeDISKWrite latencyLower latency, slower reads Comparison Table Aspect Many Indexes Few Indexes Write path Every INSERT/UPDATE/DELETE also updates each index’s structure Writes mostly touch just the base table (or 1-2 indexes) Structures updated per write One update per index plus the table (N+1 operations) Minimal: table plus a small, fixed set of index updates Disk I/O per write Extra page writes and WAL/journal entries for each index B-tree Fewer page writes, smaller transaction log footprint Write throughput & latency Lower sustained throughput; each write costs more Higher sustained throughput; commits return faster Read/query performance Fast lookups and filtering across many indexed columns Slower queries on unindexed columns; more full scans Storage footprint Larger on-disk size from redundant index copies of data Smaller footprint, closer to raw table size Maintenance cost Rebuilds, vacuums, and statistics updates scale with index count Cheaper, faster maintenance windows Best-fit workload Read-heavy, query-diverse systems (reporting, OLAP) Write-heavy, ingest-heavy systems (logging, OLTP, ETL) Key Differences Every extra index adds a corresponding update on each write operation, not just at read time. Many indexes shrink query latency but inflate insert cost on the same table. Fewer indexes cut WAL volume and lock contention during heavy write bursts. Index count is a direct trade between read performance and write throughput, not a free win. Rebuild and vacuum overhead grows with every index the database has to maintain. When to Use Each Many Indexes ...

September 6, 2026 · 3 min · 429 words · jeonck

Cache Freshness vs Hit Rate: Correctness vs Efficiency

Overview Cache freshness measures whether the data returned by a cache still matches the current state of its source of truth, while hit rate measures how often requests are answered directly from the cache instead of falling through to the origin. The two metrics pull in opposite directions: optimizing for freshness means shorter TTLs and more origin traffic, while optimizing for hit rate means longer TTLs and a higher chance of serving stale data. Tuning a cache well means choosing the right balance point for that specific data’s tolerance for staleness. ...

September 6, 2026 · 3 min · 470 words · jeonck

Local vs Shared Cache: Per-Instance Memory vs Centralized Cache Service

Overview A local cache stores data in the memory of a single application process, giving the fastest possible reads but no visibility into what other instances hold. A shared cache lives in a separate service that every instance queries over the network, trading a bit of latency for one consistent view of cached data across the whole fleet. The choice shapes how you handle invalidation, scaling, and failure in a multi-instance deployment. ...

September 6, 2026 · 3 min · 444 words · jeonck