Bohdan Kryvenko UX Writer / Content Designer
Senior / Lead · Content Design

Copy as a governed system, not a queue of tickets.

Good UX writing cleans up after design. Great UX writing shapes it before production.

I build the frameworks, pattern libraries, and tooling that put it there — voice consistent and dark patterns out, across iOS, Android, and web.

OBRIO (Genesis) · Nebula & NebulaX UX Writer & Content Designer · 2025–26
Flagship · System

Copy governance systemA closed loop from Figma to a living canon — two tools with a self-updating source of truth between them.

The problem

The product ships copy across three platforms from one Figma source that doubles as dev handoff. At ~1,600 strings, three things kept breaking:

  • Drift. The same string worded differently across platforms; regressions reintroduced in new versions.
  • Canon rot. The voice & tone guide lived in Notion as a doc nobody re-read; review decisions never made it back in.
  • Bottleneck. Every “is this on-brand?” routed through one writer. Copy QA didn’t scale past one person.

What I built

Deterministic · scale

copy-parityNode.js · Figma REST API

extract pair lint report comment
  • Pulls every text node, pairs equivalents across platforms, lints each against rules.yaml — the copy canon as code, where adding a rule means one regex in a config file.
  • Posts findings back onto Figma frames as node-anchored comments. Read + comment only — never mutates the canvas.
  • Catches the machine-checkable class across 1,600+ strings in one pass: casing, broken tokens, drift, banned constructions.
Judgment · on-demand

ux-copy-auditAI skill · Notion via MCP

screenshot in check canon before/after out
  • Anyone — PM, designer, writer — drops a screenshot or Figma frame. No briefing; the skill carries the context.
  • Reads the live authority stack from Notion: the UX Copy Patterns DB (Status = Active), tone-of-voice pillars, naming rules — then judges the screen against canon.
  • Returns a before/after with rationale. Approved calls graduate back into the Patterns DB and land in Field Notes with per-writer attribution.
How the loop closes
1Figma / screenshotany surface
2Auditregex + judgment
3Before / after+ rationale
4Human approvalwriter / PM
5Graduate to canonPatterns DB
↩ feedback Every approved decision sharpens the canon — so the next audit starts smarter. The canon lives as the system’s runtime state, kept current by every audit. And it’s team-scoped: multiple writers feed and draw from the same source.

Impact

1,600+
copy changes triaged into a Notion review pipeline
Team
standard for UX-copy audit — writer capacity removed as the bottleneck
30+
high-stakes surfaces rewritten — paywall family, error taxonomy, onboarding, the chatbot funnel
I treat copy as an owned system — methodology, canon-as-code, tooling, and a feedback loop that keeps voice consistent at team scale.

Next: merge the deterministic and judgment halves into one LLM-in-the-loop linter.

A slice of the parity matrix — what pair + lint emit
CanoniOSAndroidWebStatus
Not nowNot nowNot nowNot nowidentical
See psychicsSee psychicSee psychicsSee psychicsdrift
Chat longer and saveChat longer & SaveChat longer and saveChat longer and savestyle
Recommended for youRecomended for YouRecomended for You— missingtypo · gap
[Name][Name][ExpertName][Name]token

Caught as a pattern, fixed once

The payoff, in one number — an unverifiable “most accurate” accuracy claim had quietly shipped across the product:

81screens carried it
6surfaces
1ruling in the canon
Chat flows33
Elite Hub17
Audio Calls16
Membership9
Catalogue4
Chat Next2

One claim, shipped to 81 screens. Plain text-similarity linting misses this class — it isn’t visible “drift,” it’s a canon issue. The system surfaced every instance; one ruling fixed them all.

Appendix · Audit log

What was live in productionBefore the audit system existed — now caught automatically. The pile the linter clears so I can spend judgment on the hard calls.

− Process message+ Incorrect email or password. Please try again.
Placeholder text shipped as the live login error.
− You need >30 credits on the balance+ You need more than 30 credits in your balance
A math symbol in reader-facing paywall copy.
− Need an account? Sign up+ Already have an account? Log in
Backwards — the link sat on the Sign Up screen itself.
− Recomended for You+ Recommended for you
Shipped identically on iOS + Android v1.
− See psychic+ See psychics
A v2 regression — a singular CTA led to a list of many.
− You can archive unnecessary chats!+ You can archive chats you don't need. Swipe left to try it.
Calling a user’s own chats “unnecessary” works against an empathetic voice.
Case study
Case study · Trust

Trust by designResonance without manipulation — in a product built on spiritual hope, for people who arrive at vulnerable moments.

People come to a psychic-chat product mid-breakup, in grief, or facing a hard decision — and the business monetizes that moment. The frictionless copy manipulates: a guaranteed soulmate, a countdown, a struck-through price. I held the line between resonance and manipulation on every surface a user meets — the conversation, the promos, and the money flow — and encoded it into the system so the line holds without me.

Part 1 · In the conversation
Auditing a chatbot intake funnel where the whole conversation is the unit of analysis

The brief was “here’s the flow, fix the copy” — a ~220-string chatbot funnel steering new users toward a trial psychic chat. I reframed the problem before touching a word.

A line can be perfect alone and broken in sequence — “Got it.” landing four times in a row. So the unit of audit isn’t the string. It’s the assembled dialogue.
Path-level finding · assembled transcript
answers genderGot it.
answers statusGot it.
taps “Other”Got it.
answers focusGot it — this is an internal journey.
Each line is fine on its own. Four “Got it” openers in a row read like a machine — a rhythm only visible once you assemble the path. You only catch it by reading the assembled path. So the fix below changes what each reaction is doing, screen by screen.

