What if an onboarding paid attention to you?

What does it feel like to be onboarded by something that's actually paying attention?

Something more like a conversation a friend would have with you. That was the bar I wanted to hit.

The LLM onboarding at Glassdoor is nine phases of conversation between the assistant and a new user. The content design and prompt work are my responsibility. This is a look at a slice of that ongoing work: aka improving how the whole system listens.

Okay, so the onboarding was, by technical measures, working (most of the time). LLM nondeterministic tendencies and all. Users completed it. They reached the first job match.

But anyone who actually used it could feel the thing was running on rails.

Early users had been telling us the chat felt transactional, more form than conversation. The diagnosis was half-right. The intro needed work.

But the failure mode wasn't "the user doesn't know what Door is." It was: the assistant doesn't feel like it's listening overall.

I had a choice. Do the original request. Clean content design job. Fix the copy. Update the prompt.

Or push back and argue for something bigger. Fix the moment.

So I did.

PM agreed. Which mattered more than the rewrites, honestly.


Before we go any further, a quick overview might be valuable.

Onboarding is a state machine. Each phase is a YAML block with its own prompt, its own tool list, and its own required fields. The user moves through them in rank order. Each phase knows what it owns and what to ask for next.

Each phase runs two streams.

The primary stream reads the user's message, decides which tool to call (save_profile, confirm_resume_step, generate_resume_card...), saves what the user just shared, and writes a brief acknowledgement. The acknowledgment is grounded in what was just captured. It does not ask the next question.

The follow-up stream runs immediately after. It doesn't call tools. Its only job is to ask the next phase's question and to feel like a continuation of the acknowledgement, not a hard cut.

Both streams inherit from a shared persona block at the top of the YAML file (universal tone rules, response shapes, anti-patterns, etc). Every phase references it. Nothing repeats. When something needs to apply everywhere, it lives in persona.

So when I say "I rewrote the followUpPrompt blocks for both phases," those are the prompt strings the follow-up stream reads. When I talk about an "ack" or "response," that's what the primary stream emits.

I didn’t define this architecture, BE did. I just inherited it. That's the box the work lives in.


First move was to well… move desired job title (CAREER_GOAL) up and Current job title (EMPLOYER) down.

Why do that you say? Also, on behalf of my engineers also questioning it.

Well… I argued the ordering was part of the problem.

Aspiration before logistics changes the emotional framing. When you ask “what are you reaching for?” before “where do you currently work?” it reframes onboarding as a partnership instead of an intake form.

Makes it more hero’s journey, less checklist.

BEFORE:  EMPLOYER → CAREER_GOAL → LOCATION → ... (logistics first)

AFTER:   CAREER_GOAL → EMPLOYER → LOCATION → ... (aspiration first)

Next, was updating the desired job title framing altogether. It’s new home changed it’s meaning. For the better.

Most onboarding systems accidentally reduce career movement into a binary. Stay or leave. But real career shifts usually fall into three buckets. Stay where you are, step up, or pivot sideways into something new.

┌──────────────────────────────────────┬──────────────────────────────────────┐
│ BEFORE                               │ AFTER                                │
├──────────────────────────────────────┼──────────────────────────────────────┤
│                                      │                                      │
│  ╭────────────────────────────╮      │  ╭────────────────────────────╮      │
│  │ "Do you want to keep       │      │  │ "You're a Senior PM.       │      │
│  │  going as a [title], step  │      │  │  What's pulling you next?  │      │
│  │  up into something more    │      │  │  More of this, something   │      │
│  │  senior, or shift into a   │      │  │  bigger?"                  │      │
│  ╰────────────────────────────╯      │  ╰────────────────────────────╯      │
│  22 words. One sentence. Mouthful.   │  16 words. Three sentences.          │
│                                      │  Conversation.                       │
│                                      │                                      │
├──────────────────────────────────────┼──────────────────────────────────────┤
│                                      │                                      │
│  followUpPrompt: |                   │  followUpPrompt: |                   │
│    Ask about their career direction  │    Ask about their career direction: │
│    — what kind of role they're       │    the role they want next.          │
│    targeting next. Be curious. If    │    One question, 1-2 sentences.      │
│    the collected profile has a       │                                      │
│    currentJobTitle, reference it     │    If currentJobTitle is in          │
│    to frame the question and         │    collected, lead with the title,   │
│    surface all three directions      │    then the three directions: stay   │
│    the user could go: staying at     │    at the same level, step up into   │
│    the same level, stepping up       │    something more senior, or pivot   │
│    into something more senior, or    │    into a different kind of role.    │
│    pivoting into a different kind    │    Do NOT frame it as binary "stay   │
│    of role. Do NOT frame the         │    vs change" because that hides     │
│    question as only "stay vs         │    the level-up option. Examples:    │
│    change" — that hides the          │    - "You're a [title]. What's       │
│    level-up option. E.g., "Do you    │       pulling you next? More of      │
│    want to keep going as a           │       this, something bigger?"       │
│    more senior, or shift into a      │    - "[Title] at [employer]. Where   │
│    different kind of role?" Adapt    │       are you trying to land?        │
│    the phrasing naturally.           │       Deeper in, up a level, or      │
│    One question, 1-2 sentences.      │       somewhere new?"                │
│                                      │                                      │
│                                      │    If currentJobTitle is NOT in      │
│                                      │    collected, ask openly. Do NOT     │
│                                      │    mention "more of the same" or     │
│                                      │    any stay-vs-change framing.       │
│                                      │    Examples:                         │
│                                      │    - "Tell me what you do. Where     │
│                                      │       are you trying to take it?"    │
│                                      │    - "What role are you building     │
│                                      │       toward?"                       │
│                                      │                                      │
└──────────────────────────────────────┴──────────────────────────────────────┘

