Programs as Data Flow: Rethinking Architecture Paradigms Through Building a 3D Editor in C++

Building a 3D editor in C++ as a dataflow graph—rethinking whether programs are objects or data flows.
A developer built a 3D editor from scratch in C++, modeling the program as a dataflow graph rather than a collection of objects. This article explores the fundamental differences between dataflow architecture and OOP, the insights of Greenspun's Tenth Rule, and the debugging benefits of visual programming.
From Objects to Programs: A Shift in Architectural Thinking
After growing tired of traditional ASP.NET and object-oriented architecture, one developer chose to build a 3D editor from scratch in C++. This seemingly ordinary technical project sparked a deep discussion on Reddit about the very nature of programs: Is a program a collection of objects, or a graph of data flows?
The developer's core argument is this: he no longer designs programs as a pile of objects calling one another, but instead models them as a dataflow graph—each node has inputs and outputs, and the connections between nodes are the logic of the application. This approach has long existed in the field of visual programming; the only difference is that the developer expresses it in C++ code rather than "boxes and wires."
The dataflow graph as a computational model has theoretical roots dating back to early research at MIT and the RAND Corporation in the 1960s and 70s. Unlike the von Neumann architecture (where instructions execute sequentially), node execution in the dataflow model is driven by the readiness of data—once the input data is available, a node can compute immediately, naturally supporting parallelism. The computational graphs of modern deep learning frameworks like TensorFlow and PyTorch are industrial-grade implementations of this very idea: a model is expressed as a directed acyclic graph (DAG), where each operator is a node and tensors are the data flowing along the edges. This makes automatic differentiation (autograd) and GPU parallel scheduling natural products of the architecture rather than features bolted on afterward.
The Familiar World of Node-Based Programming
For those unfamiliar with this paradigm, the community offered several excellent analogies:
- Unreal Engine's Blueprints: The visual scripting system most familiar to game developers, replacing code with connected components.
- Grasshopper: A node-based tool widely used in architecture and structural/mechanical engineering, designed for architects and engineers.
- Blender's node system: Node-based tools ubiquitous in 3D modeling and game art workflows.
Unreal Engine's Blueprints system is no isolated case but rather the culmination of decades of evolution in visual programming languages (VPLs). As early as the 1980s, Prograph and LabVIEW had already begun experimenting with expressing program logic through graphical nodes. LabVIEW remains a mainstream tool in industrial measurement and control to this day, and its dataflow execution model aligns closely with the developer's architectural approach. Blueprints' core contribution lies in bringing this paradigm into a real-time rendering game engine: execution pins handle control flow while data pins handle value passing. The coexistence of these two pin types resolves the awkwardness that pure dataflow models face when handling side effects and timing—which is also the central tension nearly every engineering-grade dataflow system must ultimately confront.
In other words, what the developer is doing is not invention from thin air, but rather reimplementing—in a systems-level language—a paradigm that has long been mature in the design and engineering worlds.
Why "Program as Workflow" Is Closer to the Essence
One of the most widely endorsed views in this discussion is that all programs are ultimately workflows. Data flows from input to output, undergoing a series of transformations, and finally producing a result.
As one commenter aptly noted, the developer's architecture "makes this workflow essence more explicit through a spreadsheet metaphor." This also explains why the developer was jokingly said to have "turned C++ into Excel"—a runtime, spreadsheet-style virtual machine where each cell is a computable node.
Object-Orientation Is Not a Universal Skeleton for Architecture
Interestingly, the developer did not reject OOP outright. The community especially appreciated his stance: object-orientation is useful, but it should not become the core organizing principle of an entire program.
The rise of object-oriented programming (OOP) has deep historical context. When Alan Kay designed Smalltalk, his original intent centered on "message passing"—objects as independent computational units communicating with one another through messages, much like biological cells. This differs fundamentally from the later simplification of OOP in C++/Java into "encapsulation of data plus methods." Kay himself has repeatedly stated that the "object-oriented" he invented was severely misunderstood. The real problem is this: when OOP becomes the organizational skeleton of a program, "inheritance hierarchies" often evolve into rigid coupling, and "encapsulation" sometimes hinders data visibility instead. The functional programming (FP) revival, Domain-Driven Design (DDD), and ECS (Entity-Component-System) architectural paradigms that have emerged over recent decades all, in various dimensions, reflect on and correct the overuse of OOP. The developer's dataflow architecture is the latest variant in this wave of reflection.
So why do we need objects at all? One commenter offered a rational explanation: objects mean communicating by "passing messages." You don't need to know which specific function is being called—you simply send a message to an object, and the corresponding function gets executed. This decoupling capability—independence from concrete implementations—is precisely the core value of objects.
Nearly all mainstream programming languages support objects, which shows this is by no means a passing fad. The developer's contribution is that he demotes objects to the humble role of "data structure + attached functions," rather than treating them as the skeleton organizing everything.
The Ghost of Lisp and Greenspun's Tenth Rule
Interestingly, the discussion soon slid into a classic topic in programming language history. Someone half-jokingly remarked: "Every programming language is asymptotically approaching Common Lisp with curly braces."
The Deeper Meaning of Greenspun's Tenth Rule
Someone then pointed out that this echoes the famous Greenspun's Tenth Rule:
Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.
Greenspun's Tenth Rule was proposed by Philip Greenspun in 1993. On the surface a jab at C/Fortran programmers, it more deeply reveals a software engineering law: complex systems spontaneously generate a demand for "programmability" as they evolve. When business rules become complex and frequently change, the maintenance cost of hard-coded logic rises sharply, and developers often introduce configuration files, rule engines, DSLs (domain-specific languages), or even complete scripting runtimes—a process that essentially amounts to implementing an interpreter for another language within the host language. Common Lisp's macro system is repeatedly invoked because it provides the ability to "express code as data" (homoiconicity), making metaprogramming a first-class citizen of the language rather than a stopgap patch. The developer's node-graph runtime is undergoing precisely this inevitable journey from "program" to "metaprogram."
The deeper meaning of this rule is that any sufficiently complex project will eventually require metaprogramming capabilities. Common Lisp is renowned for exactly this—its powerful macro system, functional programming capabilities, and CLOS (its object system)—leaps and bounds ahead in an era before closures were commonplace.
Commenters also cleared up a common misconception: the original Lisp indeed had only two data structures, atoms and lists, but Common Lisp long ago integrated arrays and hash tables into its standard library. The developer's editor architecture is, in a sense, "rediscovering" this ancestral wisdom.
The Debugging Dividend of Visualization
Setting aside the paradigm debate, the most practical benefit of the dataflow architecture was widely acknowledged: every value can be inspected, and undo/redo capability is built in by nature.
When a program is expressed as a dataflow graph, you can directly observe how data flows between nodes and how it is progressively transformed. This makes bug localization intuitive—you can see the intermediate states rather than facing a pile of black-box object calls. Undo/redo also transforms from "a feature requiring extra design" into a natural product of the architecture.
Dataflow architecture naturally supports undo/redo, with a theoretical foundation rooted in immutable data and persistent data structures from functional programming. Undo/redo in traditional imperative programs requires developers to manually implement the Command pattern, maintain an operation history stack, and handle complex state rollback logic—a notorious engineering challenge in systems like game saves and document editors. But when a program is modeled as a composition of pure-function nodes, where each computation produces a new state instead of modifying the old one in place, historical states are naturally preserved. Redux (a frontend state management library) and the Elm architecture are prime examples of bringing this idea into industrial practice: unidirectional data flow combined with an immutable state tree makes time-travel debugging possible, allowing developers to freely traverse and inspect historical states.
Innovation, or Reinventing the Heap?
Of course, there were sharp criticisms too. Some argued the developer had "reinvented the heap"—building yet another "heap" on top of heap memory for allocation and deallocation, which is precisely what programming languages have spent decades trying to hide from developers.
What the critic points to happens to be a mature alternative in the game engine field: the Entity-Component-System (ECS). ECS decomposes game objects into pure-data components and systems that process this data, with data locality as its core design goal. It stores data of the same component type contiguously in memory, fully leveraging the CPU cache and significantly improving performance when handling large numbers of objects. Unity's DOTS (Data-Oriented Technology Stack) and the Bevy game engine (Rust) are its latest industrial implementations. ECS and the developer's dataflow graph share the core philosophy of "separating data from logic"; the difference is that ECS places more emphasis on cache-friendly memory layout, while the dataflow graph emphasizes making dependencies between nodes explicit. The two are not opposed but rather respond from different dimensions to the same proposition: "the essence of a program is data transformation."
This critic's observation is not without merit: if implemented in JavaScript, you would probably use a TypedArray to serve as this "hash memory," whereas C++ allows you to touch the underlying heap memory directly, with a thinner abstraction layer. The byte representation of memory can be visualized as a contiguous region, clearly showing how data flows and transforms.
Conclusion: The Value of Starting From First Principles
Even amid the skepticism of "reinventing the wheel," the community as a whole viewed this project with appreciation. As one commenter put it: "Whatever fuels your drive, I admire building from first principles."
The true value of this project perhaps lies not in inventing some brand-new technology, but in prompting us to re-examine architectural habits we take for granted:
- Should objects really be the center of program organization?
- Is explicit data flow more conducive to understanding and debugging than implicit object calls?
- Do the underlying mechanisms that programming languages carefully hide actually bring transparency when deliberately exposed?
There are no definitive answers, but that is precisely the significance of rethinking architecture from first principles.
Key Takeaways
Related articles

Disaster and Glory of the Apollo Program: The History We Must Revisit Before Returning to the Moon
From the fatal Apollo 1 fire to Apollo 8's daring lunar orbit to Apollo 11's successful landing—revisiting the disasters, fears, and compromises of the Apollo program and their lessons for today's return to the Moon.

Netflix Trust Exercise Turns Into Firing Trap: Where Are the Boundaries of Corporate Trust?
A Netflix employee was fired after sharing private info in a trust exercise. We analyze the risks of corporate trust exercises and how employees can protect themselves.

AMD CDNA5 Architecture Deep Dive: Technical Evolution and the AI Computing Competition Landscape
Deep analysis of AMD's CDNA5 architecture covering Chiplet packaging upgrades, HBM memory evolution, and low-precision compute optimization, examining how AMD challenges NVIDIA's AI chip dominance.