In 1997 psychologist Arthur Aron sat two strangers across from each other and handed them a list of 36 questions. They escalated: from "who would you want as a dinner guest" to "when did you last cry in front of another person." At the end — four minutes of looking into each other's eyes in silence. Several pairs grew close after the experiment. One got married.
Questions bring people closer. But only when two people answer and actually hear each other — not peek and adjust. I took that principle and built a product on it. Built it with AI — I'm a fractional AI consultant, and I build with the same tool I sell. But the architecture, the forks, and the production reality are carried by me, not the model. I designed it, shipped it to production, and I keep running it. The bot lives under the name Closer, works every day, and does exactly what it was made for: two people get to know each other more deeply.
This case isn't about "I made a bot." It's about how I ship an AI product to production with AI — and what starts AFTER launch. Because "after launch" is precisely what separates a working product from a folder on GitHub.
Blind mutual reveal — is the mechanic where both partners answer the same question separately, and the other's answer only opens once both have answered. Nobody sees the other's answer in advance, so nobody can adjust to it — each answers honestly. It's the core of the product and the main invariant around which everything else is built.
What is the bot for, and why does it work?
The goal is simple: a couple should know more about each other after the bot than before. Not "we played and forgot," but "oh, I didn't know you thought that."
That moment is no accident. It's built into the mechanic. The core is blind mutual reveal: both answer the same question separately, and the partner's answer only opens once both have answered. Nobody sees the other's answer in advance, so nobody can adjust to it. Each answers honestly — which is why the reveal lands.
The goal is met by architecture, not persuasion. That's the case.
What's inside the bot?
In short: pairing, sessions of questions with rising intimacy, a daily question, a "guess the answer" game, and streaks. Point by point:
- pairing by code or by a forwarded link: deep link → one tap → you're a pair
- sessions of 5 questions escalating in intimacy, 4 moods across 3 levels, 128 questions
- the "Passion" section opens only by mutual consent
- a question of the day, every day, with a smart nudge to whoever stays silent
- a "guess the answer" game: you answer first, then guess your partner
- streaks, a history of revealed answers, an admin panel with stats and broadcasts
What does the architecture look like?
The key decision — separate the clean logic from the Telegram shell. The whole substance of the product lives in modules with not a single Telegram import, and is therefore fully under tests. Telegram is a thin layer on top.
app/
clean logic (no Telegram, under tests)
pairing.py pairing, codes, parsing invites
reveal.py the reveal rule: visible only when both answered
sessions.py picking questions by rising intimacy
streaks.py streaks and their recalculation
daily.py scheduling and choosing a fresh question of the day
data
db.py SQLite layer, schema, migrations
Telegram shell (thin)
handlers/ start, session, question of the day, admin, router
runner.py assembly, registration, background schedulerWhy this way and not "everything in one file"? Because the product is about rules, and rules have to be checked. When the logic is separated from Telegram, tests run against it — and on any change the reveal, streaks, and question selection don't drift apart.
Which decisions actually made the product?
The case isn't "we made a bot." The case is in the decisions.
Blind reveal as an invariant
The partner's answer isn't visible until both have answered. One rule in one function, with the whole product built around it.
One active session per pair
So a pair doesn't open three conversations at once and get lost. A simple invariant that removes a whole class of bugs.
A database for real conditions
At first the data lived in an external cloud database. On the server it breathed unevenly: a query would take 0.3 seconds, then 14, then time out. I moved it to a local SQLite next to the bot — it became 0.0003 seconds. Lesson: reckon with real infrastructure, not the ideal kind.
Cutting the unnecessary
There was a temptation to add a "take turns on one phone" mode. I killed it: it blurs the core — blind reveal only works when each person has their own screen. Cut at the design stage, not finished as dead weight.
How does building from architecture differ from building from features?
Feature-first chases the count of capabilities and hopes one sticks. Architecture-first picks one core mechanic and bends everything else to serve it — Closer was built that way.
| Feature-first | Architecture-first (this build) | |
|---|---|---|
| Where the outcome comes from | Add more features, hope one sticks | One core mechanic, everything serves it |
| How scope is decided | Build whatever is askable | Cut what doesn't serve the core |
| The core logic | Tangled with the platform | Separated, under tests |
| Behavior at the edges | Patched bug by bug | Held by invariants |
| The builder's role | Coder of requests | The one who runs the system |
The product is shipped. This is where the work begins
"The bot is done" — someone else would say that here and close the project. The live test passed, the partner's answer opened instantly. I exhaled. Too soon.
A couple of weeks later I went into the live database — just to see how real conversations were going. And I saw it: one answer landed under the wrong question. Not "theoretically possible," but right there in the data. The picture came together fast: a new question of the day arrived, but the person answered the old one still sitting higher in the chat — and the answer went to the wrong place.
This isn't fixed with a patch. It's fixed with two decisions:
- The answer is tied to the question the person was actually shown — not to the "pair's current question." Telegram doesn't tell you what exactly is being answered, so I hold that binding myself.
- A half-finished question is no longer overwritten. If one answered and the other is silent, there's a locked answer living there, waiting for the second person. The scheduler used to be able to overwrite it with a new one. Now it leaves it alone.
And since I was in there — I dialed in the thing the product lives for, keeping two people engaged:
- a streak forgives one missed day: one random slip doesn't zero out what's accumulated
- the bot gently brings a gone-quiet pair back step by step, instead of nagging into the void
- the moment one answers, the other instantly gets "your partner answered, open it and see"
- a guess can be skipped — the round reveals without it
- when a guess matches the answer, that's its own little celebration in the reveal
It was 62 tests. Now it's 108.
This is exactly the difference between "shipped and forgot" and "running it." I found the bug — reading the live database by hand. Not a client whose bot "keeps mixing up the questions for some reason."
And here's the important part. The AI wrote the code, fast. But the AI won't go read the live database on its own and catch that one answer landed under the wrong question. It won't decide what to bind the answer to. It won't ship and verify on live data. Code is cheap now — anyone can do it. The expensive part is something else: getting it to actually work on real people. That's my job.
Does it actually work?
Yes. All in production. Deployed in Docker, local database, working every day. 108 automated tests green. Modular code with separated layers. The bug found in the field is closed, retention is dialed in, the changes are shipped and verified on live data.
What does this demonstrate?
This is a showcase of how I work. Not "pile on features," but:
- separate clean logic from the shell, so the substance of the product is under tests
- hold invariants instead of patching bugs one by one
- reckon with real infrastructure, not the ideal kind
- ship to production — and not abandon it there, but run it on live data
And tie all of it to the goal. The bot exists not for features, but to make two people feel warmer toward each other. Architecture here isn't the point — it works in service of closeness.
What do I offer?
I take your idea and bring it to a working AI product in production. And then I run it — because a product nobody runs quietly dies.
Who it's for
- Businesses with an audience — coaches, schools, communities, services. A bot that engages and retains people, not "one more chat they scroll once and forget."
- Founders — an MVP you're not ashamed to show an investor and a user. With architecture, tests, and in production — not "works on my machine."
How it works
- Turnkey build with AI — from idea to production, fast. I build with the same tool I sell. But I steer it, catch what it missed, and bring it to working: clean logic separated from the shell, covered by tests, deployed.
- Support on MRR — I don't disappear after launch. I watch the live data, find the holes before users do, and dial in what keeps people in the product.
Why this way. Half of "finished" products die not from bad code, but from being shipped and abandoned. A user finds the bug. Nobody measures retention. The feed goes quiet, and everyone pretends it was meant to be. I close exactly that gap: I ship to production and stay close while the product lives.
Closer isn't a quick demo cobbled together. It's a working product where you can see my whole approach: from blind reveal as an invariant to a bug caught in the live database by hand.
Have an idea that's ready for production?
Or a product you launched and abandoned? Write to me. I'll take a look and tell you straight: what's actually doable, and what's hot air.