But staging caught something evals couldn't. Double-personalization. The primary stream's acknowledgement landed cleanly:

"Senior PM at Meta tells me you can run at serious platform scale. Director feels like a clean next chapter."

But the follow-up stream would then open with:

"Senior PM at Meta is a strong foundation. So where are you based right now?"

Same role and employer, said twice in a row.

Luckily it was a single edit to FOLLOW_UP_TONE in the follow-up stream's prompt:

  // OnboardingFollowUpStreamService.kt

  const val FOLLOW_UP_TONE = """
  ...
- Reference the user's just-saved input naturally.
+ NEVER restate, summarize, or echo back information that was
+ just confirmed. The user already saw the acknowledgement.
+ Open with a short transition phrase that bridges from the
+ ack WITHOUT echoing the saved field. Examples: "Since...",
+ "Given that...", "Now...", "So...".
  ...
  """

Once the structure was right, the content of the acknowledgment was still flat.

I identified three phases EMPLOYER, CAREER_GOAL, and COMPENSATION and gave two of them grounded beats that reflect something meaningful back instead of echoing the field.

The interesting design question lived in CAREER_GOAL.

The reasonable instinct would be give users a brief market signal for the desired title. Giving the user context about the role they want. Sounds good.

But there's no fetch_role_demand tool. And I won’t be getting one anytime soon.

So I reframed the question. Given we have the user's current role and the role they want next.

What's the most meaningful beat we can add to this moment?

Direction-naming. Stay, step up, pivot. The model has both data points. It can honestly name the move the user is making. The acknowledgment is for the user, not the system.

They just told us where they want to go. The warmest thing we can do is reflect that move back to them as a story they recognize as their own. That helps build connection.

A clean step-up earns real recognition. A stretch step-up earns honest language, not false reassurance. That honesty build trust.

That's the context the market signal was trying to give them, grounded now in what they actually said, not in what the model would have invented.

But all this did not come without learnings…

The first surfaced in eval CI. The RESUME_UPLOAD acknowledgement kept merging with the next phase's question:

"Got it, resume confirmed. Let's dive into your job preferences. What job title are you aiming for?"

The prompt already said "DO NOT ASK THE NEXT ONBOARDING QUESTION." The rule was there.

Why you no listen robot…

Turn out a negative instruction with no examples has no teeth.

So I put the model's own failure trace into the prompt as a Bad example, almost verbatim. The model recognized its own failure shape and stopped reproducing it. Yay.

The second surfaced a few weeks later. I came back to the prompt for a different ticket. Different phase, different problem. But I scanned the EMPLOYER block on the way through, because it was the work I was proudest of.

The micro-reward pattern and example I shipped looked like this:

"Staff Engineer at Amazon. That shows you can handle real scope and complexity. So Principal does seem like a natural next move from there."

Three sentences. Confirm-the-field, interpret-the-capability, bridge-to-the-goal.

But on a reread, my content designer brain broke a little bit. The first sentence was dead weight.

"Staff Engineer at Amazon." is a weak confirmation beat. A form receipt before the warmth arrives. I'd written it that way because the structure made sense. Confirm, then interpret, then bridge.

I'd built the rule first and the sentence second. The reader was meant to feel warmth, but it was in sentence two. They were getting a form receipt in sentence one.

So now the version living in the prompt today fuses the naming into the interpretation:

"Senior PM at Meta tells me you can run at serious platform scale. Director feels like a clean next chapter."

Same principles, tighter shape. My original line… now lives in the what not to do part of the prompt 🤣

Good: "Senior PM at Meta tells me you can run at serious platform scale. Director feels like a clean next chapter."

Bad: "Staff Engineer at Amazon. That shows you can handle real scope. So Principal does seem like a natural next move." (stubby confirm-beat; should be fused into one sentence)

Iteration log (selected moments)

I also left a bigger restructure on the runway. A doc proposing every phase do exactly one job (not how engineering originally set it up). Backed by the prompts already behaving that way under CHECK FIRST.

It needed backend changes I couldn't fit in a one-week ticket, so I wrote it up and left it to pick up sometime after beta. And the work isn't done. I’m still fighting for it.

Not because there are more phases to ship. Because the case for letting an LLM drive a high-stakes flow isn't settled inside the company. Some folks would rather onboarding be deterministic. An LLM doesn't work that way. Every time the model surprises you, the conversation reopens I my opinion.

Here's what that conversation looks like in motion for now.

Next
Next

The (ridiculously) long road to a living content system