Development Journal

Building a ten-year reunion

An online platform for a high school class ten years after graduation —the design reasoning, the participation data, and the things that broke.

It had been ten years since we graduated from high school, and I wanted to put together an online reunion. After talking it over with a few friends and with AI, I decided to use my time between jobs to practise building something with AI. The whole thing was built with Claude Code — the one exception was a performance problem near the end that I felt it wasn't solving well, so I threw that one at Codex to see what it would do.

TL;DR

Tap a section name to jump to it.

Ten-year surveyReplaced one open-ended prompt with a frame of ten questions each for past / present / future, to lower the bar for sharing. Answering any five cleared the first gate.38 of 71 people wrote something; 19 got to 10–19 questions; 3 answered all 30.Would fix: no rich text and no clickable links, so long answers were hard to read. "Mail this to yourself in ten years" got cut over email authorisation and doubts that the mail provider would still exist — people saved a PDF by hand instead.
Daily game — Three Truths and a LieA different classmate in the spotlight each day to create a reason to come back. No requirement to reveal your values, only a ban on cheap wordplay. Results revealed immediately, but deliberately without showing who picked what, so nobody was afraid to guess.35 questions, 458 answers submitted, 43.2% average participation.Would fix: questions didn't link to that day's subject profile; subjects weren't asked to write down what the lie actually was; almost nobody edited their question later, which says you should get people to their best version up front.
Ten-year dreamsWrite down one dream for the next ten years; anyone else can second it, extending the connection past the end of the event.35 people finished. The most-seconded dream was financial independence.
Three onboarding gatesSurvey → truths-and-a-lie → dream, all three required before the rest of the site unlocked, so everyone who got in had shared something first rather than just browsing others.71 signed up, 35 finished everything (49.3%). The biggest drop was the survey gate (−21). 73.2% (52 people) signed in with Google.Would fix: the gates did add real friction. I should have said up front that nothing is visible to others until the game period starts, so people could fill it in without worrying.
Points and reactionsTurned one-way answering and passive reading into something you could react to, made every interaction score, and used the points to drive the plant and the leaderboards.The full points breakdown is visible on each dashboard — who reacted to you, who seconded your dream — and it is up to you whether to reach out.
Plant growthAI read your survey answers and assigned a personality: 5 plants × 3 materials = 15 types, each with 5 growth stages. The final stage is deliberately "withering", not "full bloom".75 illustrations in total, 25 people unlocked theirs, most got "Glass Fern".Fixed: the day after launch I relaxed the unlock rule from "answer ten survey questions during the survey period" to also allow interaction points during the game. Would fix: never showed the reasoning behind the AI verdict, and never asked users whether it felt accurate. The auto-analysis cron felt like a hassle so I skipped it.
Leaderboards and streaksFour sub-boards next to the overall one (knows-everyone-best / most active / most dream companions / longest streak). From a seven-day streak on, that day's game score was multiplied by 1.5.Each board winner got a commemorative potted plant. I made 30; only 3 people signed up for the offline meetup, so in the end anyone who wanted one could claim it.
Class board and dashboardA second way to cut the same data — read every answer to one question side by side instead of person by person. A progress bar showed how many classmates you had read. Deliberately no in-app comments or DMs.Didn't reinvent the wheel — connections got pushed back to the platforms people already use, so nothing is lost when this site goes offline.Would fix: I could have opened a Discord server that any registered classmate could join.
Did it work?An informal poll on my personal Instagram stories after the event, asking how many old classmates people reconnected with because of it.17 replies; 8 of them (47%) reconnected with three or more people — genuinely beyond the organiser himself.Limits: the sample is self-selected (people who watch my stories and bother to answer are the more engaged ones), and there was no "0" option, because everyone reconnects with the organiser at minimum. Hence no response rate and no summed total of reconnections.
Building it with AIClaude Code throughout, with every permission set to require my approval, specifically so I could see what it was doing at each step. Mostly Sonnet, with Opus for a few audits.Caught it planning unnecessary checks more than once. Every service I picked stayed inside its free tier.Would fix: no proper user feedback survey (only an informal Instagram poll afterwards). I should have popped a survey after the last day's question, or emailed one after the event.
Things that brokeA schema that split scheduling into its own table, AI ignoring third-party API default limits, a basic upsert never written, and zero prior knowledge about egress.First login went from 10–13 seconds down to 5–6.Fixed: scheduled questions not picking up user edits, the leaderboard stuck on the first 1000 rows, the reminder mailer capped at two emails per minute, the dream field inserting a new row on every edit, and the egress blow-up.

