Frameworks Are Institutional Memory

I stored passwords in plaintext on a floppy disk in the 1980s. Recently, a coding agent made a version of the same mistake.

When I was running bulletin board systems in the 1980s, password management could be remarkably straightforward.

A user created an account. They chose a password. The BBS needed to determine whether the password they entered later was the same one they had chosen.

So you stored it.

In plaintext.

On a floppy disk.

Hard drives were expensive and hardly universal in the hobbyist computing world I inhabited, so there was nothing strange about application data living on floppies. The important thing was that the software could retrieve the password and compare it against whatever the user typed the next time they called in.

Something like this was perfectly understandable:

KEN|swordfish
SARAH|password123
MIKE|enterprise

There was, of course, a slight problem.

The person running the BBS could read everybody’s passwords.

So people started solving that. Maybe the password should not be stored exactly as the user entered it. A substitution cipher or some other homegrown transformation could at least stop someone from casually opening the user file and reading every credential.

Problem solved.

Well. One problem solved.

The transformation might be reversible. Two users choosing the same password produced the same stored value. A compromised file exposed everyone at once. Users reused those passwords elsewhere. Password recovery introduced an entirely separate collection of problems.

Each solution exposed another question.

Over the following decades, “store a password” accumulated an extraordinary amount of engineering knowledge.

Today, saying an application needs authentication describes far more than comparing one string against another. It means hashing, salts, password reset, expiring tokens, single-use tokens, session invalidation, account enumeration, brute-force protection, cookie attributes, CSRF, rate limiting, and a long list of other concerns depending on the threat model.

A developer can still build all of that from scratch.

The more interesting question is whether they should.

The Code You Don’t Know You Need

The first thing developers learn about frameworks is that they save us from writing code.

That is true, and it undersells them badly.

A mature framework does not merely contain code somebody else wrote. It contains decisions somebody else already had to make, edge cases somebody else already hit, vulnerabilities somebody else already discovered, and fixes somebody else already learned were necessary.

A mature framework is partly institutional memory encoded as software.

Take authentication. The happy path is not conceptually difficult:

User submits credentials
        ↓
Find account
        ↓
Check password
        ↓
Create session
        ↓
Return success

A relatively inexperienced developer can understand that flow and implement a version of it.

Production authentication is not hard because the happy path is hard. It is hard because of everything surrounding the happy path.

How should the password be stored? What happens after repeated failed attempts? Can an attacker determine whether a username exists by comparing error messages? What happens to existing sessions after a password change? How long should a reset token remain valid? What happens after it is used? Can it be replayed? What attributes belong on the session cookie?

The developer writing authentication for the first time does not necessarily know to ask any of that.

A mature framework often does.

That is a very different kind of value from saving keystrokes.

Abstractions Change Who Has to Think

Software has always progressed partly by taking things that required specialized knowledge and putting them behind abstractions.

Assembly gave me extraordinary control over what the processor did. It was also a tremendous pain. Higher-level languages let us express more sophisticated ideas without manually considering every instruction. C retained substantial control over memory and execution while being far more productive. Later languages and runtimes provided increasingly sophisticated abstractions for memory management, type safety, concurrency primitives, and networking. Libraries and frameworks moved the boundary again. Managed platforms moved it again.

None of those layers made the layer underneath disappear.

Your Python application still causes a processor to execute machine instructions. Your Django application still communicates over networks. Your application on a managed platform still runs on computers somewhere.

What the abstraction changes is who has to think about those things routinely.

Control and Responsibility Arrive Together

Developers sometimes discuss control as though it were an unqualified good.

C gives me more control over memory. Running directly on infrastructure gives me more control than a managed platform. Writing SQL gives me more control than an ORM. Building against browser automation primitives gives me more control than a higher-level testing abstraction.

All of those statements can be true.

But control has a twin that gets mentioned far less often: responsibility.

If you control memory allocation, you are responsible for memory allocation. If you control your infrastructure, you are responsible for configuring, securing, observing, updating, and troubleshooting it. If you control every selector in an end-to-end test suite, you own every selector when the application changes.

