Objects fell out of fashion for good reasons, and we threw away the part that actually mattered. The Abject project and Ask protocol brings it back in a world with AI.
Origins of Object-Oriented Programming
I am having a hard time explaining to people what my Abject project is about. I've been taking people's feedback and updating the website to make it clearer. One idea people are having a hard time with is when I say, "The internet is an object-oriented system." Those trained in computer programming often have a hard time grasping this because we have been trained that object-oriented means classes and class hierarchies. Through that lens, it doesn't make sense.
The person who coined the term "object-oriented programming" was Alan Kay, when he was in graduate school before he developed Smalltalk. He was influenced by Ivan Sutherland's Sketchpad, one of the earliest systems to contain object-oriented ideas. Sketchpad had no textual programming language in the modern sense; it was visual, and problems were solved through constraints and solvers. Kay was also influenced by his background in biology.
The big idea Alan Kay had was messaging. When he developed Smalltalk, the kernel was about sending messages between objects with a good metasystem to do late binding. By late binding I mean deciding what a message means at the moment it is received, not freezing that decision into a class hierarchy ahead of time. By metasystem I mean the layer that lets the system reason about and extend those rules safely. This is why Kay's definition of objects connects so directly to the internet: the internet is not one giant class hierarchy; it is independent systems sending messages across agreed boundaries. Kay made this connection in his message in the Squeak development mailing list in 1998. I'm going to reproduce it here because it's so important.
Alan Kay alank at wdi.disney.com
Sat Oct 10 04:40:35 UTC 1998Folks --
Just a gentle reminder that I took some pains at the last OOPSLA to try to
remind everyone that Smalltalk is not only NOT its syntax or the class
library, it is not even about classes. I'm sorry that I long ago coined the
term "objects" for this topic because it gets many people to focus on the
lesser idea.The big idea is "messaging" -- that is what the kernal of Smalltalk/Squeak
is all about (and it's something that was never quite completed in our
Xerox PARC phase). The Japanese have a small word -- ma -- for "that which
is in between" -- perhaps the nearest English equivalent is "interstitial".
The key in making great and growable systems is much more to design how its
modules communicate rather than what their internal properties and
behaviors should be. Think of the internet -- to live, it (a) has to allow
many different kinds of ideas and realizations that are beyond any single
standard and (b) to allow varying degrees of safe interoperability between
these ideas.If you focus on just messaging -- and realize that a good metasystem can
late bind the various 2nd level architectures used in objects -- then much
of the language-, UI-, and OS based discussions on this thread are really
quite moot. This was why I complained at the last OOPSLA that -- whereas at
PARC we changed Smalltalk constantly, treating it always as a work in
progress -- when ST hit the larger world, it was pretty much taken as
"something just to be learned", as though it were Pascal or Algol.
Smalltalk-80 never really was mutated into the next better versions of OOP.
Given the current low state of programming in general, I think this is a
real mistake.I think I recall also pointing out that it is vitally important not just to
have a complete metasystem, but to have fences that help guard the crossing
of metaboundaries. One of the simplest of these was one of the motivations
for my original excursions in the late sixties: the realization that
assignments are a metalevel change from functions, and therefore should not
be dealt with at the same level -- this was one of the motivations to
encapsulate these kinds of state changes, and not let them be done willy
nilly. I would say that a system that allowed other metathings to be done
in the ordinary course of programming (like changing what inheritance
means, or what is an instance) is a bad design. (I believe that systems
should allow these things, but the design should be such that there are
clear fences that have to be crossed when serious extensions are made.)I would suggest that more progress could be made if the smart and talented
Squeak list would think more about what the next step in metaprogramming
should be – how can we get great power, parsimony, AND security of meaning?Cheers to all,
Alan
Abject-Oriented Programming
I call the Abject project an Abject-Oriented system, playing off the phrase Object-Oriented. When I talk about objects, I am talking about them in the sense that Alan Kay had: it is really about messaging, and about having a metasystem to do late binding.
The Ask Protocol
The key message in the Abject system is the "ask" message, and it is really the heart of the system. It's about implementing the Ask Protocol. Every object in the Abject system implements this protocol. And I do mean every single object in the system. The details of how I built the Abject system are less important than the protocol. I built it to test the ultimate metasystem.
ask(question: string) -> stringThat's the ask protocol. It's that simple.
Any object can ask another object, in plain English, "How do I save this sketch?" and the storage object can answer with the exact message shape it accepts. The object can ask questions back to the sender too. It can be a two-way conversation. Behind them you need a model to convert the English into code, which LLMs do fairly well now. This is a project meant for the future, meant for a world where this kind of operation can be fast and local. Today you can run Abject with a rented model from a provider like Anthropic, OpenAI, or OpenRouter, and it also supports local models. The rented path is practical now; the local path is the direction I want computing to go.

