12 min read
SOCIA: separation of concerns in AI
By Artem Panfilov
Where "separation of concerns" comes from
Ask any developer to define separation of concerns and you will get an answer. It is one of the standard interview questions, and it should be.
The phrase comes from Edsger W. Dijkstra. In his 1974 essay "On the role of scientific thought" he described a habit of mind: study one aspect of a problem in isolation, for the sake of its own consistency, while knowing that it is only one aspect. He was writing about thinking, not about code layout, and he called this separation, even where it cannot be done perfectly, the only technique he knew for ordering one's thoughts effectively.
The engineering root is two years older. In 1972 David L. Parnas published "On the Criteria To Be Used in Decomposing Systems into Modules." He took one small program and decomposed it two ways: once by processing steps, and once around design decisions, with each module hiding one decision likely to change behind an interface. The second decomposition won. Modules could be developed independently, a change stayed inside one module, and each module could be understood on its own. He called the idea information hiding.
Together they give the premise. A system stays comprehensible only if each part can be reasoned about in isolation, behind a narrow interface. The candidate who says "keep unrelated things apart" has memorized the definition. The candidate who says "the cheapest bug is the one you catch at the boundary" has understood it.
The thinking did not go away
Then AI coding arrived, and the mechanical part of the work changed hands. Code that used to be written by a junior team member under supervision is now written by a model under supervision. What did not change is the need to think. Someone still has to decide what the system is, where its boundaries are, and what it must never touch. The design work did not shrink. It moved up a level, and it got more urgent, because building the wrong thing is now cheap and noticing is not.
It also grew. We now design two things at once: the software, and the AI process that produces it. Which context the model gets. What it reads before it acts. Who checks its output, and against what. These are architecture questions, and most teams answer them by accident, in one long chat window.
Look at that window the way Parnas would. It has state. It accumulates coupling: the search results from earlier, the path that got abandoned, the three topics that came before this one. The model completes from all of it, with the current task competing for attention against everything before. That is a god object, and it degrades the way god objects degrade.
The window carries three costs. Tokens: the whole transcript is re-read on every turn, and most of it is exhaust. Governance: nobody can say what the model took into consideration, because the input was the entire transcript. Quality: a wrong assumption made while exploring flows uninspected into the plan, then into the code, and surfaces, if at all, as a plausible-looking diff.
Dijkstra's question applies: which aspect are you studying right now? So does Parnas's: where are the interfaces?
The principle: SOCIA
SOCIA stands for Separation of Concerns in AI. It is the classic principle applied to the process of working with a model, distilled from repeated hands-on workshops with delivery teams. The acronym is mine; the idea underneath it belongs to Dijkstra and Parnas. It fits in one line:
One session, one concern, one file out. The file is the interface.
Any non-trivial task an AI performs has the same steps inside it: discover the terrain, analyze what you found, plan the moves, execute them, verify the result. The model already runs a compressed version of this loop inside every prompt. SOCIA pulls the loop apart, gives each step its own isolated session, and puts a small, explicit, human-readable file at every seam as the only thing allowed to cross.
The five concerns
- DISCOVER maps the terrain: what this change touches, what it must not touch, what is unknown. It is read-only. It maps; it does not fix.
- ANALYZE judges the map: how the touched territory actually works, where the traps are. It consumes the map, not a dump of the repository.
- PLAN decides the moves: which changes, in what order, in what phases. Each phase must be verifiable on its own, and a plan a person cannot review in about five minutes is deferred confusion.
- EXECUTE makes one phase of moves and produces the diff for that phase, nothing else. Deviations are allowed, but they go on a ledger.
- VERIFY checks the result against the plan, fresh. It reads the task, the plan, and the diff, never the conversation, and reports what drifted.
The two rules
Rule one: the next concern reads the file, not the chat. If something matters, it goes into the interface file before the session closes. Anything that lives only in the conversation is already lost.
Rule two: a fresh session per concern. Not as hygiene, as function. A window that spent itself exploring dead ends carries those dead ends into the build. The isolation is where the quality comes from.
Files as interfaces
The handover file is about one page. Every claim in it is either cited, naming the file that taught the model the fact, or flagged under UNKNOWNS, which is the next concern's work order. The files are committed to the repository in a handovers/ directory, so the review trail is the commit history.
This is information hiding applied to a process. The discovery session knows how it searched. The planning session does not need to know. It needs the map.
Gates at the seams
Every seam is a gate, and every gate asks one checkable question. Is the map right, with everything uncited in UNKNOWNS? Does the analysis explain the map, or only restate it? Does the plan touch only what discovery found? Does the diff do what this phase said, with every deviation on the ledger? Did verification check against the plan, and does it name its blind spots?
A wrong assumption caught at the DISCOVER gate costs one line in a file. Caught after EXECUTE, it costs a feature and its tests. Caught in production, it costs trust. The pipeline turns one unanswerable question, "is the whole feature right," into five that a person can answer in minutes each.
Gates also buy independence. Suppose two checks each miss 20 percent of defects. If they are independent, both miss the same defect about 4 percent of the time, because 0.2 times 0.2 is 0.04. That is arithmetic, not a measurement; the 20 percent is an illustration, and it holds only if the checks are independent. The same window reviewing its own work twice is the same 20 percent twice. A fresh VERIFY session never saw the author's reasoning, so it has nothing to agree with.
Models rarely fail loudly. Their defects arrive with confidence, and a reviewer of a finished diff cannot see a defect that sits upstream of it. In workshops, teams repeatedly caught defects I had planted at the DISCOVER and PLAN gates, defects the same teams had voted to ship when reviewing the finished code. The gates front-load a cost you were paying later, with interest: five gated one-page reviews against one ungated review of a long diff, plus the rework.
Agent or skill
A skill is a move inside your session. An agent is a phase with a clean handover. If the handover matters, it is an agent. DISCOVER is an agent on a cheap model. ANALYZE is an agent on a strong model. PLAN is a skill, a conversation you steer. EXECUTE is a skill that runs under your eyes. VERIFY is a skill with rule two enforced: fresh window, no history.
A model per phase
Each phase starts from one file, so each can run on its own model. DISCOVER is mostly search and listing, so it goes to a cheap, fast model. ANALYZE needs judgment, so it gets a strong one. PLAN is where a wrong call costs the most, because every phase downstream inherits it, so it gets the strongest model you have. EXECUTE is mechanical once the plan is good, so a mid-tier model does fine. VERIFY gets a strong model and, always, a fresh window.
When not to use it
When the task has no real unknowns, do not. A one-line fix does not need five sessions; the tool already runs a small version of the loop internally, and five handover files for a typo is theater. For small and medium tasks, two pieces are usually enough: a blast-radius map from DISCOVER, and a plan written to a file before EXECUTE. The full pipeline is for work with real unknowns.
Try it now
SOCIA needs sessions and files, nothing else. Open a fresh session and paste this, with your own goal in the placeholder:
Do not write any code.
Concern 1 of 5: DISCOVER.
Write handovers/01-discovery.md for this change: <one sentence: your goal>.
Contents:
(1) every file this change plausibly touches, and why;
cite the file that taught you each claim;
(2) what this change must NOT touch;
(3) UNKNOWNS: everything you could not determine, as questions.
Anything you cannot cite goes in UNKNOWNS, not in the map.
Then STOP.
Read handovers/01-discovery.md like a reviewer. If the map is wrong, you just saved a thread of confident work built on it. If it is right, open another fresh session. For a first run, skip analysis; the seam is what you are testing.
Concern 3 of 5: PLAN.
Read handovers/01-discovery.md. That file and this message are your entire input.
Write handovers/plan.md: the changes, in phases, each independently verifiable.
You may not plan any change to a file the discovery map does not list.
If you need one, add it to an OPEN QUESTIONS section instead.
Then STOP.
Hold the gate yourself. Does the plan touch only what discovery found? If it reaches for a file that is not on the map, that is the seam doing its job.
What it gives you
Everything below follows from one structural move.
Less money spent on tokens
In a single long session, every turn re-reads the whole transcript. The input you pay for on turn forty, say, includes turns one through thirty-nine. Total input over a session grows with the sum of the transcript lengths at every turn, far faster than the transcript itself. That is arithmetic, not a benchmark. Caching lowers the price per re-read; it does not change what is re-read, and most of it is exhaust.
SOCIA breaks that curve. Each phase pays for its inputs and nothing else: one interface file, one instruction, and the files it needs to open. Nothing that happened in DISCOVER is billed again in PLAN, because PLAN never sees it.
The second saving is larger. Discovery is grep-shaped; a cheap model does it. Judgment lives in ANALYZE, PLAN, and VERIFY; that is where the strong model earns its rate. A strong model reading a one-page map is a small bill. A strong model dragging a two-hour transcript through every turn is a large one. Optimize the invoice, not the token counter.
More secure, because of gated controls
Every seam is a gate, and a person holds it. Nothing passes from one concern to the next without a human reading a one-page file and deciding it may proceed. That is a control you can describe to an auditor in one sentence, and the structure enforces it.
Context control comes with it. You decide precisely what each model sees at each step, because what it sees is a file you wrote or reviewed. Data that must never reach a model, client records, personal data, regulated material, is excluded at the interface. Each phase also gets only the authority it needs: DISCOVER is read-only, and EXECUTE gets one phase rather than the whole repository and a goal.
And the committed handovers are the audit trail. When someone asks what the model took into consideration, the answer is a file list with a commit hash, not a screenshot of a chat. Forensics after an incident means reading five short files in order.
More room to expand, because the workflow is loosely coupled
Each concern depends on one input file and produces one output file. Nothing downstream depends on how a phase did its work, only on what it produced.
Each concern can become an agent: a fresh session with a scoped context and a handover file as its contract. Moving it from a session you run to an agent is a configuration change, not a redesign. You review at the joints, not over the agent's shoulder.
Phases run in parallel, and any phase can be swapped, re-run, or handed off without touching the others. Once the plan is phased, several EXECUTE sessions can work at once. When a session dies, you lose one concern, not the afternoon. A phase can be handed to a colleague, or to a cheaper model, because its contract says exactly what it may consume and must produce. Delegation without a contract is hope.
This is also what makes fully automated self-revised agentic workflows possible. Once every phase produces a contract-shaped file, an agent can hold the gate: it reads the contract and the artifact, never the conversation, and sends the work back with a list of what drifted. The loop revises itself until the gate passes, and people sign only the gates that carry consequences. That is the subject of SAW: self-revised agentic workflows.
Vendor-agnostic, and this is the one that matters most
The interface is a file. Not a thread, not a memory feature, not a session identifier in one product's database. A plain text file in your repository. Anything that can read a file and write a file can run a SOCIA phase, so the pipeline works in any tool with any model.
You can mix vendors per phase today: a cheap model from one provider on DISCOVER, a strong model from another on PLAN, a third on VERIFY because it shares nothing with the first two. You can move a phase to another vendor tomorrow, because the phase depends on the file it reads, not on the tool that produced it. A price change, a retired model, an outage, a new model that reviews better: each becomes a one-phase decision rather than a migration.
There is no lock-in to one assistant, one API, or one context window. Long-conversation workflows are quietly coupled to the tool that hosts the conversation, because the state lives in that tool. Bigger context windows do not fix this; they raise the ceiling on how much exhaust you can accumulate before quality drops. SOCIA moves the state out of the tool and into your repository, where you already control access, history, and retention. The tools become interchangeable. The files are yours.
Dijkstra's point in 1974 was that separation of concerns is how a person orders their thoughts. That is still the point. The model does the mechanical work now. Ordering the thoughts, and putting a checkable interface where one thought hands off to the next, is still our job. SOCIA is that discipline, applied to the newest module we have.