I ran two passes: a string-level pass, and a path-level pass — programmatically assembling seven end-to-end transcripts and reading them as real conversations for rhythm, repetition, and cumulative tone. The audit framework had no conversational lenses, so I built five and added them as a reusable pattern family:

PersonaTurn designSequence rhythmPromise vs. intentBot → human handoff
One of the most common and most painful fears. You're not alone in it.
+So many people secretly carry "am I enough" — you're not alone in it.
Vulnerable-moment reaction. “Common” quietly devalues the very fear the user just admitted. Normalize without diminishing.
Your psychic will show you where closure actually comes from.
+Waiting for closure that never came — that ache is real. It deserves a real answer.
Outcome guarantee → process. Process verbs (read, tune into, look at) are fine; guaranteeing an outcome is a hard voice prohibition — and a compliance risk for an entertainment-only service.
The future feels uncertain right now. I want to understand why.
POV bug (flagged 🔴). The bot speaks in the user’s own voice, then answers itself. Persona integrity: the assistant prepares the question, the human psychic reads.

The lead moment — give each line a job

After the PM signed off, the real question surfaced: how are these responses even formed? The whole corpus ran on one generator — mirror the user, weigh the feeling, bridge to the psychic — so the whole thing read like a catalog of captions. The assistant never responded — it evaluated. The fix wasn’t another polish pass. It was giving authors a palette of conversational jobs and a quota per path.

User taps: “I work hard but I’m invisible and overlooked”
Mirror

Being good at what you do and still not being seen — that chips away at you.

Presence

That’s an exhausting way to work. I’m glad you said it straight.

Normalize

More people carry this one than ever say it out loud.

Silence · bridge

Noted — word for word. This goes to your psychic as is.

One option, four different jobs. The rule: on a ~7-reaction path — 3 mirrors · 2 presence · 1 normalize · 1 bridge. A constitution for the copy — the rules authors write to.
44
changes shipped (13 critical / 31 refinements) across the funnel
7
end-to-end transcripts assembled to audit at the path level
0 → 1
“human” signal at the bot → person handoff (present in none of 232 replies)
Part 2 · On the promo surfaces
The same line, held across quiz, horoscope, and tarot promos

The conversational work set the principle; these show it applied at product breadth — keep the emotional pull, remove the certainty the product can’t honestly claim.

Quiz result screen, audited 1
Quiz — result screen
1
There’s a high chance that you’ve already met your soulmate from a past life. Those intense feelings you have are real soul nudges, not just daydreams.
+Some people describe feelings like this when they’ve crossed paths with someone important before.
Quiz result. A past-life soulmate reunion stated as fact → an honest observation. The hook survives; the claim doesn’t.
Tarot reading screen, audited 1 2
Tarot — daily insight
1
…This card confirms that you are on the right path… Your wishes have been heard.
+This card is an encouraging sign — keep moving forward with confidence.
Guaranteed outcome. “Confirms” and “wishes have been heard” are destiny language — a hard prohibition. Encouragement without the guarantee.
2
Get the best guidance and advice! …you can be sure to find it in your Tarot Reading.
+Want to go deeper? Chat with a psychic for guidance tailored to you.
Superlative + guarantee. “The best” and “you can be sure to find it” → an honest CTA. A shared template repeated across several tarot screens.
Same principle · other surfaces
✨ In 8 days, you won’t feel alone anymore. You’ll know someone special is just around the corner…
+After these 8 days, you’ll have a clearer sense of what you’re looking for — and the signs to recognize it when it shows up.
Interval reading. A manufactured emotional guarantee (“you won’t feel alone”) → a promise the product can actually keep.
Part 3 · In the money flow
Dark patterns, taken out of the paywall

In a money flow, the frictionless copy manipulates the choice. I took three patterns out — two the governance system now catches on its own, and one that needed a person to name.

I don’t want free week of usage — I want to cancel my subscription
+Cancel subscription
Confirmshaming, on the cancel button. The user had to narrate refusing a free week just to quit. Caught by the confirmshaming rule; the free-week offer stays as a separate button, minus the guilt.
Don’t miss out on a single perk!
+See everything your membership includes
Manufactured FOMO. “Don’t miss out” invents urgency where there’s no deadline. Caught by the urgency-scarcity rule → a plain, informative label.
Encoded once, enforced everywhere

Neither was spotted one screen at a time — confirmshaming and urgency-scarcity are rules I encoded in rules.yaml, so the linter flags every instance on every screen automatically.

The one no rule could catch

Some risks aren’t pattern-matchable — they need a person who knows the law.

$39.99
$9.99
!
Fictitious former price · 16 CFR §233.1

A struck-through “original” manufactures a discount — but there was no record of that higher price ever being the standing one. I flagged it as an FTC compliance risk and escalated it to PM and legal — the kind of judgment a linter can’t make. And I set Crowdin as the localization source of truth, so fixes propagate instead of drifting.

Illustration of the flagged pattern — not the product’s real pricing.

One line runs through all of it — the conversation, the promos, the paywall: a product built on hope can’t grow by spending users’ trust, and the honest choice should never be the harder one.

Let’s talk.

I’m after senior/lead content design where copy is owned as a system.