Code › tail-villain
Launching the Tail Villain Beta
The first Tail Villain beta launch, from production deployment failures to preparing the product for its first users
On May 1, I prepared Tail Villain for its first launch. The product already worked locally, but Vercel could not find the backend entrypoint. Once that was fixed, the server hit a module-format conflict. Clicking deploy and actually launching a product turned out to be different jobs.
The core journey already worked. A user could create a roadmap, study a topic, run a mock interview, and receive a report. Moving beyond my own development environment required more than checking those features again. The system needed to identify who was making each request, stop expensive AI operations from running without limits, and verify that a new account belonged to a real email address.
Instead of adding another feature, I spent the final two days preparing the product to stay online for other people.
What had to be protected before launch
Tail Villain calls AI throughout roadmap generation, interview turns, Study responses, and evaluation. A single request costs more than an ordinary database read, so unbounded usage turns cost control and security into the same problem.
An IP-based rate limiter initially looked sufficient. It was simple, but it treated everyone behind an office, school, or other shared network as one user. One person’s usage could block someone else, and authenticated behavior should not be governed by exactly the same identity as an anonymous request.
I changed the limiter to track authenticated requests by user ID and fall back to IP only for anonymous traffic. Endpoints were divided into short, medium, long, and heavy policies rather than sharing one arbitrary limit. Roadmap generation and interview messages received stricter treatment because every call directly consumed model budget.
That exposed a guard-ordering problem. User-aware throttling requires authentication to finish first, but a global throttling guard cannot see a user ID if it executes before the authentication guard. I reorganized the request flow around distinct layers: IP protection, authentication, email verification, and then user and credit checks.
I also stopped exposing internal database IDs as the public language of the product. URLs and API responses moved to prefixed public IDs that identify the resource type without leaking raw primary keys. At the same time, centralized LLM usage logging made model consumption visible by operation. Rate limits and credit rules need observed costs, not guesses.
Email verification became an operational feature
Creating an account was not enough to grant immediate access to every sensitive flow. I added OTP-based email verification and blocked unverified users from protected endpoints when verification was required.
The interface also needed to explain that state. A verification banner and modal gave users a direct next step instead of leaving them with an unexplained failed request. The Raven Message presentation matched Tail Villain’s visual language, but the important part was making the account state and required action clear.
Email verification could be enabled or disabled through an admin setting. During an early beta, operational policies need to change without requiring another code deployment every time. That control surface gave me a way to respond while the first users entered the system.
Local development started returning 429
After the stricter limits were in place, the local frontend began receiving 429 Too Many Requests responses. The application was not actually flooding the API. React Strict Mode was running an effect twice in development, causing the same request to arrive twice within a short window.
I excluded localhost traffic from production throttling rules. It looked like a development-only inconvenience, but the failure exposed how sensitive the limits were before launch. A rule that is too loose does not protect anything. A rule that is too strict blocks legitimate product behavior.
The server would not start on Vercel
The security work did not make deployment automatic. The monorepo built locally, but Vercel could not find the backend entrypoint.
The NestJS build was producing dist/src/main.js instead of placing main.js at the dist root expected by Vercel. There was also a nearly invisible trailing space in the Vercel Output Directory setting. I corrected the tsconfig rootDir and include and exclude settings, then removed the stray space from the deployment configuration.
Build order caused another problem. Shared types and the Prisma client had to exist before the frontend and backend builds began. Rather than distributing long command sequences across individual project settings, I centralized generation and build orchestration in the root package scripts. A clean deployment environment could no longer depend on artifacts left behind by a previous local build.
Once the entrypoint worked, nanoid failed with ERR_REQUIRE_ESM. The installed nanoid v5 release was ESM-only, while the compiled NestJS server still loaded dependencies through CommonJS. Changing the entire module strategy immediately before launch would have expanded the risk, so I moved to the CommonJS-compatible nanoid v3.3.8 release.
Fixing one deployment failure exposed the next.
From v1.0.0 to v1.0.2
Tail Villain v1.0.0 reached production after those changes, followed by v1.0.1 and v1.0.2 as I corrected deployment configuration and module compatibility. I also aligned the local PostgreSQL version with the PostgreSQL 17 environment used by Neon so that authentication, sessions, credits, and user data were less likely to behave differently between development and production.
The first beta did not represent a finished product. It established the minimum conditions required to keep operating: user-aware throttling, email verification, public IDs, observable LLM usage, adjustable system settings, and a repeatable monorepo build.
A launch was not complete when a version number existed. The assumptions that worked locally had to survive production, failures needed to be diagnosable, and the product had to protect itself after users arrived.
That was how the Tail Villain beta began.