One problem with scaling objects is that all interactions between objects, the messages they pass, have to be coded by people. This limits the composability of these systems. If you have two objects speaking different message formats, you need to bridge them by writing that bridge. If you want to create a new object to talk to another, you must first learn the messages the other can handle and make sure you format them correctly.
The Ask Protocol solves this problem. Because we now have LLMs that are good at writing code, especially in the small, they can build these bridges automatically. They can create new objects that talk with existing ones automatically. The protocol is what enables this because the LLM can have conversations with the objects to discover how they work. Objects can have conversations with each other to change themselves. You no longer have to manually glue them together.
Dialog and Compression vs Abstraction

There’s an objection to all this that I should address, because it’s the one I hear most often once people accept that objects can talk to each other. If you have an LLM sitting outside the system, and it can read every object’s manifest, why do the objects need to talk at all? Let the observer read the manifests, work out the message shapes, and write the object that bridges them. One reader, one pass, done. That sounds simpler than a protocol. I think it is worse, and for two reasons.
The first is dialog. When an object handles an ask, it knows who is asking. That means it can ask back. It can say: what are you actually trying to do here? Are those dollars or cents? What should happen when this fails? Does this need to be atomic? An outside observer reading manifests gets none of that. It has to guess, and the trouble with guessing is that it happens silently and nothing in the artifact it produces records where a guess was made or how confident it was. The ask protocol turns an ambiguous integration into a negotiated one. The handler can interrogate intent, push back on a constraint, refuse a request it thinks is a mistake, or redirect the caller to some other object that should be doing the work instead. Guessing is not a weaker form of asking. It is a different activity, and it comes with no error bars.
The second reason has to do with what a manifest can carry. A manifest and its documentation say nothing about the implementation behind them, and that is not a small omission. The DCI writing that Trygve Reenskaug and James Coplien have been doing for years makes the point starkly. On the fullOO site: “A class tells us everything about the properties of the individual objects that are its instances. It does not tell us anything about how these instances work together to achieve the system behavior.” And blunter still: “The result is that our code does not reveal everything about how a system will work.” A manifest is that class interface by another name.
In their DCI paper on Artima, Reenskaug and Coplien reach back to Smalltalk to name this precisely: “You could cheat in Smalltalk and invoke a method in any class in an object’s inheritance hierarchy whether or not a default implementation appeared in the base class. It would work, but it exacerbated the discovery problem, because the base class interface wasn’t representative of the object’s total behavior.” The discovery problem. That is the name for exactly what the outside observer is up against. The interface is not representative of the behavior, and no amount of careful reading fixes that, because the missing part was never written down.
So here is a distinction I do not think gets drawn often enough: abstraction and compression are not the same operation. An abstraction throws the implementation away. Somebody decided once what mattered, wrote that down, and discarded the rest. And because it was frozen at a moment, it goes stale. When the implementation changes, the abstraction either leaks or quietly lies, and the contract shifts underneath everyone who depended on it. An outside observer working from manifests alone is working from someone else’s frozen, lossy judgment about what mattered, made at some earlier date, for some other purpose.
The ask handler is not doing that. It runs inside the object, with access to the actual implementation code. When it answers a question it is not reciting a summary somebody wrote down once; it is deriving one, right now, from the real thing. That is compression rather than abstraction. It is still lossy because any answer shorter than the program, but it is faithful to what the program currently does, and it gets re-derived on demand every time somebody asks. Abstraction discards and freezes. Compression preserves and stays current. That is the whole reason an object can tell you more about itself than the most careful outside reader of its manifest ever could.
Which is why I do not think of the ask protocol as a convenience layer sitting on top of manifests. It is the thing that lets the system route a question to the only place where the answer actually lives.
Theory and Artifact