You could write your inventory API in C. Manage the networking, parse HTTP, handle allocation, implement routing, serialization, authentication, database connectivity, concurrency, and error handling yourself. You would have an enormous amount of control, and you would have volunteered for an enormous number of problems that Flask, FastAPI, Django, Rails, Spring, and others have spent years making uninteresting.

The trade is not:

abstraction versus control

It is closer to:

less control and less responsibility versus more control and more responsibility

Sometimes the additional control is worth the additional responsibility. Sometimes it is not.

Flowchart showing the choice between owning complexity for greater control and engineering responsibility, or using an abstraction while accepting its opinions and constraints. Both paths lead toward building the work that differentiates the product.

Using the abstraction does not make you less of an engineer. Building the lower layer yourself does not automatically make you a better one. The skill is recognizing which problems deserve your attention, while understanding what you surrendered to have the others solved for you.

Opinions Are Part of the Product

“Opinionated” sometimes gets used as a criticism.

It certainly can be one. If a framework’s opinions conflict with your application’s fundamental requirements, you will spend more time fighting it than benefiting from it.

But an opinion is also a decision you do not have to make. A framework with an established approach to authentication, migrations, forms, routing, validation, sessions, and project structure has removed that many questions from your team’s agenda, and mature defaults usually carry years of accumulated experience with them.

The other side of the bargain is that somebody else has partially decided what your options look like.

Using MongoDB with Django was a long-running example. Django’s data layer was built around assumptions that fit relational databases particularly well, and getting MongoDB underneath it was possible through various third-party approaches, though “interesting” would be a charitable description of that experience. You were making one architectural choice while using a framework whose opinions had been designed around another.

What happened next supports the argument better than the complaint did. MongoDB shipped an official Django backend in public preview in early 2025, and it now handles embedded models, queryable encryption, geospatial lookups, and most of what the third-party packages struggled with. The framework’s opinions were not overturned. They were extended, by people willing to do the work of reconciling the document model with Django’s assumptions.

That took years, and it is exactly the process this whole post is about. The accumulated knowledge is the product. It just accumulates slowly.

So before adopting any framework, do not look only at the complexity it removes. Ask what decisions it makes on your behalf, what assumptions are embedded in those decisions, and how painful things become when your application needs something outside them.

You can dislike the choices. You can replace some of them. You can decide the framework is wrong for you. What matters is understanding the bargain.

Every Platform Sells You Both

Managed platforms made this visible in a way frameworks alone did not.

Before them, deploying an application meant owning a considerable amount of infrastructure knowledge: somewhere to run it, a way to deploy it, process management, configuration, networking, logging, databases, scaling, and monitoring. Managed platforms did not make any of that cease to exist. They changed who had to own it.

What is easy to miss is that this was never a choice between vendors. It is a choice available inside every vendor. AWS will happily sell you App Runner, Elastic Beanstalk, Lambda, or Amplify, and it will just as happily sell you EC2 instances, a VPC, load balancers, autoscaling groups, and a pile of IAM policies to assemble yourself. Google Cloud offers Cloud Run and App Engine alongside Compute Engine and GKE. Azure has App Service and Container Apps on one side, virtual machines and AKS on the other.

Same provider. Same workload. Radically different amounts of responsibility.

The opinionated paths get you running quickly and constrain what you can do. The assembled paths let you build nearly anything and hand you an operational surface that keeps expanding for as long as the system exists. Most real architectures contain both, which is usually the right answer.

The constraint is real in either direction. If you need behavior outside the managed model, the abstraction becomes limiting, and there are plenty of workloads where owning the pieces makes more sense. That is not a failure of the abstraction. It is the trade the abstraction offered.

One asymmetry is worth pricing in advance: the managed path is cheap to enter and expensive to leave. Moving off it means rebuilding the machinery the platform was quietly providing, usually under time pressure, usually at the exact moment the constraint became intolerable. That is not an argument against starting there. It is an argument for knowing which constraint would force the move before you have to make it.

For most teams the question was never whether their engineers could build and operate infrastructure. It was whether infrastructure was what their customers were paying them to be good at.

Spend Your Complexity Budget Wisely

I think of this as a complexity budget.

Every team has finite attention. There are only so many engineers, so many hours, and so many systems a group can deeply understand and maintain. Complexity spends that budget.

