The display name field
allowed twenty characters.
The check was one line.
If the name is longer than twenty,
cut it to twenty.
It passed every test,
because every test name
was written in plain letters.
Then a user typed nineteen letters
and a thumbs up.
The profile showed nineteen letters
and a small box
with a question mark inside.
Nobody had typed a box.
The code had made it,
from a perfectly valid name,
in one line nobody would review twice.
A JavaScript string
is not a list of characters.
It is a list of UTF-16 code units.
Most letters
fit in one unit.
An emoji does not.
It takes two,
a pair that only means something
together.
So length counted the name as twenty one,
and slice cut at twenty,
straight between the two halves.
What was left
was half a character,
which no font can draw.
It gets worse.
A family emoji
is several people
glued together with invisible joiners.
A flag
is two regional letters.
A skin tone
is a second code point
after the hand.
One thing on the screen,
up to eleven units in the string.
So length gives you storage.
It has never given you
what a person would count.
Decide which one you mean.
If the limit protects a database column,
count what the database counts,
bytes or code points,
and reject
instead of cutting.
If the limit is for people,
count what people see.
Intl.Segmenter
splits a string into graphemes,
the clusters a reader calls one character.
Cut on those boundaries,
and the thumbs up
stays whole or goes whole.
Then fix the tests.
Add one name with an emoji
at the exact edge of the limit.
Add a flag.
Add a family.
They take a minute to write
and they live in the gap
where the bug lived.
Your users do not type
in the alphabet
your test data was written in.
Let your fixtures
look like them.
– Serguey Asael Shinder
Top comments (0)