Distributed Systems: What They Are and How They Work

What distributed systems are, their characteristics and types (client-server, P2P, microservices, grid) and the challenges of CAP, consensus and fault tolerance.

Ficha dorada de ajedrez frente a un grupo de fichas plateadas conectadas entre sí por flechas doradas sobre una superficie oscura

A distributed system is a collection of independent computers that communicate over a network and coordinate their work to deliver a service that, from the outside, looks like a single one. They share neither memory nor a common clock. They reach agreement by exchanging messages, and they assume that any of their nodes may fail without the whole thing stopping. Cloud computing, a replicated database, a microservices application and a file-sharing network are all distributed systems. You lean on them all day, usually without noticing.

The idea is easy to state and hard to put into practice. Spread the work across many connected machines and problems appear that simply do not exist on a single server, from network partitions to clocks that drift apart and partial failures that leave the system half alive. On top of that sit a handful of theorems that mark the limits of what is possible. This article walks through what distributed systems are, how they differ from related ideas, their characteristics, advantages and challenges, the main types, scalability, fault tolerance and real-world examples, backed by primary sources such as NIST and the classic papers of distributed computing.

Distribution is a tool, and you reach for it for one of three reasons. One machine can no longer carry the load. You cannot afford a single point of failure. Your users are scattered across the planet. When none of those is true, a single well-built server will beat a fragile cluster every time, and it will be far easier to keep alive at three in the morning.

What a distributed system is

The classic definition by Andrew Tanenbaum describes a distributed system as a collection of independent computers that, to the user, appear as a single coherent system. That appearance is the whole point. Underneath there may be dozens or thousands of machines, and you should not have to know where each task runs or which node holds your data. You click a button, something answers, and you never think about the wiring behind it.

Three conditions follow from that definition and show up in every distributed system:

  • Autonomous components. Each node is a computer with its own processor and its own memory, and none of them controls the others absolutely.
  • Communication over a network. The only way to coordinate is to exchange messages. There is no shared memory everyone can read, and sending a message is neither instantaneous nor guaranteed to arrive.
  • A common goal. The nodes cooperate to provide a service, whether that is storing data, computing or answering requests, that none of them delivers on its own.

It is worth separating this from a few ideas it is often confused with. A centralised system concentrates everything on one machine or one point of control. A network operating system also pools the resources of several machines, but it does not hide them. Each server and client keeps its own identity, and you have to know which machine you are talking to. A concurrent program inside a single computer uses several threads or cores but shares the same physical memory. Distributed is something else. Several separate computers, no single clock, and a fragile network between them. That distance is exactly what brings the advantages, and it is also what introduces the complexity.

The word “distributed” is also not the same as “parallel”. Parallel computing splits one problem across many cores so it finishes sooner, usually inside a single machine where all the cores share memory and fail together. Distributed computing splits a service across machines that do not share memory and that fail independently. A program can be intensely parallel without being distributed at all, and a distributed system can be built from nodes that are not especially parallel inside. What defines the distributed case is that the units of work live in separate failure domains and can only talk to each other by sending messages.

Characteristics of a distributed system

Beyond the definition, a set of properties explains why designing one is different from programming a single machine. Read them as a to-do list, because each line is also a problem you have to solve.

  • Genuine concurrency. Several nodes run tasks at the same time, often over the same data. Coordinating that simultaneous access is the core problem.
  • No global clock. Every machine reports a different time, so there is no single “now” and the order of events has to be reconstructed.
  • Message passing. Nodes do not share variables. They send information that can be delayed, lost, duplicated or delivered out of order.
  • Partial failures. One part can go down while the rest keeps running. The system sits permanently somewhere between up and down.
  • Transparency. The system hides its complexity: access (using a remote resource as if it were local), location, replication and failure.
  • Heterogeneity. It is common to mix different hardware, operating systems and vendors, which forces common interfaces and protocols.
  • Scalability and decentralisation. The goal is to grow by adding resources without rebuilding the foundations and without a single point whose failure halts everything.

Look again at what that list really is. It is a list of promises you have to keep. You reach failure transparency with replicas, retries and consensus, and it does not come for free. The absence of a global clock forces you to build a logical ordering of events. Every line there is a design constraint, and the moment you forget one, it comes back as a bug you cannot reproduce on demand.

