The Quiet Rebellion of a POCO's Life
On simplicity, identity, and the discipline of knowing nothing
A Rebel Is Born
Most rebellions announce themselves. They come with manifestos, with noise, with the explicit intention to disrupt. The rebellion I want to talk about is different. It shows up as a class with a handful of properties, no base class inheritance, no framework references, no grand declaration. It compiles quietly. It asks for nothing. And yet, in its very plainness, it refuses almost everything the modern software ecosystem demands of it.
I have been writing .NET code since 2002. Over those years, I have watched frameworks rise and fall, patterns come in and out of fashion, entire architectural philosophies get adopted with religious zeal and then quietly abandoned. Through all of it, the Plain Old CLR Object — the POCO — has remained. Not because it is fashionable. Not because any conference talk celebrated it. But because it embodies something that does not go out of fashion: the discipline of being only what you are.
In the Vedantic tradition, there is a concept called svarupa — one's own essential nature, prior to all conditioning, prior to all roles and relationships. A POCO, in its own modest way, is the svarupa of a domain concept. It exists before the database touches it, before the HTTP pipeline knows about it, before the serializer has its way with it. It is, at the moment of its definition, purely itself.
The World It Enters — A Landscape of Colonization
To appreciate what a POCO refuses, you need to understand the world it is born into. For much of the early 2000s, frameworks did not merely assist your domain objects — they colonized them. Many frameworks encouraged domain objects to adopt framework-specific conventions, base classes, interfaces, or attributes. UI frameworks expected your models to implement particular interfaces. Persistence layers marked your properties with their own attributes, branding your clean domain concepts with the fingerprints of infrastructure.
I remember writing entities in early NHibernate configurations and early WebForms-era applications that felt less like domain objects and more like framework citizens. The class belonged, nominally, to your application. But it owed loyalty to something else. It carried debt — the invisible debt of coupling. Change the framework, and the object changes with it. The identity of your domain concept was hostage to infrastructure decisions.
This is not a trivial thing. When a class inherits from a framework's base, or implements its interface, something shifts. The object no longer belongs entirely to the problem domain. It belongs, in part, to the solution infrastructure. And that belonging has a cost that rarely shows up immediately — it shows up later, during a migration, during a framework upgrade, during a test that suddenly requires a web server to run. The coupling was always there. It just waited patiently to matter.
What It Refuses — Discipline, Not Laziness
A POCO does not know about the database. It does not know about HTTP. It has no opinion about how it will be serialized, validated, rendered, or stored. To someone unfamiliar with design principles, this can look like incompleteness — as if the developer simply forgot to add things. But this ignorance is not accidental. It is architected.
The Single Responsibility Principle, in its philosophical core, is about this exact discipline: a thing should have one reason to change. When a class knows about its domain and also about its persistence, it now has at least two reasons to change. Add serialization concerns and it has three. Each piece of additional knowledge is also a point of fragility — a thread connecting your clean concept to a world that will change without asking your permission.
The UNIX philosophy has a related insight: do one thing, and do it well. The tools that have survived decades in the UNIX ecosystem are the ones that resisted the temptation to do more. The POCO carries this same wisdom into object-oriented design. It models the concept. Everything else — persistence, serialization, validation — is someone else's job.
In Platonic terms, the POCO is the Form — the pure idea of the entity. The database table is one manifestation of it. The JSON response body is another. The UI view model is yet another. But the POCO itself stands apart from all these manifestations, uncontaminated by any of them. It is, in the most literal sense, the concept made code.
The Journey — Traveling Across Layers
Watch a POCO move through a well-designed application and you witness something almost graceful. It begins in the domain layer, defined as pure intent — properties that capture the essence of a business concept. Then the ORM picks it up, sees it as a table mapping, and hydrates it from rows and columns. The POCO does not resist this. It does not become a database object. It simply allows itself to be read in that context.
Then it travels to the service layer, where business logic is applied. Then to the API layer, where a serializer sees it as a JSON payload. Then perhaps to a presentation boundary, where it may be transformed into another model designed for display. At every step, the surrounding infrastructure interprets the POCO through its own lens. And at every step, the POCO remains unchanged. It is the still point around which the application's complexity revolves.
This is the POCO's quiet superpower: it belongs to every layer precisely because it belongs to none of them. It carries no layer's assumptions, no layer's dependencies. It crosses boundaries that framework-coupled objects cannot, because it has made no promises to any boundary. In a codebase full of objects that owe allegiance to something, the POCO is the only truly free citizen.
The Temptation — When the Framework Wants More
It never starts dramatically. It begins with a small concession — a [Required] attribute here, a [JsonIgnore] there. The framework is asking politely, and the attribute seems harmless. The POCO bends slightly and the code compiles. Nobody calls a design review.
But each attribute is a thread. Pull enough threads and what you have is no longer a plain object — it is a framework artifact wearing domain clothing. The class still describes your business concept, but it now also describes the HTTP contract, the database schema, the validation rules, and the serialization behavior. It has become an object that knows too much about the world around it.
There is an interesting identity question buried here: at what point does a POCO stop being a POCO? Is it one attribute? Three? A base class? This is not a purely technical question. It is closer to the Ship of Theseus — as each plank is replaced, at what moment does the ship become a different ship? As each framework dependency is added, at what moment does the domain object become an infrastructure artifact?
There is no bright line. But there is a direction, and that direction matters. Every addition that moves a POCO away from pure domain expression and toward framework awareness is a small cost. Small costs compound. The object that started as a clean concept gradually becomes something you are afraid to move, afraid to reuse, afraid to test in isolation — because it has too many invisible dependencies on the world around it.
The Identity Crisis — Anemic or Pure?
For years, a particular criticism has been leveled at POCOs: they are anemic. They are mere data bags. They lack behavior, and an object without behavior is not really an object in the OOP sense — it is just a data container with pretensions. This is often named as Anemic Domain Model and considered an anti-pattern.
But that conflates two separate things: what a POCO is, and how a POCO is used. A POCO is a design choice about coupling and dependency. Whether it carries domain behavior or not is a separate design choice entirely. You can have a rich domain model composed entirely of POCOs — classes with meaningful methods, invariants, and encapsulation — that still have no knowledge of persistence, HTTP, or serialization. The absence of framework coupling does not mandate the absence of behavior.
The anemic model problem is not a POCO problem. It is a design problem. An object can be plain and still be intelligent. Think of it this way: a person's essential character — their values, their way of thinking, their response to the world — exists independently of their job title, their employer, their social role. The roles come and go. The character remains. A well-designed POCO is the character. The framework integrations are the roles.
What Survives — The Quiet Victory
My personal experience of writing, maintaining, migrating, and, in some cases, mercifully retiring .NET code has taught me this: framework-coupled code ages badly. The WebForms code is a museum piece. The early NHibernate mappings are archaeology. The objects that inherited from framework base classes require the framework to even instantiate — and when the framework changes, they are stranded.
The POCOs? They still compile. The plain class that modeled a Customer or an Order in 2002 is still valid C# today. It requires no NuGet packages, no version pinning, no migration guide. It was not written for the framework of its time. It was written for the concept it represented, and concepts do not deprecate.
This is the POCO's quiet victory: longevity through non-attachment. In the Buddhist sense, the objects that cling — that grip tightly to a framework, a version, a platform assumption — suffer when those things change. The object that clings to nothing survives everything. It does not outlast the framework by fighting it. It outlasts the framework by simply not needing it.
Legacy — The Idea Evolves, But Does Not Die
The POCO did not merely survive — it taught. The modern .NET ecosystem has, in many ways, moved decisively in the POCO's direction. Entity Framework Core embraced POCO-centric design from the outset. ASP.NET Core was redesigned to be POCO-friendly from the ground up. Dependency injection, once a complex ceremony, became lightweight and non-invasive. The framework, slowly and without fanfare, learned to come to the object rather than demanding the object come to it.
And then came C# records — perhaps the most explicit acknowledgment yet that the programming model was moving toward something purer. Records can be seen as a natural extension of the same philosophy: immutable by default, value-equality built in, concise syntax that gets out of the way of the concept. They are not a replacement for POCOs so much as a natural maturation — the same philosophy with better tools. The child of the POCO, inheriting its values.
The rebellion that started with refusing a base class inheritance has quietly reshaped how an entire ecosystem thinks about objects. Not through a manifesto. Not through a conference keynote. Through the accumulated weight of code that was simply easier to work with, easier to test, easier to understand, easier to move. The ecosystem noticed. The ecosystem changed.
Plain Is the Result of Thought, Not the Absence of It
There is a temptation, especially for experienced developers, to measure the sophistication of a design by its complexity. The more patterns applied, the more layers introduced, the more framework features leveraged — the more serious the architecture looks. There is a temptation to demonstrate depth through complexity.
The deepest design decisions are often the ones that result in less. The decision to keep a class free of framework knowledge is not laziness — it is the result of having thought carefully about what that class is for, and having the discipline to protect that purpose. In the Shaiva Advaita tradition, the highest state of consciousness is described as pure awareness — unconditioned, unentangled, luminous in its simplicity. It is not the absence of something. It is the fullness of what remains when everything unnecessary has been released.
A POCO, in its own humble register, reaches for something like this. It is a domain concept that has released everything it does not need. It is not trying to be impressive. It is not trying to demonstrate architectural sophistication. It is simply, precisely, clearly itself. And in that clarity, it turns out to be the most durable, the most portable, the most testable thing in the codebase.
The frameworks that demanded so much of your objects? Many of them are gone. The POCO that refused their demands? It is still there, still compiling, still doing exactly what it was always meant to do.
That is not a small thing. That is what a life well-designed looks like.
That’s all for now. May your intention be clear and your mind be still. With this quiet wish, I rest my pen and return to the silence.