Building Fluidware: A New Software Development Paradigm

Is Software Soft Enough?

Every computing system is a layered answer to one question: what is allowed to change?

Hardware is rigid: once fabricated, it is fixed. Software is flexible: rewrite it, redeploy it, replace it. Between them sits firmware: stable code burned into the machine, bridging physics and logic.

Hard, firm, soft. Three layers, each trading rigidity for adaptability and absorbing complexity so the layer above does not have to. This progression has served us for decades.

However, software, despite its flexibility, still locks decisions at deployment. We can ship fast, but every release hard-codes assumptions, which reality eventually invalidates. That is the limit: software is soft, but not soft enough to adapt without constant human intervention.

What we need is softer software. We call it fluidware. More than wordplay, the name marks a paradigm shift in how we design, build, and operate software.

Why Does Software Keep Failing at Complexity?

The central problem of software has always been complexity, and our most powerful strategy against it is abstraction: hide detail behind simple, well-defined boundaries so you can reason about parts without confronting the whole. Over decades, we have built an impressive arsenal on this foundation: layering; type systems; high-level languages; modularity and composability; encapsulation, data abstraction and interface abstraction; SOLID principles and design patterns; declarative paradigms; metaprogramming and so on. These are battle-tested approaches aimed at the same goal: tame complexity by dividing it, hiding it, and conquering it piece by piece.

These practices handle accidental complexity well. Essential complexity still remains.

Fred Brooks made the distinction in No Silver Bullet. Accidental complexity is the difficulty we create for ourselves through tooling choices. Essential complexity is the difficulty inherent in the problem itself, where abstraction hits a wall. You can hide essential complexity behind a clean interface, but you cannot eliminate it. The business logic is still messy underneath. The edge cases don't disappear; they leak through. Joel Spolsky named this The Law of Leaky Abstractions: all non-trivial abstractions, to some degree, are leaky.

Essential complexity resists specification in two ways. Sometimes the problem is fully specified, but the solution space is too vast to enumerate: chess has simple rules, yet no computer can exhaust every possible game. Sometimes the problem is unknowable at design time: human requirements are vague, contradictory, and incomplete; real-world conditions change unexpectedly. Autonomous driving suffers from both. In either case, more code alone does not make the search space tractable, nor resolve ambiguity, incompleteness, or unexpected change.

This is why complex software is persistently bug-prone. The primary issue is not carelessness; it is the requirement to write precise logic for a world that cannot be fully bounded or modeled.

What Is Fluidware?

If essential complexity cannot be eliminated by abstraction, perhaps it can be absorbed by a system designed to treat uncertainty as an architectural phase. This is the premise of fluidware.

Fluidware is software in which logic begins as probabilistic intent and is systematically hardened into deterministic code as understanding grows. While conventional software follows a specification: explicit instructions for anticipated cases, fluidware follows a purpose: a clear intent that guides behavior across cases no one anticipated, evolving through use, not only through redeployment.

A fluidware system treats uncertainty not as a specification failure, but as a natural phase of development. Open-ended decisions are deferred to runtime until patterns emerge. Behavior adapts from interaction data. Such a system exhibits distinct properties:

  • It tolerates ambiguity. Users express intent imperfectly. Fluidware interprets rather than rejects or ignores, making reasonable decisions without requiring every input to match a predefined format.
  • It handles the unspecified. When a situation arises that no developer foresaw, conventional software crashes or returns an error. Fluidware navigates the unknown by falling back on its understanding of purpose, while staying within its constraints.
  • It learns from use. Each interaction refines behavior through accumulated context, not only developer patches or redeployment.
  • It is context-aware and personal. Two users with different needs experience different behavior, not because someone wrote conditional logic for each case, but because fluidware shapes itself around the reality of each user.

The fundamental distinction is when decisions are made. In conventional software, decisions are made at write time. In fluidware, some are deferred to use time, guided by purpose rather than prescription. It remains adaptive because logic has not fully hardened, and may never need to.

What Is Fluidware Not?

It is not the LLM itself. The model is the engine; fluidware is the vehicle built on top of it.

It is not AGI. AGI, if it arrives, would render most software development discussions irrelevant. Fluidware is not that. It does not set its own goals or reason independently. It operates within boundaries its designers defined, serving purposes they chose. Think of it as the practical middle ground: more adaptive than conventional software, but still firmly under human control.

It is not vibe coding. Vibe coding treats AI-generated output as the end product: prompt, accept, ship. Fluidware is different. Whether code is generated by AI or written by human is beside the point. What matters is how we organize, evaluate, and control the result.

It is not prototyping. A prototype is built to be thrown away. Fluidware is built to evolve in place: each iteration is already part of the product, refining what exists rather than discarding and rebuilding from scratch.

Why Does This Need a Name?

Consider firmware. Technically, firmware is software: code stored in memory and executed by a processor. Yet it has a separate name because names shape engineering discipline. Say "firmware" and every engineer immediately infers the constraints: hardware-embedded, rarely updated, and high risk to change. The term creates a cognitive boundary that guides design decisions.