Advantages of distributed systems

If distributing makes everything harder, why do it at all? Because it solves problems a single machine cannot handle well.

  • More performance and capacity. Work is spread across many nodes that together add up to more power than any isolated machine.
  • High availability and fault isolation. If one node goes down, others take over its role, and a localised problem does not drag the whole system with it.
  • On-demand scalability. Resources are added or removed according to load, something impossible when everything depends on a physical server with fixed limits.
  • Proximity to the user. With nodes in different regions, latency drops for people far from the main data centre.
  • Better cost-to-performance ratio. Many mid-range machines are cheaper than one top-tier machine, and you replace them one at a time.
  • Flexibility and evolution. Components can be upgraded or rewritten separately without stopping the whole.

Cloud computing platforms are the most visible example of these advantages. They deliver the elasticity and the geographic spread that a self-hosted infrastructure struggles to match. That same success is why almost any service that matters today is, internally, a distributed system, including the site you are reading this on.

Challenges and limitations

The friendly face of distribution has a well-documented technical reverse side. Most of the headaches come from one uncomfortable fact. The network is not reliable, and the nodes never share a perfect state. The trouble is not that your machines are slow. The trouble is that they disagree about what is true, and they do it at the worst possible moment.

The CAP theorem

The CAP theorem is the starting point of any discussion about consistency. Eric Brewer formulated it in 2000, and Seth Gilbert and Nancy Lynch proved it formally in 2002. It states that a distributed system cannot guarantee all three of these properties at once:

  • Consistency: every read receives the most recent confirmed write, as if there were a single copy of the data.
  • Availability: every request receives a response, even if some node is down.
  • Partition tolerance: the system keeps working even if the network splits into two halves that cannot talk to each other.

Network partitions happen sooner or later, so partition tolerance is taken for granted. The real trade-off is between consistency and availability during the partition. A CP system rejects the request when it cannot guarantee fresh data. An AP system answers anyway, even if the result may be old, and reconciles later. Neither choice is “better”. It depends on whether the business can tolerate a stale datum (a missing “like”) or a stopped service (a payment that does not go through).

The model was extended by PACELC, published by Daniel Abadi in 2012. When there is no partition (Else), you still have to choose between latency and consistency. Coordinating nodes to give a coherent answer costs time. Relax that, and responses come back faster. So it is not enough to say “we are CP or AP”, because in normal operation the latency decisions weigh just as much.

Consistency and consensus

Consensus means several nodes agreeing on the same value: who the leader is, in what order operations go and whether a transaction is committed. They keep that agreement even if some of them fail or messages get lost. It is the piece on which consistency is built, and it has known theoretical limits. Fischer, Lynch and Paterson proved that, in a purely asynchronous model, no deterministic algorithm can guarantee consensus if even one process may fail. In practice you work around that with timeouts, which reintroduce a degree of synchrony.

The two most studied consensus algorithms are Paxos, described by Leslie Lamport and considered hard to implement for years, and Raft, designed by Diego Ongaro and John Ousterhout with understandability as an explicit goal. Both rely on a quorum, a majority of nodes, to decide and to survive minority failures. That is why quorum-based clusters usually have an odd number of members. Three or five nodes can lose one or two respectively and still reach a majority. An even number buys you the same tolerance at a higher cost.

Another fundamental piece comes from much earlier. Lamport’s 1978 work on logical clocks showed that, without a shared clock, the order of events can still be reconstructed using the “happened before” relation and logical timestamps. Databases and messaging systems later drew on that idea to decide which version of a datum is the most recent. The same reasoning family underpins vector clocks and version vectors in replicated stores.

In practice, consistency comes in degrees, and naming the model you actually need keeps you from overpaying for it. Linearisability is the strongest everyday guarantee. Every operation appears to take effect at a single instant, in a global order that respects real time. Sequential consistency keeps a single order of operations but does not tie it to real time. Causal consistency preserves the order only between operations that are causally related, which is often all an application needs. A comment should never appear before the post it replies to, and two unrelated likes need no particular order. Choosing the weakest model that still satisfies the user is one of the most effective ways to keep a system fast.