Build your own authentication and you have spent some of it on authentication. Operate your own infrastructure and you have spent some of it on infrastructure. Build a custom persistence layer and you have spent some of it on persistence.

Any of those can be excellent investments when the capability differentiates your product or your requirements genuinely demand the control. Dan McKinley’s “Choose Boring Technology” makes a neighboring argument with innovation tokens: you get about three, so spend them where they matter. The complexity budget is the same instinct pointed at what you build rather than what you adopt.

“We could build it ourselves” does not establish anything. Engineers can build lots of things.

The better question is:

Is this where we want to spend our complexity budget?

Learn What’s Underneath Anyway

None of this argues against learning fundamentals. Quite the opposite.

Understanding HTTP makes you better at using a web framework. Understanding SQL makes you better at using an ORM. Understanding memory makes you better at diagnosing what a runtime is doing.

Every abstraction eventually leaks. When it does, knowing what is underneath tells you whether you are looking at a bug, a limitation, a bad assumption, or the consequence of a trade you made a year ago.

But there is a difference between understanding a layer and taking responsibility for operating it.

I do not need to fabricate a processor to understand how one works. I do not need to write an HTTP server in C to understand HTTP. I do not need to implement my own password hashing to understand why plaintext was a bad idea.

Sometimes understanding a problem thoroughly is precisely why you decide not to implement it yourself.

The Password Problem Never Really Went Away

Recently I had a coding agent implement password reset for a small application.

It built the feature in about five minutes. The email arrived. The link opened. The password changed. The user logged in with the new one.

Everything worked.

Except the reset link worked a second time.

The implementation satisfied the obvious behavior while missing the invariant underneath it: once the reset succeeds, the token has to become useless.

That felt familiar.

Forty years ago the question was:

Does the password let the right person log into the BBS?

Yes.

Then somebody asked:

Can the sysop read everyone’s password?

Oh.

Years later:

Is the stored password protected?

Yes.

Can the stored representation be reversed or cheaply cracked?

Oh.

And now:

Does the password reset feature work?

Yes.

Can I reuse the token?

Oh.

The technologies change. The pattern does not. We implement the requirements we know about, and experience introduces us to the ones we did not.

That is why mature abstractions matter. They carry some of that experience forward so the next developer inherits the lesson without personally reliving it. Somebody already hit the edge case. Somebody already found the vulnerability. Somebody already spent three days on the concurrency bug that only appears on Tuesdays.

Which leaves an open question I do not think we have answered yet.

A mature framework carries its institutional memory in its decisions. Sometimes the reasons stay attached through issues, commits, documentation, and design discussions. Often they do not, and only the resulting constraint survives. Either way, somebody encountered the problem and changed the system because of it.

That is the part worth noticing. The single-use token check runs whether or not anyone remembers why it was added. The lesson is enforced rather than remembered.

A model trained on a very large corpus of code has absorbed something that resembles institutional memory. But it learned from artifacts produced after those decisions were made, rather than from the decisions themselves. It can reproduce what surviving code looks like without inheriting anything that enforces why one implementation survived and another did not.

Which might explain why my agent wrote a flawless happy path and dropped the invariant.

Happy paths are abundant. Invariants live in the decisions.

Facebooktwitterredditlinkedinmail

The Hidden Taxes of Prompt-Only AI

Part 8 of the Building the AI Memory Stack series

Over the past seven articles we’ve built an architecture that treats memory as infrastructure rather than as an oversized prompt. We’ve separated execution from assembly, preservation from explanation, trust from proof, and finally showed how verified knowledge returns to active reasoning through Context Hydration.

Now it’s time to ask a different question.

What does all of that cost?

Every AI system pays for memory. The only question is where.

Many systems choose to pay almost every cost inside the prompt itself. As context windows grow larger, it becomes tempting to treat them as an infinitely expandable memory system. If the model forgets something, add more documents. If retrieval misses context, increase the top-k value. If the answer is incomplete, make the prompt longer.

That approach works surprisingly well, until it doesn’t.

The cost isn’t limited to API pricing. Large prompts consume attention, increase latency, complicate orchestration, and force the model to separate important information from noise. The result is an architectural bill that grows long before the invoice from your model provider does.