Fluidware deserves its name for the same reason, at the opposite end of the spectrum. Firmware anchors to the most rigid layer; fluidware anchors to the most fluid: human intent, shaky requirements, and problems not yet fully understood. The constraints are inverted. Where firmware's discipline is caution against change, fluidware's is resistance to premature commitment.

We need the word because we need a new mindset. Terms like "AI-assisted development" and "AI-powered system" bury the architectural change behind an adjective. Fluidware calls it out: a different operating model with different properties, constraints, and engineering trade-offs.

Why Now?

Fluidware became viable as three conditions aligned recently:

  1. AI capability. AI has existed for decades, but recent LLMs crossed a practical threshold: they generate working code and actionable plans from ambiguous descriptions. Imperfect, but enough to reveal the shape of the problem.
  2. AI cost. Generation is now cheap enough for iterative exploration in many workflows. As marginal cost falls, teams can test multiple directions before committing.
  3. Cultural readiness. A generation of developers now works with LLM-assisted coding. The shift from "the model writes my code" to "the model helps me discover the right code" is already underway.

How Do We Build Fluidware?

Two States of Fluidware

The distinguishing activity of fluidware engineering is not writing code, but managing state transitions.

To handle new complexity, we melt rigid logic into fluid intent. We could replace brittle if/else blocks with LLM prompts that describe goals. We trade certainty for adaptability.

To stabilize behavior, we freeze fluid intent into solid code by hardening successful patterns into deterministic functions, schemas, and rules. This cuts latency and LLM token costs alike.

Two Layers of Fluidware

The states introduced above correspond to architectural layers. A real system has three: conventional software at the bottom (infrastructure, external integrations, platform tooling) that never participates in state transitions; above it are the two fluidware layers.

       ┌─────────────────────────────────────────────────────────┐
       │                        Fluidware                        │
       │                                                         │
       │          ┌───────────────────────────────────┐          │
       │          │            Fluid Layer            │          │
       │          └───────┬───────────────────▲───────┘          │
       │                  │                   │                  │
       │           freeze │                   │ melt             │
       │                  │                   │                  │
       │          ┌───────▼───────────────────┴───────┐          │
       │          │            Solid Layer            │          │
       │          └───────────────────────────────────┘          │
       │                                                         │
       └──────────▲──────────────────────────────────────────────┘
                  |                                   │
           absorb │                                   │ release
                  │                                   |
       ┌──────────────────────────────────────────────▼──────────┐
       │                Conventional Software Layer              │
       └─────────────────────────────────────────────────────────┘

                    3-layered Software Architecture

The fluid layer is non-deterministic, AI-driven, and purpose-guided. It handles essential complexity beyond conventional code. It evolves through three channels:

  • Developer interaction: shaping behavior through prompts, specifications, and iterative refinement.
  • User interaction: learning from real usage, feedback, and accumulated context.
  • Self-evolution: inspecting performance and proposing updates to prompts, configuration, or source code through a controlled loop: propose, evaluate, approve, canary, then promote.

The solid layer is deterministic and testable: any logic exposed or potentially exposed to uncertainty belongs here, frozen now but ready to melt if conditions alter. It monitors and constrains the fluid layer through boundaries, permissions, logging, fallback logic, and rollback, allowing low-risk actions within policy but gating high-risk changes behind human approval.

The divide between fluidware and conventional software is not fixed. Conventional logic can be absorbed into the solid layer when it begins facing uncertainty; solid logic can be released back to conventional when it proves permanently stable.

The Build Loop

  1. Separate. Identify which parts of your system must stay conventional: infrastructure, security, third-party services or libraries, and non-business logic. These remain outside fluidware as the stable foundation.
  2. Melt. The remaining moving parts belong to fluidware. Determine the most volatile and melt those first. This may mean building new fluid components, or relaxing existing solid logic that can no longer keep up.
  3. Express intent, not implementation. Describe what you want in natural language or partial specifications. Let AI agents fill the gaps.
  4. Probe and iterate. The initial output is not production code; it makes the unknown visible. Run it, observe it, correct it, and repeat until success rate meets a satisfactory threshold.
  5. Constrain and validate. Define the boundaries the fluid layer must operate within: schemas, invariants, permissions, confidence gates, and safe fallbacks. Stress-test before external exposure with adversarial prompts, edge cases, and failure-mode drills.
  6. Limited rollout. Open to a small, monitored audience. Capture feedback, observe drift, and refine memory, prompts, configuration, and code.
  7. Let it self-evolve. The fluid layer may inspect its own behavior and propose improvements, but changes must pass sandboxing, policy checks, and rollback controls; dependency or source-level upgrades require explicit approval gates.
  8. Freeze and ship. When behavior stabilizes over a sustained window, harden what works into deterministic, testable code. Deploy with monitoring, staged rollout, and rollback plans.
  9. Melt again. Systems and users evolve. When frozen logic becomes brittle, return to step 2.

