Code › tail-villain

From Developer Tool to Interview Platform

Removing software-interview assumptions to support interview practice across professions

During an IELTS Speaking session, Kovill asked about the trade-off in the user’s hometown development.

The question was not completely nonsensical. Any topic can involve trade-offs. But the wording exposed the real problem: Tail Villain still had software interview assumptions hidden under its new product language. The user was practicing an English speaking test, while the interviewer was still carrying habits from system design interviews.

I thought the big rename had already happened. Job descriptions had become goals. Resumes had become backgrounds. Database tables, API fields, DTOs, and UI labels had moved in that direction.

The product’s actual behavior had not fully moved with them.


The universal platform change started at the schema level.

The early product grew out of the interview format I knew best: software engineering. A user provides a job description and a resume, the system finds gaps, and the villain pushes on those gaps through technical questions. That is a useful flow, but it is too narrow for IELTS speaking, presentation practice, certification prep, or any other high-stakes evaluation where the user needs to explain and defend knowledge under pressure.

So the core entities became Goal and Background. A goal is what the user is preparing for. A background is the material or context they bring into that preparation. Roadmaps, interview sessions, and study sessions were reattached to those entities. The database migration used table renames to preserve data instead of recreating everything.

That was the visible part.

The harder part was that old assumptions were scattered across prompt templates, i18n keys, helper functions, test fixtures, persona definitions, and UI badges. A screen can say Goal while the LLM still receives instructions about technical judgment, role fit, JD alignment, or trade-offs. When that happens, the model follows the hidden prompt, not the new label.


Screenshots exposed the layers one by one.

One showed Kovill introducing the session with background and technical judgment. That came from a focus-area description. Another showed full mock interview wording in an IELTS context. That came from a session-kind description. Another showed an Integrated badge on a single-topic IELTS session, which meant a roadmap-only UI concept was leaking into a topic session.

Then the first step label still said Career pitch and role-fit narrative. That was hardcoded in the interview angle map.

Finally, Kovill asked the hometown trade-off question. The cause was a single opening directive: ask for one judgment or trade-off only.

That one line pulled a software-design habit into an English speaking session.

The fix was not to write more negative instructions. If the prompt says do not ask about trade-offs, the model still sees the concept of trade-offs. I removed the concept and replaced it with positive framing: short, direct, match the natural framing. Follow-up instructions moved from trade-offs, evidence, or execution detail to clarity, concrete evidence, or domain-relevant detail.

Persona names needed the same discipline. English sessions should not produce lines like I am Socrates, or as some call me, with the localized Korean name attached. The prompt now receives one persona name based on the response language, not every available label.


The interview structure also had to become dynamic.

On May 13, roadmap interview angles started being generated during roadmap analysis and stored in the analysis summary. The point was simple: an IELTS roadmap and a backend architecture roadmap should not share the same interview steps.

The next day, evaluation criteria moved the same way. The previous fallback still carried software-interview concepts such as role fit and JD alignment. Rewording those into neutral phrases would have reduced the obvious bug, but it would have kept the wrong structure: a fixed fallback pretending to be universal.

I added roadmapEvaluationCriteria to the analysis response schema, then passed those criteria into the roadmap interview module when available. If no criteria exist, the system passes an empty list. Before removing the fallback, I checked both sides: the AI prompt handles an empty criteria block, and the frontend does not render a fake list.

That mattered. In a general-purpose product, an empty state is safer than a wrong criterion. It is better to omit evaluation criteria than to judge an IELTS session by a repackaged software interview rubric.


The UI needed the same boundary cleanup.

Locked checkpoints now show the actual upcoming question title in a muted state instead of only a lock icon and Waiting. Users should have a sense of the path ahead. The Start Interview badge disappears once the conversation begins. Focus-area badges are scoped to roadmap sessions instead of appearing everywhere.

Completion state also needed a hard boundary. The chat input was initially disabled after completion, but the empty input container still remained at the bottom of the chat room. That looked like the user could keep typing. The render prop itself now disappears when the session is no longer active, and the wrapper only renders when an input exists.

Timer display had a similar issue. A completed session could show both a 0:00 countdown and an elapsed duration badge. The countdown now belongs only to active sessions; completed sessions show duration. The helper that formats duration stopped returning hardcoded Korean strings and moved through the i18n path at the call site.

The URL import feature fit this broader shift too. Goals and backgrounds became things users could paste from real-world sources, not just type manually. The server tries public ATS APIs first, then a constrained HTML extraction path, with SSRF checks, content limits, and graceful errors. No LLM call is needed to preview a URL. A plain HTTP boundary is enough for that job.


The main lesson was that terminology is infrastructure.

Renaming a label is easy. Moving a product assumption is not. The database, DTOs, shared schemas, prompts, personas, i18n, tests, admin views, session badges, and evaluation criteria all have to agree about what the product is.

Tail Villain did not stop supporting software interviews. Software interviews became one goal among many. That is a different architecture. It means the product cannot smuggle software-specific defaults into generic places and hope the UI copy hides them.

A domain-agnostic product is not created by using broader words.

It is created by removing hidden assumptions from the places that generate behavior: schemas, prompts, criteria, and state boundaries. The awkward trade-off question was a small bug, but it pointed directly at the work that still needed to be done.