⟵ work ⟶ · Client work

I built the backend, AI workflows, migration and billing for UpLinked’s move from Bubble to Convex, turned it into serious high-tech software, together with the two very tech savvy founders

UpLinked interviews entrepreneurs about their work, helps turn their answers into LinkedIn posts, and schedules publication. Bart and Thierry had an existing Bubble product with customers and their writing already in it. Our four-person team rebuilt it on Convex; I owned the backend, agents, prompt system, migration and billing, and merged the team’s pull requests.

The uplinked.app front page: a large headline in Dutch, “Je bent goed in wat je doet. UpLinked zorgt dat je LinkedIn dat laat zien.”, a purple button to try fourteen days free, four avatars with “750+ ondernemers gebruiken UpLinked”, and below it a mock of the app’s planning calendar for September 2026.
The landing site, a Next.js app in the same monorepo.
The same front page on a phone: the headline stacked over five lines, the purple trial button, and the start of a section “Hoe het werkt: groei op LinkedIn in 4 stappen”.
The same page on a phone.
The app’s login screen at my.uplinked.app: a white card with e-mail and password fields, a purple sign-in button, Apple, Google and LinkedIn options, next to a wall of testimonial cards.
The app’s front door, on the address the Bubble app used to have.
The advies.uplinked.app funnel: a hand mirror emoji, a serif heading in Dutch announcing an AI-guided thought experiment, three paragraphs of explanation, fields for first name and company name, and a purple button “Start het experiment”.
The lead magnet: a thought experiment that scrapes your LinkedIn and your website and writes a report.

4 images — swipe or scroll sideways

Let the product team improve the prompts

The wording, model choices and post templates live in an editable system with drafts, versions and rollback. A change can reach the app within a minute without a code deployment.

The boundary is deliberate: people can change how an agent is instructed, while the structure of the tools it calls stays in code. Feedback on a generated post is tied to the prompt version that produced it, so we can investigate which change helped or hurt.

Keep the interview alive

An AI response can take long enough for someone to close a tab. I moved the work into background actions and let the interface subscribe to the saved conversation. Returning to the app brings the conversation back.

That introduced some less obvious failures. Duplicate starts produced two greetings; stalled responses left the typing indicator running indefinitely. I added an atomic check to claim each start and a timeout that offers a retry when a response gets stuck. The recovery matters as much as the successful answer.

Move the existing customers across

The migration brought 196,000 rows out of Bubble. My first approach tried to process too much data at once and ran out of memory. I split the import into small jobs, linked records to their old IDs and made each job safe to repeat.

Customers reactivated their accounts to find their profiles, posts and subscriptions in the rebuilt app. The first days exposed some missing knowledge data; I brought that across through the same repeatable import. The new app now runs at the address the Bubble version used.

Publishing needs one source of truth

A calendar bug made the consequence of inconsistent state very clear: moving a post changed its displayed date, but its old publishing job could still run. I brought the date and job changes into one operation and made stale jobs check their scheduled time before publishing. That keeps the calendar and the action it promises together.