How Do We Test Fluidware?

The solid layer is tested conventionally: unit, integration, and end-to-end tests. The fluid layer is different: outputs vary, so evaluation must combine deterministic safety checks with statistical performance checks.

1) Test boundaries deterministically. Validate that the fluid layer stays within policy constraints, respects permission boundaries, and falls back correctly when confidence is low. Add hard guards for unsafe tool actions and hallucinated tool assumptions through typed allowlists, schema validation, and pre-execution policy checks. These checks should pass regardless of output variability.

2) Evaluate behavior statistically. Track task success rate, human override rate, policy violation rate, and behavioral drift over time. Include drift controls such as versioned prompts, diff reviews, and rollback to known-good versions, and monitor memory quality with trust tiers, provenance tags, and TTL. The fluid layer passes not by producing identical outputs, but by consistently meeting reliability targets across representative samples.

3) Regression-test freeze points. When fluid behavior is frozen into solid code, that code inherits conventional obligations: unit coverage, edge-case suites, integration and e2e tests, and rollback readiness.

What Changes in Practice?

The developer switches from writer to judge. The core activity is no longer writing everything from scratch, but evaluating model output: what works, what is missing, and when to freeze or melt. The key skill is no longer implementation speed but critical discernment.

Project estimation changes. Early discovery and solution exploration become faster and cheaper, but turning fluid behavior into production-ready logic still requires expert judgment.

Quality assurance moves upstream. More validation happens during exploration, where teams run, observe, and correct fluid behavior before commitment. Formal testing still applies to the solid layer.

Architecture becomes more explicit. Not everything should be fluid, and not everything should be solid. Personalization may stay fluid; data integrity, security, and financial logic must remain solid. Defining this boundary becomes a core architectural responsibility.

Technical debt takes a different form. The classic software risk is bad code under pressure; the fluidware risk is leaving too much unfrozen where deterministic logic is sufficient.

Case Study: Incident Triage Agent

Context

A platform team runs 200+ microservices. Their automated remediation system is rigid: an alert router matches errors to known signatures (regex on logs, tag combinations on metrics), maps each to an error ID, and fires a rule-based runbook script. It works when signatures match, but many incidents slip through, and adding new rules to cover them is slow and error-prone.

Before:

alert → router(regex/tag → error ID) → match? ─┬─ yes → runbook script
                                               └─ no  → page engineer

Result: More than half of alerts escalated to humans, and the routing rules had grown into a massive YAML file that was increasingly brittle to maintain.

Solution

Melt. The team replaced the rigid router with a fluid triage agent: an LLM given alert context, recent deploys, dependency graphs, and the runbook library. Far fewer alerts reached humans, but the agent misattributed failures caused by third-party outages to recent deploys and recommended unnecessary rollbacks. The trade-off was immediate: fewer escalations, higher risk.

Freeze. To manage this risk, every diagnosis carried an uncertainty score, and every action had a tolerance level (lowest for destructive ops like rollback and restart). If uncertainty exceeded tolerance, the agent escalated to engineers with reasoning attached. Known signatures (disk full, certificate expiry) had near-zero uncertainty and executed through deterministic handlers, bypassing the LLM entirely. Every outcome was recorded. Failures became lessons fed back as context; stable patterns were frozen into handlers, configuration, or source code, proposed by the agent as recommendations or even pull requests, merged with engineer approval. As a result, the fluid layer got smarter, the solid layer got thicker.

Re-melt. A migration from Datadog to Grafana changed metric schemas, breaking all metric-based frozen handlers. The team melted them back to fluid, relearned under monitoring, and refroze once stable. During this window the agent coincidentally surfaced a recurring pattern: two services that always failed together during deployments due to a dependency ordering issue, which no existing rule had matched. The agent proposed a new frozen handler, merged with engineer approval.

Outcomes

After:

alert → fluid agent
      → uncertainty ≤ tolerance → execute
      → uncertainty > tolerance → page engineer (with reasoning and related context)
      all outcomes → audit log
      corrections → fed back as fluid context
      stable patterns → proposed as recommendations/PRs → new frozen handlers

Result: Escalation rate dropped below 10%, and the routing YAML was replaced by a fluid layer that maintained itself, monitored by engineers.

Closing

Software development has always struggled with premature crystallization: the pressure to harden logic before we fully understand the problem. Fluidware offers an escape.

It resolves this tension through three layers. Conventional software is the skeleton: the rock-hard foundation that does not change with each interaction. The solid layer is the muscle: it monitors, constrains, and stabilizes. The fluid layer is the skin: the outermost surface that touches the world, adapts to it, and discovers what no specification could capture. Stable patterns freeze; brittle assumptions melt back. It is a symbiotic loop where each layer feeds the others.

As a fluidware developer, you are inside this loop as the final judge: stay fluid where you must, freeze where you can, and keep transforming as reality evolves.