There is an idea I keep returning to from Peter Naur’s 1985 essay “Programming as Theory Building” that I think is one of the most important and most ignored insights in all of software. Naur’s argument, briefly put, is that a program is not the primary product of a programmer’s work, the primary product is a theory. A theory of the problem: why it has the shape it has, what forces are in tension inside it, why this particular structure resolves those forces well and not just adequately. The code is an artifact produced from that theory, a shadow of it, a record of certain decisions frozen in a particular moment. But the code is not the theory, and you cannot recover the theory by reading the code. When the people who held the theory leave, the program is, in a meaningful sense, dead, even if it still compiles and runs.
I think about this every time someone asks me why Abject is structured the way it is, and especially every time I describe how it handles objects. An object in Abject is not precious. The system creates it, gives it a registered name, and assigns it a runtime identifier, but the runtime identifier is explicitly ephemeral. When the system rewrites an object, the old runtime instance is orphaned and garbage-collected; a fresh one takes its place under the same durable name. When the system discards an object entirely, the implementation is marked discarded in an audit trail and the name is deregistered. Nothing is deleted from the record, but the running object is simply gone. From the inside, this looks like a system that treats its own code as expendable, which is exactly what it is.
The reason the system can afford to be so cavalier is that it is accumulating something more durable in parallel. As the system works with you, building objects, wiring them together, completing goals, observing what succeeds and what fails, it writes to a knowledge base. Every entry in that knowledge base has a type: a fact, a learned lesson, an insight, a reference and a pattern. Entries are recalled by keyword, by tag, or by exact title, and a selection of them is injected into the prompt context every time a new agent goes to work. The knowledge base is not documentation you write after the fact; it is a running accumulation of everything the system has figured out that is worth carrying forward. When a goal completes, the system mines the outcome and the task history for patterns. Those candidate patterns sit waiting for promotion, invisible to the matcher until someone, you or the system itself, decides they are solid enough to trust. Once promoted, a pattern is scored against the context of every future task and, if it matches well enough, surfaced to the agent about to do that work.
This is the mechanism Christopher Alexander had in mind when he developed the idea of a pattern language, not a checklist of solutions, but a living vocabulary of tested resolutions: each pattern names a recurring tension, the forces that make it hard, and the structural move that resolves those forces in this kind of context. The pattern does not tell you what to do; it tells you what shape tends to survive. James Coplien, whose writing on DCI I have been drawing on throughout this essay, has spent decades arguing that patterns are the real unit of architectural knowledge in software. That what you are actually building when you design a system well is a pattern language for this domain, not just a pile of code. I think he is right, and I think Abject makes the mechanism for that accumulation concrete in a way that most systems leave implicit.
What Abject is slowly building, as it works through your goals and learns which objects to create and which to discard, is a pattern language for this workspace and this user. The patterns that get promoted are the ones that survived contact with real tasks. The knowledge-base entries that persist are the ones that turned out to matter. And the objects, the actual running code, the TypeScript source stored by typeId, the runtime instances with their ephemeral UUIDs, are just the current artifacts the theory happens to have produced. You could throw every one of them away and rebuild from the knowledge base and the pattern language and you would get a system that works at least as well, probably better, because you would be building from understanding rather than from habit. That is what Naur meant, I think, when he said the theory is the valuable thing: not that the code is worthless, but that the code is downstream of something that matters more. The ask protocol routes questions to the objects that can answer them right now. The knowledge base and the pattern language are what survive when those objects are gone.
Data, Context, and Interaction
Data, Context, and Interaction (DCI) is an evolution of object-oriented thought: it keeps stable domain Data in objects while moving scenario-specific behavior into a Context, where Interaction describes the roles objects play. DCI keeps messaging readable without turning every behavior into another class hierarchy. The architecture was developed by Trygve Reenskaug and James O. Coplien.
Abject extends that evolution into a workflow. When a user asks the chat to accomplish a task, Abject creates a goal, decomposes it into tasks, gathers or creates the objects needed to perform those tasks, and sends messages to those objects to accomplish the goal. The context is the goal in motion; the interactions are the messages; and the data is carried by the objects. The Ask Protocol lets each object explain how to interact with it, so the workflow can be composed rather than hard-coded.
A Love Letter
The Abject project is a love letter to this idea of messages. It's a love letter to the idea of the ultimate metasystem. Of late binding. Of the power of symbiogenesis (another idea from biology).
Abjects' contribution is to make that idea concrete for today's software: every object can explain itself, every object can be discovered by conversation, and new bridges can be created by asking instead of hard-coding. It's a simple idea but a powerful one. Hard to grasp its scope. Kind of like the Schrödinger equation. You can put it on a t-shirt but spend a lifetime trying to understand it.
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.