Capacity Is Not Communication

One of the recurring themes throughout this series has been that storage and communication are different problems.

A library may contain every book ever written, but that doesn’t mean every book belongs on your desk while solving today’s problem. Likewise, Durable Memory can preserve years of organizational knowledge without requiring every byte of it to enter today’s Context Window.

The purpose of architecture is deciding what should move, when it should move, and what it costs to move it.

Introducing the Tax Model

The Sovereign Systems Specification describes these recurring costs as architectural taxes. They are not bugs. They are the predictable costs of moving, storing, validating, retrieving, and communicating information through an AI system.

Some taxes are unavoidable.

Others are self-inflicted.

Good architecture minimizes the second category.

The taxes that bear most directly on memory are these.

Prose Tax

Every explanation has a cost.

Humans naturally communicate in paragraphs. Models consume tokens. The more words required to express an idea, the more attention the model must allocate before it can begin reasoning.

High-information-density representations, such as structured records, schemas, identifiers, and references, often communicate the same meaning with a fraction of the prompt budget.

Context Tax

Every additional token competes for attention.

Context windows have grown dramatically, but attention remains finite. As more information enters the prompt, genuinely important information must compete with increasingly irrelevant material.

Bigger windows increase capacity.

They do not guarantee better focus.

Retrieval Tax

Searching for information is not free.

Embedding generation, vector searches, re-ranking, filtering, serialization, and prompt assembly all consume compute and latency before the model has produced a single token of useful work.

As argued earlier in this series, retrieval should support memory, not replace it.

Observer’s Tax

Every measurement has a cost.

Telemetry, debugging information, traces, evaluation artifacts, and compliance records are essential for production systems. Left unchecked, however, they begin competing with operational workloads for compute, storage, and engineering attention.

Observability is infrastructure.

It should not become interference.

Ingestion Tax

The cheapest place to improve information quality is before information enters the system.

Poorly structured data generates downstream costs forever. Duplicate records, inconsistent schemas, missing provenance, and unverifiable observations all create future work for retrieval pipelines, prompt assembly, and reasoning itself.

Every bad write compounds.

Every good write pays dividends.

Fiscal Architecture

Viewed individually, these taxes seem manageable.

Viewed together, they become an architectural discipline.

None of these taxes exist in isolation. Attempts to reduce one often increase another. Expanding a prompt may reduce retrieval work while increasing Context Tax. Adding more telemetry may improve observability while increasing Observer’s Tax. The goal isn’t minimizing a single tax; it’s balancing the entire system.

Tax What it charges for Lowered by
Prose Tax Meaning expressed in more tokens than it needs Structured records over paragraphs
Context Tax Irrelevant tokens competing for finite attention Hydrating only what the task needs
Retrieval Tax Search, embedding, and re-ranking before any output Higher memory quality, less searching
Observer’s Tax Telemetry and traces competing with real work Bounded, purposeful observability
Ingestion Tax Poor structure and missing provenance at the write Verified, structured writes

Organizations often spend months optimizing prompts while ignoring the systems that create those prompts. Yet the largest savings usually come from improving memory quality, reducing unnecessary movement, and preserving information in forms that are inexpensive to hydrate later.

Traditional software architecture optimizes CPU, memory, network bandwidth, and storage.

AI architecture adds another economic dimension: attention.

Every architectural decision ultimately affects how much attention the system spends producing useful reasoning.

In other words, the cheapest token is often the one that never needed to exist.

The Goal Isn’t Zero Tax

Every system pays taxes.

A trustworthy system willingly pays some of them.

Hashes must be computed. Receipts must be signed. Memory must be verified before it is restored. Good engineering accepts these costs because they purchase integrity, explainability, and confidence.

The goal is not eliminating cost.

The goal is paying the right costs in the right places.

Looking Ahead

The final article brings everything together.

We’ll revisit the AI Memory Stack from the perspective of runtime execution rather than teaching order, showing how data actually flows through the architecture and mapping each responsibility to the Sovereign SDK. By the end, the stack should feel less like a collection of concepts and more like a blueprint that can be implemented today.

Facebooktwitterredditlinkedinmail