You have decided to build the product. The first question on the table is what to build it on, and it feels like the biggest question in the room, because a framework looks hardest to reverse. It is not. The framework you start with can be replaced in weeks, often by people who are not you. The data model you start with decides what the product is allowed to become, and it is still there in two years, long after the stack argument is forgotten.
Stack talk takes the space it does because it can be had before the product exists. Benchmarks are published, migration stories are public, opinions are free. Nobody in that conversation can tell you where a customer's record will live, which nightly job may touch it, or who will look at a stuck queue on a Sunday. Those questions decide whether the product survives its second customer, and all of them are answerable on paper, before anyone installs anything.
So this is not a comparison of frameworks. It is the order in which the decisions stop being cheap, what to measure when you cannot settle everything at once, and the argument this article exists for: the second decision, the data model and its boundaries, is worth more than the first one, the framework.
1. The data model decides more than the framework
Take a sheet of paper and write the nouns of your product: customer, subscription, invoice, project, report, whatever is actually yours. Then draw the lines between them. This takes an hour, and it is the part of the stack that cannot be swapped. A framework change is a project with a date. A data model change is a rewrite, and rewrites carry the old shape for years, because the historical rows are still there.
The question to answer on that paper is not which database is faster. It is who owns each row, and what a row is allowed to be shared with. One customer's invoice belongs to that customer everywhere: in the report, in the export, in the email the job sends at night. Products fail here in a predictable way. A table is built without an owner because there is only one customer, then the owner column is added later and made optional, and from that day on every new page depends on somebody remembering the filter.
Write ownership into the table rather than into the page. Then ask the two questions that usually expose the decision hiding underneath: what happens when a customer leaves, meaning what they can export and what gets deleted, and what a report is allowed to cross. Neither answer is a framework.
2. The second customer is a boundary, not a feature
If more than one company will pay for the same product, you are building a multi-tenant system whether or not anyone says the words. The choice is between rows that share tables and are separated by an owner column, and storage that is physically separate per customer. Neither option is correct in general, and both are expensive to change later, because migrations, indexes, backups and background jobs all inherit the choice.
Decide it before the first line, in one sentence a new developer can read, and then keep one promise with two logins. Two customers who must not see each other open the same report, and the rows differ. That is the whole test, and you can run it yourself. The places where this boundary actually breaks are described in where the boundary between two customers actually breaks: an export, a nightly email, a search box, a selector with an all-companies value in it.
Notice that this decision is not about scale. Two customers are already enough to need it. A product with ten users and a clean boundary is in better shape than a product with a thousand users and a filter somebody can forget.
3. Name the work that cannot live inside a web request
Every SaaS has work that does not fit a request: an invoice to issue, an export that takes a minute, an import from a file, an email per customer, a webhook that arrives twice. Decide now whether that work goes to a queue with retries or to a scheduled process, and what a second run is allowed to do. An invoice issued twice is a real conversation with a real customer. So is a summary email that arrives twice on a Monday.
Ask three questions about each piece of this work. What starts it? Which customer may it touch? What does a second run do? If the answer to the last question is that it checks something, write down what it checks against, because that sentence is your retry policy. A job that fails at three in the morning should be visible to you in the morning without anyone opening a server by hand. When a queue is already growing, the order of fixes is in check the states before you add workers, and those same states are worth deciding on before the first job is written.
4. The first thousand users are an operations test
A thousand paying users are not a load. Traffic of that size fits on one small server, and every framework being argued about handles it. What breaks around that point is not the framework. It is one machine holding the database, the files and the emails, with no tested restore, with logs nobody can search, and with a deploy one person runs by hand from a terminal in the evening.
Measure four things from the first week rather than from the first crisis: the slowest page at its ninetieth percentile, the depth of the queue, what the slow query log says, and how long a restore onto a second machine takes. That last number is the only one that tells you whether you have a backup. None of these four depends on the framework you chose. All four decide whether the first thousand users stay for a second year.
5. The maintainer in year two decides what was wise
The stack is a hiring decision made by someone who is not hiring yet. The people who maintain the product in two years will pick it up from what is written down, not from what was said in a meeting. An exotic choice that one person understands becomes a liability the moment that person takes a long holiday, and a rewrite argument on the day they leave.
Prefer what a new developer can be productive in within a week: a mainstream language, a mainstream database, migrations that run with one command, and hosting that does not need a specialist to stay up. This is not cowardice. It is the recognition that most of a product's life is maintenance, and that whoever inherits yours will run something like a technical audit against it: which flow is dangerous, which query is slow, which account sees everything, which decision lives only in one head.
6. Decide the framework last, on evidence you can collect
The framework is the most reversible of these decisions, so make it the last one, after the data model, the boundary, the background work and the maintainer. The evidence worth collecting is small and concrete. The questions are simple: does the team already know it, can it queue jobs and run migrations out of the box, can you host it at ten users without regret and at ten thousand without a rewrite, and will documentation exist for whoever reads the code at midnight. If those answers hold, the differences between the mainstream options are smaller than anything in the five sections above.
Before the first line of code, write these five down on one page:
That is the order I work in when a product starts: the boundary and the data model first, the work that leaves the request second, the framework last and briefly. Services cover the first version of a SaaS or CRM, the architecture around it, and the work that keeps it running after launch. Selected work shows the same decisions on systems that already run: a CRM split per branch, reports that keep customers apart, background work that survived its first thousand jobs. If you are at the point where the stack still has to be chosen, start with product development from scratch, or write to me with the nouns of your product and what each row belongs to. Leave passwords and customer data out of the message.