I built AiCal because I was tired of copying flight times out of confirmation emails into my calendar by hand. The idea was clean: forward an email, a model reads it, an event appears on your calendar. I had a working version in a weekend. And then I did what almost every developer does next, which is the thing this article is about not doing. I started polishing. Better parsing, a settings page, recurring-event support nobody had asked for. I was building features for a user base of one: me.
That is the trap. As developers we are extremely good at the part that comes after validation and extremely bad at validation itself, because validation feels like not-coding, and not-coding feels like not-working. So we skip it and build. This playbook is the set of corrections I now force on myself before a side project earns the right to be called a SaaS attempt. None of it is glamorous. All of it is cheaper than building the wrong thing for three months.
The only question that matters first
Before pricing, before a landing page, before a single line of "monetization" code, there is one question: does anyone other than you feel this pain badly enough to change their behavior over it?
Notice what that question is not. It is not "would this be useful," because almost everything is vaguely useful and "useful" never paid for anything. It is not "do people like the idea," because people are polite and liking an idea costs nothing. The bar is behavior change. Will someone forward an email, fill out a form, give you their address, or hand over money? Those are the only signals that survive contact with reality, because they all cost the person something.
When I am honest about my own projects through this lens, half of them fail instantly. MyUniNotes was useful to me, but the behavior-change bar for "students switch their note system" is brutally high. PrioTask was a fun build, but the task-manager graveyard is enormous precisely because the pain is real but the switching cost is higher than the relief. Knowing that before you build is worth more than any feature.
Before the demand tests, sit with the question that catches most of us. Predict honestly:
Cheap demand tests, ranked by how much they cost you
The whole game is buying the strongest signal for the least effort. Here is the ladder I actually climb, weakest and cheapest at the bottom.
The conversation. Talk to five people who have the problem. Not "would you use a tool that," which invites politeness, but "tell me about the last time this happened to you." If they cannot remember a specific recent instance, the pain is theoretical and you should stop. This costs an afternoon and saves months.
The landing page with a real ask. A single page describing the product as if it exists, with one call to action that costs the visitor something small: an email address, a waitlist spot, a "notify me." A page that gets 200 visitors and 2 emails is telling you something loud. I would rather hear it from a static page I built in an evening than from an empty Stripe dashboard six months later.
The concierge version. Do the thing manually for a handful of real users before you automate it. For AiCal, the honest test was not the parser, it was: will people actually forward me their messy real-world emails and trust the result? I could have answered that by reading emails by hand and replying with calendar invites, no model required. If nobody forwards anything, the slick automation is irrelevant.
The pre-sale. The strongest signal short of a running business. Ask for money before the product is fully built. It feels uncomfortable and slightly fraudulent the first time, which is exactly why it works as a filter: a credit card is the least polite, most honest signal a human can give you.
Here is the same ladder as a switcher, weakest and cheapest first, strongest and most uncomfortable last:
Talk to five people who have the problem. Not "would you use a tool that," which invites politeness, but "tell me about the last time this happened to you."
If they cannot remember a specific recent instance, the pain is theoretical and you should stop. Costs an afternoon, saves months.
Which rung you climb first is not a free choice. It depends on what you already have access to and how far the idea has actually moved. So before you default to the test that feels most comfortable, answer two honest questions about your situation.
Which demand test should you run first?
Pick the cheapest test that produces a signal you can actually trust.
Can you already reach five people who have this pain?
The tree collapses to one rule: never run an expensive test when a cheaper one would have told you the same thing. Zoomed all the way out, the individual tests are stages in one funnel, and here is the path an idea actually walks before it earns the name SaaS:
Start with the one behavior-change question: does anyone but you feel this pain badly enough to act on it? Write the kill criteria here, while you are still objective. The tip: phrase the idea as a specific recent moment someone lived, not a vague category of usefulness.
Each stage is only worth running if you are honest about the signal it produces. Now, pricing.
Pricing is a validation tool, not an afterthought
Developers treat pricing as a thing you bolt on at the end. It is actually one of your sharpest validation instruments, and you should deploy it early. The moment you put a number on something, the conversation changes from "nice idea" to "is this worth twelve euros a month to me," and that is the conversation you needed all along.
You do not need a billing integration to test price. You need a number and a way to watch the reaction. Put three tiers on the landing page and see which one people click before any of them work. Ask the concierge users what they currently pay to solve the problem some other way; that anchor tells you the ceiling. The goal at this stage is not revenue. It is to find out whether the value you imagine is anywhere near the value the market assigns.
// You do not need this working to test pricing.
// You need this *visible* to test whether the number scares people off.
const tiers = [
{ name: "Solo", price: 0, limit: 20 },
{ name: "Pro", price: 12, limit: 500 },
{ name: "Team", price: 49, limit: Infinity },
];
If everyone clicks the free tier and nobody even hovers over Pro, you have learned something for the cost of three <button> elements.
The reason pricing matters this early is that it turns a vague hope into a number you can stare at. A handful of paying customers makes the math concrete and forces you to be honest about what counts as traction:
The slider is small numbers on purpose, because small numbers are where real validation actually starts for almost every project.
As a working student at BMW and an M.Sc. CS student at LMU Munich, the first pricing page I ever shipped taught me more in a week than a semester of theory. Here is what each line of that scrappy tier array was actually doing.
const tiers = [
{ name: "Free", price: 0, cap: "100 runs" },
{ name: "Pro", price: 19, cap: "unlimited" },
{ name: "Team", price: 49, cap: "5 seats" },
];A free taste, a clear default, and a bigger option. More tiers do not lift revenue, they stall the decision. Make the middle one the obvious pick.
Annotating it like this made the tradeoffs impossible to ignore, and it forced me to defend every number before a single user saw it.
Why a paid signal beats ten thousand free signups
I used to feel good about a graph going up: signups, waitlist emails, stars on a repo. None of it ever paid for the hosting. A free signup is the cheapest thing a person can give you, which is exactly why it tells you almost nothing. They liked a button for a second. The moment money enters, the question stops being "is this neat" and becomes "is this worth more to me than the price," and that is the only question a business runs on.
The milestone that actually changed how I think was the first real dollar. Not the first thousand, the first one. The day one stranger paid me for something I made, the project stopped being a hobby in my head and became a thing with an obligation attached. One paying user who renews tells you more than a thousand free accounts that never come back, because renewal is the rarest and most honest signal there is.
Put the two kinds of numbers side by side and the difference stops being abstract very quickly:
// 4,200 signups
// 18k page views
// 900 newsletter subscribersThe left column is a nice screenshot. The right column is a business. Only one of them survives the month you lose motivation.
I kept the left column on a wall for one of my dead projects, and it lied to me for months while the right column sat empty.
The hard part is charging before you feel ready, and you will never feel ready. The product will always look unfinished to the person who built it. So I now put a price up while it still embarrasses me a little, do the concierge work by hand if I have to, and let the willingness to pay decide whether I keep going. Charging early is not greedy. It is the fastest way to find out if you are building a business or just keeping yourself busy inside the editor.
Why vanity metrics lie, and what to track instead
Vanity metrics mislead because they only ever go up and they never ask anything of the person being counted. A signup, a page view, a follower: each one costs the human nothing, so each one tells you nothing about whether they value the thing. The number climbs, the graph looks like progress, and you can stare at it for months without learning a single fact about your business. Worse, it feels like validation, which is exactly how it buys your attention away from the questions that matter.
The numbers that actually signal validation are the ones that cost the person something. Did they pay. Did they come back. Did they renew. Paid plus retained is the whole game: a customer who hands over money and then keeps doing it has voted twice, and that vote is the only one reality honors. Everything else is noise dressed as a trend.
Track the right ones from the start, even at tiny scale. I log paying customers, MRR, and renewals by hand in a spreadsheet for the first dozen users, plus one retention query for "did they come back." Boring, small, and honest beats a dashboard full of numbers that only flatter you.
Instrument from the first user, not the hundredth
This is where my actual skill set as a developer earns its keep, and where most validation advice goes quiet. You cannot validate what you cannot see. From the very first real user, you need to know what happened, not what people tell you happened, because what people tell you is a story and the logs are the truth.
On my projects I lean on lightweight structured logging and a small admin analytics view rather than bolting on a heavy product-analytics suite for ten users. I want to answer concrete questions: did the user complete the core action, or drop at step two? How many came back the next day? The retention question is the one that actually separates a real SaaS from a thing people tried once. A product everyone signs up for and nobody returns to is not validated; it is a demo with good marketing.
// One line per meaningful event. Boring, queryable, honest.
log({ event: "core_action.completed", userId, ms: Date.now() - start });
Keep it cheap and keep it queryable. The point is not a beautiful dashboard. The point is being able to ask "where did people stop caring" and get a real answer.
A concrete gotcha I paid for: the first time I instrumented AiCal, I logged "signup" and "core_action.completed" but forgot to log the steps in between. So when day-two retention came in at roughly 30 percent, I had a depressing number and no idea why. I could see that 7 of the first 10 users never came back, but the logs could not tell me whether they failed to forward an email, forwarded one that parsed wrong, or simply forgot the tool existed. I had to add a tiny event at every transition, "email.received", "parse.succeeded", "event.created", and wait for the next cohort before the funnel meant anything. That cost me a full week of blindness for the sake of three log lines I was too lazy to write up front. The rule I follow now: instrument every step a user can drop at, not just the entrance and the exit, because the gap between them is the only place the answer lives.
The other thing I underestimated was how little tooling this needs at ten users. I reached for a hosted analytics product on an early project and spent more time wiring it up than I ever spent reading it. A single Postgres table with event, userId, and ts, queried with the same Drizzle setup I describe in the ORM I reach for, answered every retention question I actually had. At small scale, boring and yours beats powerful and rented, because you can change the question in a SQL line instead of a vendor dashboard.
The distribution problem nobody warns developers about
There is a failure mode specific to developers that deserves its own section, because it killed more of my early projects than any technical issue: building something genuinely good that nobody ever finds out exists. We are trained to believe that quality is the hard part, so we pour everything into the product and treat getting it in front of people as an afterthought, something we will figure out "once it's ready." It is never ready, and the figuring-out never happens.
The uncomfortable truth is that distribution is a harder problem than the build, and it does not yield to the skills that make you good at the build. You cannot refactor your way to users. The validation playbook has to include the distribution test from the start: do you have any plausible, repeatable way to reach the people with this pain? If the only answer is "I'll post it once and hope," that is not a channel, it is a wish. A mediocre product with a real channel beats a brilliant one with none, every single time, and pretending otherwise is the most expensive comfort in software.
When I finally took distribution seriously on one project, it looked less like a launch and more like a slow, deliberate sequence of small bets. Here is roughly how that played out, step by step, so you can see that none of it was clever or magical.
Before building anything I listed the exact places people with this pain already gather: two subreddits, one Discord, a niche newsletter. If a problem has no such place, that is a result, not a detail to figure out later.
Notice that the build never appears in that sequence until users already existed. This is also why the concierge and conversation stages matter beyond their direct signal. The five people you talk to are not just data; they are the first thread of distribution. If you cannot find even five people to have the conversation, you have learned that reaching this audience is hard, which is itself a validation result you needed before you spent three months building.
The build-versus-validate ratio
Here is a concrete discipline I impose on myself now. In the early life of a project, I track roughly how much time goes into building versus into validating, and I get suspicious when the ratio tips too far toward building. As a developer my natural ratio is something like ninety-to-ten in favor of code, because code is comfortable and talking to strangers about whether they would pay me is not. That comfort is precisely the bias the ratio is meant to correct.
The fix is not to stop coding. It is to make validation a scheduled, non-skippable part of the week rather than a thing I will get to eventually. One conversation, one look at the retention numbers, one honest review of whether the kill criteria have been hit. Treating validation as a recurring obligation, the same way I treat the test suite, is what keeps a project from quietly becoming a thing I build for myself forever while telling myself it is a business.
// The retention question, in its crudest honest form.
// Of everyone who did the core action once, how many came back?
const returned = await db
.select({ userId: events.userId })
.from(events)
.where(and(eq(events.name, "core_action.completed"), gt(events.day, day1)));
If that number is near zero, no amount of additional building will save the project, and continuing to build is just an elaborate way of avoiding the answer.
Here's the playbook as a thing you can actually run against your current idea. Tick what you've genuinely done, not what you intend to do, and notice how many are real signals versus comfortable building:
Talking to strangers is the skill, not the code
Here is the part of validation I am genuinely bad at, and I suspect most developers are too. The hard work is not the landing page or the concierge MVP. It is opening a conversation with a person I do not know and asking them about a problem in their life. The code I can do in my sleep. The talking is where I flinch.
I have started to notice that my urge to build is often just avoidance dressed up as progress. When I do not want to message ten strangers, I suddenly find an irresistible reason to refactor the auth layer or pick a nicer font. It feels productive, it produces commits, and it lets me stay inside the editor where I am comfortable and in control. Nobody can reject a function the way a real person can reject your idea to your face. So I hide in the thing I am good at, and I call it work.
The fix is not motivation, it is a script that removes the improvisation. I never pitch. I ask one question: tell me about the last specific time you ran into this. Not "would you use a tool that," which only collects polite lies, but a real memory with a date attached. If they cannot remember a recent moment, the pain is theoretical and I should stop building. If they get animated and start telling me what they tried instead, that is the signal. I shut up and let them talk.
And there is a quieter test buried in all of this. If I cannot find five real people to even have this conversation with, that is information in itself. It means I have no path to the people who would pay, no community I actually belong to, no way to reach a market. The ability to reach five strangers is not a precondition for validation. It is the first thing being validated.
Knowing when to kill it
The hardest part of this whole playbook is the part where you stop. Sunk cost is a vicious force, especially when the code is genuinely good. I have killed projects I was proud of, and the thing that made it possible was deciding the kill criteria in advance, while I was still objective, before I was emotionally married to the code.
Set them up front. If the landing page converts under some threshold you chose beforehand, stop. If the concierge users do not come back, stop. If you cannot get five people into a conversation about the pain, the pain is not real enough, stop. Writing these down before you start is the only way to make the decision with your earlier, clearer head instead of your later, attached one.
And killing a project is not failure. It is the validation working. Every project I shut down early bought me the time to build the next one, and the willingness to let go is the actual difference between someone who ships many things and someone who polishes one dead idea forever.
The validation steps I used to skip, and regretted
There is a gap between knowing this playbook and actually running it, and for years I lived in that gap. I will list the steps I skipped most, because the skipping is where the regret lives.
I used to skip talking to users before building. It felt slow and a little humiliating, and the editor was right there. So I built first and asked questions later, which meant I learned what was wrong with my idea after I had already spent the weekends I cannot get back. Talking first is not a delay. It is the cheapest version of the lesson I was going to learn anyway.
I used to charge later than I should have, always. Putting a number on something I made still feels presumptuous, like I have not earned the right. But every time I waited until it felt comfortable, I was really just postponing the only question that mattered, and collecting nice feedback in the meantime to feel better about the wait. Charging earlier than comfortable is the point, not a side effect.
I used to mistake encouragement for demand. Friends and people on the internet are kind, and "this is cool, you should build it" lands like validation when it is nothing of the sort. Encouragement costs the giver nothing, so it proves nothing. I had to train myself to hear a compliment and feel almost nothing, to wait for the email address or the card instead. This connects to something I wrote about how to pick project ideas that ship: the wrong signal early sends you down the wrong road for months.
And I used to keep features nobody used. I would build a thing, watch the logs show that not one person touched it, and keep it anyway because deleting my own work hurt. As a working student at BMW and an M.Sc. CS student at LMU Munich, my time is the scarcest thing I have, and a feature nobody uses is a maintenance tax I pay forever for a decision I was too attached to reverse. Now I cut them, and the project breathes easier every time.
The honest summary
Turning a side project into a SaaS is mostly an exercise in resisting your own instincts. The instinct to build before you've checked, to polish before you've sold, to add features before you've kept a single user coming back. The playbook is just a set of forced pauses that make you collect cheap, honest signals before you spend expensive, irreversible time.
You can see the projects this thinking has been applied to, the surviving ones and the lessons from the rest, on /#projects, and I write up the post-mortems as I go on /blog. If you want the one-sentence version: build the smallest thing that produces a real signal, look at the signal honestly, and be willing to walk away. Everything else is just coding, and coding was never the hard part.
Validation passed. Where to next?
Pick the next problem you want solved.
Whichever you pick next, the habit is the same one I lean on: test demand before you fall for the idea. If you want to compare notes, here is where I am.