Design reasoning

  1. Sign up

  2. Profile

  3. Gate 1

    Survey

  4. Gate 2

    Three Truths & a Lie

  5. Gate 3

    Ten-year dream

  6. Full access

All three gates had to be cleared before the rest of the site unlocked. Each step below covers the design reasoning and what the numbers actually looked like.

The ten-year survey

From the very start I was clear about what I wanted: people sharing their own stories, their experiences, and the values they had grown into. So the real problem was how to make people willing to share, and willing to actually read each other properly. Throwing out one big open topic was never going to produce good answers — not because people wouldn't try, but because without a frame to follow it is easy to drift off, or to miss the depth that ten years of a life actually holds.

So it ended up as a survey split into three sections — present, past, and future — with ten questions each. As for what to actually ask: River and I came up with a base of 20, and then I put them through several rounds of grilling with AI, rewriting the ones that were too vague or that were secretly asking the same thing twice. What came out was 30 questions that make you think “I need to sit down and settle myself before writing this” (there are still a few light ones in there). Answering any five of them cleared the first gate.

For the answer fields, I did consider letting people format their own text, but I figured this didn't need to read like a blog post — it is just a survey, and fewer formatting decisions is less pressure on the user. Thinking about it afterwards, it is a bit of a shame I skipped it: some answers ran long, and those needed at least bold text or a heading size to break up paragraphs. On top of that, once it was live I found people pasting links, and since I hadn't made links clickable, everyone had to copy and paste the URLs by hand. A small shame on the experience side.

One more thing about the survey: beyond sharing stories, answering it is also a way of settling your own life into a record. So I had originally planned a feature on the profile page — where all of a person's answers are collected — to schedule an email to yourself ten years from now. But there were email authorisation problems on the technical side, and I also wasn't confident the mail service I had picked would still exist in ten years. In the end I asked everyone to save the page as a PDF by hand and schedule it to their future selves from their own mail account.

Survey answers per person: two peaks, both parked on a gate

Dashed lines mark the two gates. The distribution is clearly bimodal — the 10–19 cluster is exactly what the 10-question gate pulled in.

Five answers unlocked the game; ten unlocked the plant analysis. 7 people stopped at exactly 5 and 19 landed in 10–19 — each gate left its own cluster behind.

The survey form
Figure 1 — the survey form (the interface is in Traditional Chinese)

The daily game

I was also clear from the start that this shouldn't be a one-or-two-hour event that ends and is over. I wanted people interacting across the whole period and actually connecting, and reading survey answers statically felt a bit boring on its own — it also gave nobody a reason to come back the next day. So I reached for the most classic icebreaker there is: Three Truths and a Lie. (The version most English speakers know is Two Truths and a Lie; here each person wrote three true statements and one false one, four options in total.)

On the design of it, I first considered insisting that every true option had to express or reflect some life value. That is hard, but a frame could have guided people there. Thinking about it more, it felt like asking too much, because people may simply not want to expose that much of their inner life. So the final rule was just: no cheap wordplay — for instance taking something you did at twenty and writing it as twenty-one. Beyond that, write whatever you want; finishing all four options cleared the second gate. That turned out well. People were creative in all sorts of directions and shared plenty of genuinely interesting things, well beyond what I had expected.

Scheduling was driven by headcount: across the 13 days of the event, two to four classmates' sets were playable each day. I decided the order from my admin dashboard by reading what people had written, putting the ones that felt most interesting in the first few days to set the tone, so that others would see them and go back to improve their own. Everyone could freely edit their set right up until their spotlight day. Once the schedule was fixed, that person's page showed their spotlight date, and the system emailed them a last-chance-to-edit reminder three days beforehand. The right to edit is a basic human right of internet culture; whether you actually use it is up to you. Looking at the numbers though, very few people were still willing to go back and revise later on — which says that with this kind of game mechanic you are better off getting people to their best version from the start, since going back to edit is its own cost in time and attention.

