A naming app looks like a string generator from the outside. You put in a surname, a gender, a style and a date, and two characters come out.
Generating the characters is the least interesting thing in NameSoul. What took the time is the two records that hang off the name, and the fact that they are keyed by different things and never joined to each other.
(Ep.4 of a short series about building this.)
Inputs, outputs, and what the name actually is
Input: a surname, a gender, a style, a date of birth.
Output, in the order the user meets it: a given name; a historical figure who shares the surname; a past-life figure matched to the name's temperament; the places tied to that figure; then six small artifacts, which are a voice clip, the bilingual text, the surname in seal script, the full name in brush, a colour derived from the birth date, and a personal seal.
The name is the index. Everything else is a lookup through it.
That is the whole architecture, and it is deliberately shallow. There is no model deciding anything at runtime. The decisions were made before the app existed, one record at a time, and the app just walks the path.
Record A is keyed by surname
The first record is "who carried this surname."
Pick Li. Under the name, one line appears: this name shares a lineage with Li Bai. Then a few lines about him, and one moment from his life where he was most himself.
The key here is the surname and nothing else. It does not care about the given name, the style, or the birth date. If two users with different given names both pick Li, they get the same Record A. That is intentional.
Why key it on the surname? Because the surname is the part of a Chinese name nobody chooses. It arrives with you. If a name is going to have roots, the surname is where they are. So this lookup answers one question only: who stood behind this character before you did.
Record B is keyed by temperament
The second record is "whose nature does this name carry."
This one is not keyed on the surname. It is keyed on the given name's meaning, imagery, and feel. The figure who comes back may share nothing with you. Different surname, different era, different life. What matches is the quality the name points at.
Two different keys, two different questions. A gives you a lineage. B gives you a direction. One looks back, one looks forward, and they never need to agree.
Why not merge them into one record
Merging is the obvious simplification, and it breaks the product in a specific way. If one person had to satisfy both conditions, the pool would collapse to "famous people whose surname also happens to fit this name's temperament." You would see the same handful of names repeatedly, and the second half of the app would become a lookup table of four people.
Keeping them separate costs one join that never happens and buys a much larger pool on each side. That trade was easy.
The selection problem was harder than the writing
I assumed writing would be the bottleneck. It wasn't. Choosing was.
Chinese history has more named figures with real personalities and documented lives than anyone can hold. Including all of them is impossible and would also be worse. So the filter came down to three rules:
- Famous first. Household names in China, at least recognizable abroad. They act as entry points, because recognition is what makes a user keep reading.
- Then the ones nobody outside China knows, whose lives are still extraordinary. Some are not even well known inside China. The interesting part of a culture usually lives here.
- Temperament has to match. This was the hard filter, and the one I refused to bend. You cannot take a famous person and attach them to a name because the name is popular. Something in that person has to actually answer the name.
After selecting, there is still the research: collected works, historical records, biographies, hunting for the one vivid fragment. Where was he standing. What did he say. Which poem. Which choice.
The honest limit
I can't exhaust Chinese history, and I'm not going to pretend the coverage is complete. What I do is select and match, and keep every entry sourced rather than invented. The standard I hold is "this figure is real, this moment is documented, this quality is his." The standard I can't promise is "nothing important is missing."
Places are a third lookup
The places hang off Record B, not off the name: cities he lived in, mountains and rivers he crossed, what he left behind, where he was buried.
I added them because a name that ends at a person is a fact, and a name that ends at a landscape is a reason. From a name to a person to a city to a place you could stand in. That last step is the only one I actually care about.
What the split bought
One record would have been simpler to build and much worse to use. Two records means the name can be new every time while the historical layer stays honest, and it means the same user can come back, change the style, get a different name, and land on a completely different person without the surname record being disturbed.
The whole app is this: a short path through records that were chosen slowly.
One question
If you were building this, what would you key Record B on? Temperament is the answer I landed on, and it is the least defensible one on paper, because "temperament" is not a field you can query.
I'd genuinely like to hear how someone else would model it. I made NameSoul, it's at namesoul.net, and the parts I'm least sure about are exactly the parts I'd most like argued with.
Top comments (1)
One thing that doesn't fit in the post.
The first version of Record B was keyed on keywords. I had a tag per figure, a tag per name, and I matched the tags. It looked reasonable and it produced nonsense: a name tagged "bold" would pull a general who spent his whole life retreating. Tags describe what a person did. They don't describe what a person was like.
So I dropped the tags and went back to reading. Every entry in that record is now written from the biography first and the label afterwards, which is why it took months instead of weeks.
The comment box is open. If you think temperament is the wrong key, say so.