The alternative to strong consistency is eventual consistency. If writes stop arriving, all replicas eventually converge on the same value. It is the usual model in AP systems. In exchange for accepting temporarily outdated reads, you gain availability and speed. The word “eventually” is doing a lot of work there, though. The window during which replicas disagree can range from milliseconds to seconds, and the application has to be designed so that this does not break what the user sees.

Latency, partitions and partial failures

On a single machine, it either works or it is off. In a distributed system, one part fails while the rest stays alive, and that ambiguity is the source of almost every hard bug. You cannot tell whether a node is slow, crashed or cut off. Peter Deutsch and James Gosling summed up the fallacies of distributed computing as the assumptions everyone makes and that are all false: that the network is reliable, that latency is zero, that bandwidth is infinite, that the network is secure, that the topology never changes, that there is one administrator, that transport cost is zero and that the network is homogeneous.

From those fallacies come concrete design problems. Duplicated messages trigger repeated operations. Responses arrive after the client has already given up on the request. Clocks drift apart and make a later event look like an earlier one. The usual answer is to design idempotent operations, so running them twice changes nothing, and to accept that the system will operate in a permanent state of uncertainty about the exact state of its peers. Naming the things you cannot see becomes part of the job. A node that might be down is marked unknown, degraded or recovering until evidence says otherwise.

Operational complexity and security

Every node is also a thing you have to install, patch, monitor and protect. The attack surface grows with the number of components and with the internal traffic between them, which is often watched less closely than the perimeter. On top of that comes the cost of observability. Understanding what happened to a request that crossed fifteen services requires dedicated instrumentation, because one log per machine no longer tells the whole story. A failed request in a monolith leaves one stack trace. In a distributed system it leaves a trail you have to stitch back together from partial evidence.

Types of distributed systems

There is no single model. The most common ones differ in how they split the work and where control lives. It pays to know them, because each one fails in its own way.

Client-server

This is the oldest and simplest. Several clients ask, and one or more servers answer, store the data or run the service. The client starts the conversation and the server handles it. It is the basis of the web, email and most business applications.

Its most widespread variant is the layered architecture: a presentation layer (the interface), a business-logic layer and a data layer. Keeping them apart lets you scale each one separately and swap the interface without touching the database. The classic drawback is that the server can become a bottleneck or a single point of failure if it is not replicated.

Peer-to-peer (P2P)

Here there are no fixed clients or servers. Every node is both consumer and provider. Peers connect to each other and share resources such as files, bandwidth and capacity without a central authority. There are unstructured networks, where nodes find one another at random, and structured ones, which organise content in distributed hash tables (DHTs) so each resource can be located efficiently.

BitTorrent is the best-known example of file sharing. Blockchains take the concept to the extreme, with a network of nodes maintaining a replicated ledger with no owner. The advantage is resistance to failures and the ability to grow with every participant. The challenge is the difficulty of guaranteeing identity, trust and regulatory compliance. An open P2P network has to assume that some of its members are hostile, which shapes its whole design.

Microservices

Where a monolith would keep everything in one deployable unit, a microservices system is split into small independent services. Each one has its own responsibility and communicates over the network through APIs. They are deployed separately, scaled separately and can be written in different technologies. Containers and orchestrators such as Kubernetes are the usual infrastructure for running them.

They bring agility and fault isolation, and they let different teams work without stepping on each other. The trade-off is complexity. More services means more points of failure, more accumulated latency and a need for tracing, stable API contracts and mature automation. A well-modularised monolith is often the better choice on a small project, and there is no shame in starting there. You can split out a service later, once you know where the real pressure is.

Grids and clusters

A cluster groups similar machines, normally in the same place, to behave like a single powerful machine or like a redundant service. A grid goes further. It connects heterogeneous, geographically scattered resources, often donated by different organisations, to solve very expensive computations. The classic example is volunteer computing, where thousands of computers around the world donate processing time to analyse scientific data. The SETI@home project that looked for radio signals from space is the case most people remember.

The essential difference is homogeneity and proximity. A cluster is uniform and local. A grid is varied, remote and tolerant of nodes coming and going. Today’s cloud blends features of both, with resources from distributed data centres provisioned as if they were a single pool.