The core of the daily question was to make everyone come back during the event, moving the stage lights onto a different person each day. Looking back, one thing I would improve is linking to that day's subject's profile, so people could go and read up on them before answering. Each day's question was also only viewable that day, so if you didn't answer it that day you never saw it again. The game doubles as a test of how well you know what an old classmate has been doing and who they have become over the past ten years, so the answer is revealed the moment you submit, along with live stats: how many people picked each option and what the correct-answer rate is. You can also react to options with emoji. I deliberately designed the stats so you can't see who picked which option, because I was worried that without anonymity people wouldn't dare to guess — though if I could, I would really like to A/B test whether showing who picked what increases engagement. Another thing worth improving: I would have the subject also write down, at the time they write the lie, where the lie actually is and what the truth should be, and have that revealed automatically once someone answers. (I patched this one with marketing instead — commenting on each day's options in my personal Instagram stories.)

Daily question participation

Top: answers submitted (the grey slot behind each bar is how many were possible that day). Bottom: participation rate; the dashed line is the 43.2% weighted average.

  • Answers submitted
  • Answers possible
  • Participation rate %
The Three Truths and a Lie page
Figure 2 — the Three Truths and a Lie page, showing the post-answer stats now that the game has ended

Ten-year dreams

The last gate was writing down one dream for your next ten years, so that anyone who shares it can second it. Say someone wants to open a restaurant in Hsinchu Science Park — if someone else has the same dream, they can talk privately and go and do it together. And even outside the event window, if something years from now reminds you that a friend once had a similar dream, that is another chance to reconnect. Although as it turned out, a lot of people's dream was financial independence, and that was also the most-seconded one 🤣

Onboarding numbers

So the whole onboarding flow was: sign up / log in* ➡️ fill in your basic profile ➡️ gate one: answer at least five survey questions ➡️ gate two: finish your Three Truths and a Lie ➡️ gate three: write your dream — and then you could see every page in the system. Before finishing all of it you could only see the home page and edit your own profile; click anything else and the system pushed you back to whichever gate you hadn't cleared. The reasoning was that I wanted everyone who got in to be someone willing to share their own story, rather than someone who came only to peer at other people one-way without exposing anything themselves. Thinking about it afterwards though, this definitely added friction to onboarding: people had to fill things in before knowing how any of it would be displayed, which is a little scary (although personally, I would have typed in whatever met the gate, gone in for a look, and come back to fix it later). In hindsight I should have put a short line of copy right there saying that nothing written before the game period is visible to anyone else, so people could fill it in without worrying — instead of only discovering that after finishing and visiting each page.

Onboarding funnel: 71 accounts created, 35 finished

Gold is who stayed, grey is who dropped, both on the same track.

  • Cleared this stage
  • Dropped

The worst break is at the Survey stage: only 63.2% made it through, losing 21 people in one step.

Daily signups and completed onboarding

Signups spiked to 23 on launch day, then moved only around scheduled milestones.

  • Signups that day
  • Onboarding completed that day

On 5/31, the survey deadline, 8 people finished onboarding — the highest single day of the whole run. A clear deadline effect; it fell back once the game started the next day.

Points and plants

With only what I have described so far, the only real interaction was the Three Truths game, which felt too thin. So I took the one-way survey answering and the passive reading and added the ability to react to any answer with an emoji. And how do you give people a reason to interact? I designed a points system: interact, and you accumulate points. And then what do the points do? That is where I thought of a growth game. Grow what, though? I wanted something that carried the image of “time passed, and everyone grew” — a plant fits that image best, and it can also step through growth stages as points come in.

On the growing itself: if everyone had the identical plant and differed only by stage, that would be boring, and it would lose the collective feeling of a ten-year reunion. So I decided everyone's plant should be different, so that together they would add up to a garden belonging specifically to our class. How do you make everyone's plant different? Run an AI personality analysis over their survey answers to work out which kind of plant they are.

For the plant types, Norman and I decided to find five different plants paired with three different materials, giving fifteen combinations in total. The criterion was that the plant's growth stages should have clearly distinct forms, and we settled on ferns, pines, poppies, grasses, and succulents — each broken into five growth stages: seed, seedling, flowering, fruiting, and withering. There is a decision here that really stayed with me. When I first discussed it with AI, the five stages it gave me were seed, sprout, growth, bud, and full bloom. But Norman thought the last one should be withering, because he finds a plant in decay beautiful too. That is a suggestion I don't think I would ever have arrived at with AI, no matter how I had steered the conversation, because everyone is too used to the default logic that the top level is full bloom. Huge credit to Norman for this one! We went with withering as the highest stage, and beyond being a distinctly human piece of taste and aesthetics, it carries a good meaning: over the course of this event, we gathered nutrients back into the soil and into ourselves, marking a rest at the end of one stage of life — see you in the next ten years.

Then, because our plant images were generated with AI assistance, we ran some small experiments to see which materials came out looking better, and settled on glass, metal, and coral. As it turned out, most people's result was Glass Fern: “You pick up on faint light and on the mood of a room; you already know before anyone else has noticed. You are in no hurry to say it, and sometimes you just quietly take it in. People who spend a long time around you realise that you have actually been keeping a great deal in view.” One thing I feel could be stronger here is showing the AI's reasoning, and letting people give it a simple thumbs up or down to say whether they agree with the read.

Introducing points opened up a lot more to design. For example, before the game started, to encourage people to answer more of the survey, they got base points for clearing the basic gate and then extra points for every additional question — and reaching at least ten questions during the survey period was what unlocked your plant analysis. This was a bit of a misjudgement on my part: there were a lot of questions to answer, and the plant personality analysis was only an extra bit of fun that didn't affect the actual participation gates, so by the time the game started a lot of people hadn't unlocked their plant. But given that Norman had designed all these beautiful illustrations for the plant encyclopedia (5 plants × 5 stages × 3 materials = 75 images), they had to get as much exposure as possible to be worth anything! So the day after launch I changed the unlock mechanic so that people could still trigger the analysis once the game had started, as their interaction points crossed the threshold**. In the end 25 people unlocked theirs.

Cumulative plant unlocks: the slope only rose after the rule changed

The stepped area is cumulative unlocks; the bars below are new unlocks that day, with the dark blue part being people who later re-ran the analysis.

  • Cumulative unlocks
  • New that day (analysed once)
  • New that day (later re-analysed)

Before the unlock rule was relaxed on 6/02, 17 days produced only 10 unlocks; the next 12 days added 15 more. The slope visibly steepens — 25 unlocked in total, 11 of them re-ran the analysis.

The plant encyclopedia
Figure 3 — the plant encyclopedia

Another points-related design is the streak bonus: log in seven days in a row and, from the seventh day onward, that day's answer score is multiplied by 1.5 — to encourage people to come back and take part every day.

And once there are points, of course there is a leaderboard. Alongside the overall board I designed four sub-boards: Knows Everyone Best (most correct Three Truths answers), Most Active (highest total interaction points), Most Dream Companions (most people seconding your dream), and Streak Champion (longest run of consecutive days coming back to answer). Whoever topped each board got a commemorative potted plant from the event***.

The leaderboard
Figure 4 — the leaderboard

All of these points are itemised on each user's dashboard — which points came from answering the survey, which came from emoji reactions, and so on. It goes into real detail, including who reacted to you and who seconded your dream, so each person can decide for themselves whether to reach out.

The dashboard also reserves a large block for the class board. Its card carries a progress bar showing how many classmates' profiles you have opened to read their survey answers and their dreams. Once you are inside the class board, besides seeing who has registered and where everyone is living now, the page organises information along a different axis: here it is easy to read every person's answer to a single survey question side by side, along with everyone's dreams.

The class board
Figure 5 — the class board

As for why there are no comments or direct messages in the system: first, the engineering complexity is fairly high and I didn't want to maintain that many tables; second, people mostly already have each other on social media and can message there, so there is no need to keep reinventing the wheel; third, for the people who aren't connected on social media, this was a chance to add each other and build a longer-lasting line of contact — one that doesn't disappear when this event's system goes offline. That said, I do think I could have set up a Discord server that any registered classmate could join, so people could chat in there.

What I took from it

I genuinely learned a lot from AI about what a software PM has to think about, especially all the unknown estimates: how many people would actually join the system, what the peak-period numbers would look like, how the point thresholds should be set so that reaching the highest stage during the event is even possible. Every one of those questions has design and technical decisions behind it. Luckily, every service I ended up picking happened to meet both my needs and my traffic within its free tier — for instance, roughly how many API calls would the AI survey personality analysis take? Could it be done on Gemini's free-tier API alone?

Throughout this project I set every permission to require my approval and never let Claude Code decide and act on its own, because I wanted to learn from it and understand what it was doing at each step. And it did pay off — a few times I caught it planning checks that weren't necessary. So while AI is very useful, I also came away with a much deeper feel for how much human taste and judgement matter.

Also, since I am only on the most basic subscription, I was constantly afraid of running out of tokens, so I built almost the whole thing on Sonnet and only opened Opus two or three times. I found Sonnet's audits were never quite comprehensive enough, whereas Opus would lay out all the related files and designs at once, giving me fuller information to judge from (although if you push Sonnet to audit a specific scope a few times over, it can get there too).

The other thing clearly missing this time was collecting user feedback. I could have popped a survey up after people answered the last day's question, or emailed one out after the event — which would also have been a chance to ask how many old classmates each person reconnected with because of this. But by then I was already feeling too worn out to run another reunion, so I figured there would be no next time and no need to ask 🤣 (Although now that I am writing all of this down, I really do want another chance to do the parts I got wrong properly!)

But for the sake of writing this devlog, to make the data and the story richer, I did go and run an informal poll on my own Instagram stories, asking how many old classmates people had reconnected with because of the event. 17 people answered. Let me be clear about the limits of this data first: people who watch my stories and are willing to answer are the more engaged ones to begin with, so the sample skews positive. And I didn't include a “0” option, because everyone reconnects with me at minimum — which means that for some people in the one-or-two group, the only person they reconnected with may well have been me. So the number that actually means something is this: 8 of the 17 respondents (47%) reconnected with three or more old classmates, which is to say genuinely beyond me as the organiser.

Classmates reconnected with because of the event: 8 of 17 respondents reached three or more

An informal poll run on my personal Instagram stories after the event — not system data.

  • Three or more (beyond the organiser himself)
  • One or two

Two limits worth stating up front: (1) people who watch my stories and bother to answer are the more engaged ones, so the sample skews positive; (2) there was no "0" option, because everyone reconnects with me at minimum — so for some people in the one-or-two group, the only person they reconnected with may have been me. For the same reasons I deliberately don't quote a response rate (the denominator is unclear) and don't sum the bins into a total number of reconnections (each relationship would be counted once by both sides).

Things that broke

The biggest bug after launch was that once a Three Truths set had been scheduled, it stopped picking up the user's later edits. I only found it by going into the admin panel and comparing the tables myself. You could call it Sonnet's mistake, in that it didn't update the related table automatically, and you could equally call it my own poor judgement: when I first defined the tables and the schema, I didn't think carefully about the downside of splitting the scheduling data into a separate table. In hindsight, adding a column to the same table would have been enough.

Two more that I found myself, both cases of Sonnet overlooking default limits when calling another service's API. One only returned the first 1000 rows, which meant the leaderboard stopped reflecting reality from day four onward. The other was that the spotlight-day reminder mailer could only send two emails per minute, so on days with more than two questions I had to trigger the third and beyond by hand before I had found the root cause — the fix in the end was just sleeping a few seconds automatically.

There was also a bug reported by a user: the dream field wasn't upserting, so every edit created a new record. That one felt like AI dropping the ball on me, because I would call it basic enough design that it shouldn't need an explicit prompt.

Finally, with three days left in the event, I got an email from the provider about egress traffic blowing up. That one was purely because I had no background in this at all and simply didn't know it was something to think about. I had AI patch it quickly to bring the traffic down, and it stayed safely inside the free tier after that.

And back to the problem I mentioned at the top that I handed to Codex: page load speed. First login originally took 10 to 13 seconds because the middleware had duplicated logic and loops in it; after that was fixed it came down to 5 to 6 seconds (there is still room to optimise this further).

The team

I brought in three classmates — Norman, River, and Munus — to organise this event with me, but we were each in a different time zone, so communication wasn't always timely. On top of that this was entirely unpaid extra work, which meant some decisions couldn't be talked through quickly. For scheduling in future, I learned to leave a lot more slack. I also kept telling the team to look after their own jobs and their own lives first and only put time in when they had room to spare, because everyone else had work while I was the one unemployed with time on my hands. In the end we got lucky: when it came to deciding the event name, everyone happened to be online at the same moment, and we quickly voted through 實光 — roughly “true light”, sharing a character with our school's name. The other candidate was my own rather cringeworthy 實植成長, a pun swapping that character for “planting” to read as “planted growth” 🤣

Also, since question scheduling was done entirely by hand by whoever had admin rights, I did think about what would happen if something happened to me during the event, so I asked for classmates who would be free throughout to come on as admins as well. Thinking about it afterwards though, an admin's main job is only scheduling — which could be handled entirely by AI and code: rank the sets by how interesting they are and lock in the question dates in that order, spread the number of classmates per day evenly across the days remaining, and keep a human admin's right to adjust the schedule.

One small regret on this project is that I never got round to matching the typeface toNorman's design. He had wanted a rounded gothic, but I was short on time and it was a bit awkward technically, so I never implemented it and just used the default font. I have let my designer down 🥲

Finally, huge thanks to the three of them for their help — whether that was pouring a lot of time into design or giving me technical and design feedback, thank you all so much! And thank you to every classmate who helped promote and share this!

And last of all, thank you to the alumni association for funding the commemorative potted plants**** — everyone should go and join the alumni association! There is an alumni cup competition and the general meeting on Saturday 8/29 this year; you are all welcome to come back to school with us, and I will be there sharing what I learned from organising this.