Choosing a Stack for an Adult Project: What It Decides and What It Does Not
In short. The language and framework are almost never the reason an adult project stops making money. The real reasons repeat from project to project: the payment provider changes, the media library outgrows where it lives, an acquisition channel closes. What the stack decides is not development speed but how expensive each of those three events will be. Below: which boundaries inside the system make them cheap, and how to tell when no boundaries exist.
This page is about architecture and boundaries. For what it costs to build the thing in the first place, see adult paysite development; for where it runs, see hosting for adult sites.
Three events every paid project lives through
These are not hypotheticals. They are the ordinary biography of any project older than two years. The question is never whether they happen, only what each one costs when it does.
| Event | What happens | What architecture decides |
| Payment provider changes |
Rates go up, terms tighten, or the category is dropped. You need to move in weeks, not quarters |
Whether you hold your own record of money, or only their export of it |
| Media library grows |
Files outgrow where they sat. Egress turns into a line item you can feel |
Whether video travels through the application or around it |
| An acquisition channel closes |
A network drops the category or cuts volume. Budget has to move fast |
Whether payback per source can be read off, or has to be assembled by hand |
Notice what the table does not contain: no language, no framework, no database. Everything turns on where the boundaries sit, not on what the code is written in on either side of them.
Billing: the layer where a mistake costs money the same day
The provider reports events over a server-to-server call — the postback. That is the source of truth about money: the customer's browser can close, the thank-you page can fail to load, and the charge still goes through. Three requirements follow, and none of them can be added retroactively without pain.
-
The handler is idempotent. The same call arrives twice, because of a timeout, a retry, a dropped connection. The second arrival must not pay a commission again or extend access again. In practice this means the provider's event id exists and is unique in your table.
-
The event is stored before it is processed, not after. First write down what arrived, then interpret it. Otherwise a failed interpretation takes the fact of the event with it, and providers do not retry forever.
-
A refusal is an event too. Refunds, chargebacks and cancellations arrive through the same channel. A system that records only successful charges reports revenue that does not exist.
Out of those three grows the one architectural decision genuinely worth making up front: your own ledger of financial events. Not a report you can export from the provider's dashboard, but a table of your own where each row is something that happened, with a reference back to its source. A project that has one changes provider in weeks. A project without one discovers that its entire sales history lives in someone else's account, and that the account is about to close.
Media: why video should not pass through the application
The most common mistake in a young project is serving files from the same process that renders pages. It works until the first real volume, then stops: long connections occupy application threads, and the site slows down not because the logic is loaded but because somebody is watching a long clip.
| Delivery method | When it fits | What you pay with |
| Through the application |
A prototype, a small gallery, paid access to individual files |
Application threads; scales worst of all |
| A separate serving node |
A growing library, once files and pages start getting in each other's way |
One more server and the work of keeping it alive |
| Object storage with expiring links |
Paid access to a large catalogue |
Storage plus egress; you acquire a dependency |
| A content delivery network |
Audience wider than one country, time-to-first-frame matters |
More per gigabyte, and not every network takes this category |
⚠ Not every storage or delivery provider accepts adult content. Check that before choosing, not after moving: finding the restriction once terabytes are already uploaded costs more than paying a higher price from the start to a provider that serves the category in writing.
The egress estimate fits on a napkin, and usually differs from expectations by a multiple:
Monthly egress = views × average weight of the portion actually watched
Bill = storage (volume × rate) + delivery (egress × rate)
Note that the first formula takes the weight of the watched portion, not of the whole file. People rarely watch to the end, and estimating on full file size overstates the bill twofold and more. It understates it just as badly if previews autoplay.
People's data: where the boundary runs
This niche puts wildly different sensitivities into one system: public profiles and photographs on one side, and on the other the documents proving age and consent, payment details, private messages. They cannot share one perimeter with one level of access.
-
Age and consent records live apart, with an access log: who looked, when, why. They are needed rarely, but they are needed in exactly the form the party asking will accept.
-
Card data is not stored at all. The provider holds it and you keep a token. That is not a saving on development, it removes an entire class of obligations from you.
-
Messages and viewing history are personal data, and for European traffic that means being able to produce them and erase them on request. A system that stitches everything into one table with no owner column will have to be rebuilt to answer.
The full compliance picture is in the legal compliance guide. What matters here is that this is an architectural decision rather than a legal one. Drawn at design time, the boundary costs nothing.
What the stack does not decide
A short list that saves months spent in the wrong place.
- It does not decide rankings. Speed contributes; nothing else about the stack does.
- It does not decide ad access. Categories are refused by category, never by technology.
- It does not decide conversion. Price, trust and the number of steps to payment decide that.
- It does not decide retention. Retention is about what is there and whether there is a reason to come back.
So the argument about what to write it in is almost always worth less than the argument about where to put the boundaries. A team that knows one tool well will build something sturdier than a team that picked the fashionable one.
Signs there are no boundaries
Three of these five and the problem is not performance and not the language.
- Finding out last month's revenue means logging into the provider's dashboard.
- A repeated postback creates a second record, and the cure is deleting duplicates by hand.
- Video is served by the same process that renders pages.
- Payback by traffic source is kept in a spreadsheet somebody updates manually.
- Age documents sit in the same folder as public photographs.
If the boundaries are missing and the project already runs
A full rewrite is almost never the answer. Boundaries go in one at a time, hardest risk first, and each one pays off on its own.
| Order | What is done | What becomes possible |
| First |
Your own ledger of financial events, written alongside the existing accounting, breaking nothing |
Change provider without losing history. Reconcile their report against yours |
| Second |
An idempotent handler, and refusals recorded next to charges |
Reported revenue stops disagreeing with banked revenue |
| Third |
Media delivery moved out of the application |
Viewing load stops affecting page speed |
| Fourth |
Sensitive data separated from public data, with an access log |
An erasure request or an audit stops being an emergency |
The order is not arbitrary. The first two put you back in control of the money and cost the least, because nothing a customer can see has to move. The third is visible from outside and is done once volume makes it hurt. The fourth is the only one that sometimes jumps the queue: if European traffic is already arriving, it cannot wait.
What to check right now
- Whether a ledger of financial events exists, and whether it survives the provider leaving.
- Whether the provider's event id is unique in your database.
- Whether refunds and chargebacks are recorded alongside successful charges.
- Whether files are served outside the application.
- Whether your storage provider accepts the 18+ category in writing rather than verbally.
- Whether age records are separated from public content, and whether access to them is logged.
- Whether one query can gather everything the system knows about one person.
How adults.dev approaches it
We do not start from the choice of technology. We start from three boundaries: the ledger of financial events kept apart from the provider, media delivery kept apart from the application, sensitive data kept apart from public data. Inside those boundaries the tool is chosen to fit the job and to fit what you will be able to operate yourself afterwards, not to fit our preferences.
In practice the conversation usually opens with one question: what would it cost to change billing provider? When nobody knows the answer, that is the first piece of work, whatever the requirements document says.
Talk architecture with us:
Telegram: @adultsdev
E-mail: info@adults.dev