Type How it splits the work Typical example Main challenge
Client-server Clients ask, server answers Web application with a database Bottleneck and single point of failure
P2P Every node asks and serves BitTorrent, blockchains Trust, identity and compliance
Microservices Autonomous services over APIs A container platform Operational complexity and tracing
Cluster Similar, nearby machines High-availability service Coordinating shared state
Grid Dispersed, heterogeneous resources Distributed scientific computing Managing nodes that join and leave

Scalability: growing without breaking

Scalability is the ability to absorb more load by adding resources without degrading the service. It is achieved along two paths you should not confuse with each other:

  • Vertical scaling. Give the existing machine more power: more memory, more cores, faster disks. It is simple until you hit the ceiling of the available hardware, and it usually gets disproportionately expensive.
  • Horizontal scaling. Add more machines or nodes and split the work among them. This is the natural route for distributed systems. It grows with almost no theoretical limit, at the cost of having to coordinate all those nodes.

For horizontal scaling to work, several techniques have to be combined:

  • Load balancing. A component distributes incoming requests across the available nodes so none of them is overwhelmed.
  • Caching. Storing frequently read data or already computed results avoids repeating expensive work. Cache memory is the same idea applied inside each server.
  • Partitioning or sharding. Data is split into fragments and each node handles its own, so the load no longer depends on a single database.
  • Replication. Each datum has several copies on different nodes, which spreads reads and lets the system survive failures.
  • Autoscaling. Rules or metrics add and remove nodes according to demand, so the system rides out peaks and saves money when activity drops.
  • Stateless services. If a node keeps no data of its own between requests, any node can handle the next one. The state just has to live in a shared store.

One technique deserves special mention because it underpins most of the others: consistent hashing. When data is spread across nodes, the naive approach, mapping each key to a node with a simple modulo, breaks badly the moment a node is added or removed, because almost every key changes place at once. Consistent hashing arranges nodes and keys on a ring so that adding or removing a node only moves the keys that belonged to it. That is what makes it possible to grow or shrink a cluster without a mass migration, and it is the mechanism behind many distributed caches and key-value stores.

Beyond growing in machines, it helps to distinguish two further dimensions. Geographic scalability spreads nodes across regions without the communication time between them growing, so users around the world are served locally. Administrative scalability measures how hard the system is to manage as it grows. The more nodes, the harder they are to operate, and an architecture that ignores this ends up ungovernable even if it scales technically. That part leans on automation, standardisation and centralised management tools. A system that scales beautifully on paper but takes the whole team to rebuild by hand after an incident has not really scaled.

Fault tolerance and high availability

A serious distributed system assumes something is going to break. Fault tolerance is the ability to keep delivering the service, or to degrade it gracefully, when a component fails. These are the most common strategies:

  • Redundancy and replication. Every critical function exists in duplicate on different nodes, so the failure of one does not leave the service without an answer.
  • Failover. A standby node notices the primary has gone down and takes over, often decided by consensus and quorum to avoid two leaders at once, a split-brain situation that can corrupt state.
  • Failure detection. Nodes watch each other with periodic heartbeats. If they stop arriving, the sender is declared suspect. Because you cannot tell a slow node from a dead one with certainty, timeouts are tuned to the use case.
  • Idempotent retries. When a network call fails, the operation is repeated. If it was designed to be idempotent, repeating it does not create duplicate effects.
  • Circuit breakers. If a dependent service keeps failing, it is left alone for a while so the failure does not cascade through the whole system.
  • Graceful degradation. When one part is unavailable, the system shows a cached page or a reduced feature set so the whole service does not collapse.

Beyond the failure of a single node, serious systems plan for the failure of a whole site. Running across several regions or availability zones means a fire, a flood or a power cut in one location does not take the service with it. It also raises the hardest question in distributed design. How do you keep replicas consistent when they are separated by tens of milliseconds of latency? The usual answer is to keep strong consistency within a region and asynchronous replication across regions, accepting a small window of data loss in exchange for surviving the loss of an entire site. Which side you favour is a business decision as much as a technical one.

Availability is planned with two complementary metrics. The RTO is the maximum time you accept being down before the service is restored. The RPO is the maximum amount of data you accept losing as the last unreplicated write. Both fix how much redundancy is needed and at what cost. It is also common to validate the design by causing failures on purpose, the discipline known as chaos engineering, to check that the system reacts as expected before it fails for real. Testing recovery only after a genuine outage is the expensive way to learn.

