Maybe not microservice: The Case for Pipes, Pipelines, and Functional Isolation

Vee Satayamas (veer66) | Sun Jul 19 2026 | Bangkok, Thailand

1. Subsystem Decomposition

1.1 The Decomposition Problem

A subsystem decomposes a codebase into smaller, cohesive units. Two primary axes of decomposition exist:

1.2 Tension Between Framework Prescriptions and Decomposition Strategy

Organizing top-level subsystems functionally may create friction with frameworks that prescribe a technical-first structure. Concrete examples:

Frameworks with rigid prescriptions constrain architectural choices. Frameworks with no structure shift the entire burden onto the team with no guidance. This second approach might be fine for teams that know what they are doing and how to shape the architecture properly. Not everyone needs guidance from the framework.

1.3 Contexts as a Middle Ground

Phoenix provides contexts as a compromise:

2. Pipeline Topology and Data Flow

2.1 The Unix Pipeline Model

Unix pipelines model data flow through a single stream connecting stdout to stdin. This forms a linear chain where each stage's output becomes the next stage's input. Key characteristics:

2.2 Arbitrary DAG Topologies

Orchestrators like Airflow allow arbitrary DAG topologies with fan-in and fan-out edges. Tradeoffs:

2.3 Byte Streams and Opaque Containers

Unix-like pipelines connect programs by passing data through unidirectional byte-streams:

3. Typing Heterogeneous Pipeline Data

3.1 The Problem

Strict type systems introduce complications when handling heterogeneous data flowing through a pipeline where each stage transforms the shape slightly.

3.2 Failed Approaches

Single large type with many optional fields:

Many separate types for each step:

Both approaches fail because each pipeline stage depends on more than it needs.

3.3 Partial Fixes From Functional Programming

Two techniques alleviate but do not fully resolve the problem:

Remaining limitation: every step that pattern-matches on a sum type must know about all variants. Adding a new case still propagates changes through the pipeline.

3.4 Structural Type Compatibility

Structural type compatibility offers a complementary solution:

3.5 Python Implementation

Python implements several of these patterns:

Combined approach:

4. Microservices, Processes, and Isolation Patterns

4.1 The Shared Principle: Isolated State by Default

A microservice and a Unix process share architectural similarities:

4.2 Historical Lineage

Microservices on GNU/Linux are literally processes communicating via HTTP over TCP/IP. The historical chain:

The modern distributed system traces an unbroken lineage back to Unix.

4.3 Pipes as an Alternative to Microservices

Piping via stdin-stdout chains is another mode of interprocess communication:

4.4 Erlang and Clojure as Additional Isolation Models

Erlang processes:

Clojure and other functional runtimes:

Bottom Line: Think Twice Before Going Micro

Here is the real talk. You might want to pause before spinning up your first microservice. Ask yourself these questions:

The truth is, Unix pipes have been doing data transformation reliably since 1973. Erlang processes have handled millions of concurrent connections since the 1980s. Functional isolation with STM has been working since Clojure showed up in 2009. None of these require Kubernetes. None of them need a dedicated platform team. And none of them will haunt you with debugging nightmares at 3am.

Microservices are not evil. They are just heavy. They are the nuclear option for isolation. Use them when the problem demands the weight. Otherwise, reach for the lighter tool. A pipe, a context boundary, a protocol type. Try the easy solution first. If it breaks, then scale up. Most teams never get to that point. And their systems stay simpler, cheaper, and easier to maintain because of it.

So yeah, think twice. Maybe thrice. Then build the smallest thing that could possibly work.