Cloud Pricing Is A UX Problem
Cloud platforms price units. Developers ship systems.
RecallMEM looks like a chat app until you try to price it.
Then it becomes a web app, Postgres, pgvector, background memory jobs, embeddings, file uploads, provider keys, voice sessions, transcript storage, model routing, and one very expensive mistake where background tasks quietly burned through Claude API tokens like an idiot.
The individual charges made sense. CPU, memory, storage, and model calls all cost money. What I missed was how many of those things one quiet-looking feature could trigger.
I was pricing the chat request I could see, not the system running behind it.
The Wrong Model
A lot of cloud pricing arguments start with the same move: pick a unit that makes your platform look sane.
CPU milliseconds. RAM. Requests. Seats. Machines. Function invocations. Bandwidth. Active time. Idle time.
Each unit can be honest and still hide part of the workload. That is why two developers can compare the same platforms and disagree about which one is expensive.
Their answers depend on whether the app is a short request, an idle service, a stateful machine, or several of those at once.
A useful pricing page should help me map product behavior to cost before I deploy it. If I learn that architecture from the invoice, the interface failed.
CPU Is Not The Whole Computer
There was a recent X discussion about Vercel sandbox pricing versus a small always-available machine. Jacob Paris made a fair point: if a workload only uses active CPU a tiny percent of the month, pricing it like a fully busy VM is misleading.
I agree with that. But CPU is not the whole computer.
CPU can be idle while memory stays provisioned, files persist, a database remains nearby, or an agent keeps the packages it just installed.
An AI app can look idle on a CPU graph while it waits on audio, writes facts after the user leaves, keeps files and tools available, or runs background jobs after the visible response is done.
You thought you were pricing a request. Then the product needed a database. Then a background worker. Then embeddings. Then storage. Then a voice socket. Then a model call. Then another model call. Then the cheap background model accidentally became the expensive model and now your “small feature” has a daily burn rate.
The infrastructure bill was not the scariest part. I was testing on a Fly Sprite, not a normal Fly Machine, and the sleep/wake behavior made the compute side feel reasonable. The app could sleep when inactive and wake fast enough that, in testing, it did not feel like the whole thing had gone cold.
The scary part was the model bill. Some background jobs were routing through a more expensive Claude model than I intended. Fact extraction, profile updates, title generation. Tiny product behaviors. Normal usage. Quietly $30+ per day.
I did not object to paying for those calls. I objected to discovering which code path caused them only after the bill jumped.
Requests Hide Systems
RecallMEM made the hidden work visible.
In RecallMEM, one user message can trigger retrieval, prompt assembly, provider routing, usage tracking, transcript storage, fact extraction, quote validation, embedding, profile updates, and future recall. The user sees one chat bubble; the application records several operations.
Voice makes this even more obvious. The user is just talking. But the app has to listen, stream audio, respond quickly, preserve context, avoid interruptions, save turns, and make the conversation useful later.
That does not make request-based pricing bad. Sometimes it is great. It just means the developer has to know when the request stopped being the product.
Platform Constraints Become Product Constraints
This stops being theoretical once the platform starts shaping product decisions.
The other recent thread that stuck with me was Zach Wilson’s post about DataExpert leaving Heroku after nine years. He said they had deployed the app more than 2,500 times on Heroku, were paying roughly $1,200/month, and moved to Fly.io for about $175/month with better performance and more flexibility.
The savings are the obvious part. The more interesting part is why they left.
Wildcard SSL constraints forced separate apps, and pricing tiers affected how services and databases were split. Those platform rules changed the architecture, not only the invoice.
A platform can feel simple for years, right up to the point where a certificate rule, service boundary, or database tier changes how the product must be built.
Localhost Is Lying To You
I felt a smaller version of this trying to get RecallMEM out of my local setup.
On my machine, everything felt obvious. Postgres was there. Ollama was there. Files were there. Environment variables were familiar. Model downloads had already happened. Random setup steps from three weeks ago were still quietly helping.
My computer was doing a lot of unpaid emotional labor.
Then I tried to run it somewhere else, and now I had to ask the rude questions. Where do secrets live? Does Postgres have pgvector? Are migrations running? Can file uploads survive? What happens to voice sessions? Which parts assume Ollama is nearby? Which background jobs run after the request? Which model do they use? If someone connects an OpenAI key, do we switch embeddings to OpenAI, or do we keep using Gemma?
Moving off my laptop exposed dependencies I had stopped noticing.
After that migration, I wanted explicit answers: the machine size, what wakes a sleeping service, and where persistent storage lives.
Different workloads still want different platforms. I care less about the label than whether the platform makes its cost model understandable.
I want to understand the billing model before the invoice arrives.
Surprise Is The Bad UX
Most developers do not compare cloud platforms by list price. They compare them by surprise.
Heroku surprise is: why did this become so expensive?
Vercel surprise is: why are there all these little metered things on this bill?
Traditional VPS surprise is: wait, I own all of this now?
Fly surprise is: okay, I need to understand machines, memory, regions, volumes, and usage.
That last one is real. Explicit primitives ask more from you. If a platform gives you machines, it is asking you to think about machines. Sometimes you do not want that. Sometimes you just want to ship the thing and go be happy.
But visible complexity has one big advantage.
You can build a mental model before the bill shows up. You can look at the machine size. You can look at the memory. You can look at the volume. You can understand what stays running, what sleeps, where the database is, and what happens when the app sits there doing almost nothing.
Explicit primitives are not automatically cheaper, but I can estimate them without guessing.
The Bill Should Be Boring
The bad outcome is not paying money. Apps cost money. The bad outcome is not knowing which behavior caused the bill.
Trust me, you do not want to learn this from the invoice.
If the invoice is the first place I can connect a feature to its cost, the pricing experience failed.
The same rule applies to model APIs. Choosing an expensive model for a user-facing answer is a tradeoff. Letting background jobs use it because my routing code ignored a setting is a bug.
That is a bug with a credit card attached.
I do not need every bill to be cheap. I need to know which behavior produced it.
Questions about this post? Ask the terminal on my homepage — it knows this whole site.