Real-world examples

Distributed systems are not an academic topic. They carry a large part of the infrastructure you use every day.

  • The Domain Name System (DNS). A hierarchical database distributed around the world that translates names into IP addresses. No single server knows every domain, so each one delegates to the others.
  • Distributed databases. Systems such as Apache Cassandra spread and replicate data across many nodes. Others, such as Google Spanner, combine global distribution with strong consistency by leaning on synchronised clocks. Many of these ideas trace back to the replication models of Amazon Dynamo and Google Bigtable.
  • Distributed file systems. The Hadoop file system (HDFS) and its predecessor GFS split files into blocks and replicate them across several machines to process large volumes of data.
  • Microservices in the cloud. A modern application is made of dozens of containerised services orchestrated by Kubernetes, which places them, restarts them if they fail and scales them with load.
  • Messaging platforms. Systems such as Apache Kafka distribute streams of events across nodes so producers and consumers do not depend on a single process.
  • P2P networks. BitTorrent splits the download of a file among all the participants, and blockchains keep a replicated ledger with no central authority.
  • Mobile telephony. Phone networks combine handsets, switching systems and internet-based services to share the load of calls and data.
  • E-commerce. A single purchase triggers a chain of distributed interactions. The store, the payment system, the bank and the logistics provider exchange information without any one node controlling the whole process.

Underneath, they all share the same pattern. Divide the work, replicate what is critical and coordinate the nodes with protocols that assume the network and some components will fail. The networks that connect them, and protocols such as TCP/IP, are the foundation everything else is built on.

How they are designed and operated

Designing a distributed system means programming for a world where the network is unreliable and nodes fail unpredictably. These practices make the difference:

  • Design for failure. Consistency and availability decisions are made explicitly, not by accident.
  • Idempotence and retries with backoff. Repeating a request must be safe, and retries should be spaced out with some randomness so a recovering service is not hammered all at once.
  • Timeouts and circuit breakers. Every call between services needs an upper limit so a slow component cannot drag the others down with it.
  • Observability. Metrics, logs and distributed traces that follow a request across every service. Without that visibility, diagnosing a failure is guesswork.
  • Automation and layered security. Infrastructure as code and reproducible deployments, with least privilege and encryption between services.

Conclusion

A distributed system splits the work across independent computers that coordinate over a network to offer a single service. Its advantages, scalability, availability, proximity to the user and better cost, explain why it dominates today’s infrastructure. Its challenges, the CAP theorem, the need for consensus, latency and partial failures, define the limits of what you can promise.

Before you add the next server, write down the guarantees you actually need. Pick your consistency model. Decide what to do when the network splits. Then practise the recovery while everything is calm, because the worst time to discover that your failover does not work is the night it has to.

Frequently asked questions

What is the difference between a distributed system and a centralised one?

In a centralised system, all processing and state depend on a single machine or a single point of control. In a distributed one, there are several autonomous nodes that communicate over a network and share the work. The centralised system is simpler to operate but has a clear capacity limit and a single point of failure. The distributed system scales and survives better, at the cost of far more complexity.

Does the CAP theorem really force you to pick two out of three?

With caveats. Because network partitions eventually happen, partition tolerance is assumed and the real choice only arises during the partition, between keeping consistency or availability. When the network is healthy, the PACELC model reminds you that another everyday trade-off remains, between consistency and latency.

What is eventual consistency?

It is a model in which, once writes stop, all replicas end up holding the same value, but for a while they may return different versions. In exchange for accepting temporarily outdated reads, the system gains availability and speed, which is why it is common in partition-tolerant architectures.

Is a cluster the same as a distributed system?

Not exactly. A cluster is one specific case: similar machines, usually close together, working as one. A distributed system is a broader concept that includes clusters, but also grids, P2P networks, microservices spread across regions and any set of autonomous nodes that cooperate over a network.

What is a Byzantine failure?

It is the failure of a node that goes beyond a simple crash and behaves arbitrarily or maliciously, sending false or contradictory information to different nodes. Consensus algorithms tolerant of Byzantine failures, such as those derived from the Byzantine generals problem posed by Lamport, Shostak and Pease, can keep the system standing as long as a majority of nodes is honest.

Sources