text vs varchar — which to use in Postgres, and where the number 255 came from

You ask AI to create a users table. It writes:
name varchar(255),
email varchar(255),
bio varchar(255)
Why 255? You didn't ask for it. The number has wandered through code for twenty years — from MySQL, old tutorials, other people's migrations. A month later a user pastes a long "About me" and gets:
value too long for type character varying(255)
Here's the surprise: in Postgres, varchar(255) has zero speed advantage over text. Not a byte. Let's see where the number came from and what to actually write.
In short: the difference
- text — a string of any length.
- varchar(n) — a string of at most
ncharacters. Longer — an error on write. - varchar without a number — the same as
text.
That's all. Postgres stores them the same way. The docs say it plainly: there's no performance difference, apart from a few CPU cycles to check the length.
Point by point
Speed and storage.
- text: the baseline.
- varchar(n): the same plus a length check on every write. The "varchar is faster" myth comes from other databases.
Guarding against junk.
- text: accepts even a megabyte. If you need a limit, you add it separately (shown below).
- varchar(n): the limit is baked into the type. That's its only real plus.
The error message.
- text with a check: you name the rule yourself —
name_too_long. Easy to catch and turn into a friendly message. - varchar(n): a faceless
value too long for type character varying(255). Not something to show a user.
Changing the limit.
- text with a check: drop the old rule, add a new one. Two lines in a migration.
- varchar(n): you change the column type. Raising the limit is cheap. Lowering it makes Postgres re-check every row, which is slow on a big table.
What counts as length.
- In Postgres,
varchar(10)means 10 characters, not bytes. Accented and non-Latin letters fit whole, even though they take more bytes.
Where 255 and 191 came from
This is MySQL's story, not Postgres's.
255 is a leftover from old versions where a VARCHAR's length was stored in a single byte. One byte tops out at 255. The limit was raised long ago; the habit stayed.
191 is even funnier. In MySQL with utf8mb4, one character can take 4 bytes. Old InnoDB allowed index keys of at most 767 bytes. 767 / 4 = 191 and change. So Laravel spent years recommending defaultStringLength(191), and the number spread through tutorials.
None of these limits apply to Postgres. Writing varchar(255) there is carrying someone else's scar onto a healthy database.
SQLite is funnier still: it doesn't check varchar length at all. varchar(10) silently swallows a thousand characters. More differences in "SQLite vs Postgres".
How to limit length properly
If a limit makes sense — a name of at most 100 characters, a post of at most 280 — write:
name text not null
constraint name_too_long check (char_length(name) <= 100)
What you get:
- a limit, just like
varchar(100); - an error with a clear name,
name_too_long; - changing the limit means swapping one rule, not the column type;
- room for more rules: "not empty", "no leading or trailing spaces".
And most importantly: check the length in the form too. The database is the last line of defense, not the first. People should see "max 100 characters" before submitting, not after.
Who should pick what
Pick text — almost always. Names, emails, titles, descriptions, comments. The Postgres wiki's "Don't Do This" page says it outright: don't use varchar(n) by default. Add limits with check when you genuinely need them.
Pick varchar(n) — when the limit is hard, won't change and is part of the data itself: a 2-letter country code, a fixed-length standard ID. Or when the project must also run on MySQL, where the difference between the types is real.
Skip char(n) — it pads strings with spaces up to the length. In Postgres it's the slowest of the three and a source of weird comparisons.
If you're just setting up a database, one rule: text by default, limits on purpose and with a name.
Can I switch varchar to text in an existing table?
Yes, with one alter table ... alter column ... type text. Postgres stores them the same way, so no data rewrite is needed — it's fast.
Does a long text slow down an index?
Not for normal strings. But a B-tree index can't hold a value longer than roughly 2.7 KB — that's true for any string type. Long texts aren't indexed whole; full-text search exists for that.
Short story-lessons, an agent simulator and daily practice — in our mobile app. Free.





