Alright, let’s talk about varchar(n). This is the data type you’ll use 99% of the time when you’re storing strings that aren’t the size of a small novel. Think of it as a string with a maximum length you get to define—the (n) part. It’s the “I know this field should be roughly this long, but I’m not a monster, I’ll let you use less space if you want” option. It’s the workhorse, and you need to understand its quirks, because they will bite you if you’re not careful.

How varchar(n) Actually Works Under the Hood

Here’s the most important thing to know: a varchar(100) column holding the word ‘Hi’ doesn’t take up 100 bytes of space. It takes up approximately 3 bytes: 1 for the length of the string and 2 for the two characters. PostgreSQL adds a small overhead (usually 1 byte) to track the length of the string. This is brilliant because it saves a ton of space compared to its rigid cousin, char(n), which we’ll eviscerate later.

The n in varchar(n) is a limit, not a target. It’s the bouncer at the club door. Your data can be shorter, but if it tries to be longer, it gets unceremoniously rejected with an error. This is your first line of defense against garbage data.

-- Let's see it in action
CREATE TABLE user_profiles (
    username varchar(15),
    bio varchar(500)
);

-- This works perfectly.
INSERT INTO user_profiles (username, bio)
VALUES ('data_wizard', 'I love SQL and correctly sized data types.');

-- This fails spectacularly. The bouncer says "Nope."
INSERT INTO user_profiles (username, bio)
VALUES ('this_username_is_way_too_long_buddy', 'My bio.');
-- ERROR: value too long for type character varying(15)

The Critical Choice: Picking ’n’

Choosing the right length limit is more art than science, but leaning towards pragmatism. The old MySQL-era wisdom of varchar(255) everywhere is cargo-cult programming. You’re better than that.

Ask yourself: “What is the real-world constraint here?”

  • US phone number? Even with formatting, varchar(15) is plenty.
  • Email address? The RFC says it can be up to 254 characters. Be generous but sane. varchar(254) is a well-justified choice.
  • Country code? varchar(2) for ISO codes, or maybe varchar(3) if you have a reason.
  • Username? varchar(25). No one needs a 100-character username. This isn’t an ego-measuring contest.

The reason to be thoughtful is threefold:

  1. Data Integrity: That length limit is a free, built-in validation check. It prevents someone from accidentally pasting the entire text of War and Peace into a “First Name” field.
  2. Performance: While a short string doesn’t take much space, if you go comically large (varchar(10000) for a name field), you can run into issues. PostgreSQL might decide it can’t perform an in-memory sort and instead use a slower disk-based sort for your queries. It also makes the database’s statistics less effective.
  3. UI/UX: Your application’s forms and displays are probably designed for strings of a certain length. The database enforcing this saves you from writing extra validation code in twelve different places.

The varchar(n) vs. text Debate (Spoiler: It’s Dumb)

You’ll find heated arguments online about whether you should ever use varchar(n) or just use text for everything and do all your length checks in the application. This is mostly a religious war, and the varchar(n) side has the better argument for one simple reason: The database is the last line of defense.

Your application code might have a bug. A new API might send you junk data. A direct database import might happen. The varchar(n) constraint is a guarantee that no matter what, that column will never exceed its intended purpose. It’s a core feature of the relational model. Using text and relying solely on application-level checks is like removing the safety on a gun because you “promise to be careful.”

That said, if there is no logical, sensible maximum length for the data you’re storing, then by all means, use text. That’s what it’s for. But for 90% of your string columns, a sensible n exists and provides valuable integrity.

A Gotcha: The Multibyte Character Trap

Here’s a subtle one that’ll get you. The n in varchar(n) specifies the maximum number of characters, not bytes. This is generally what you want. However, in a multibyte encoding like UTF-8 (which you should absolutely be using), a single “character” can take up multiple bytes.

For example, the ‘❗’ emoji is a single character, but it requires 3 bytes of storage. This means you can successfully insert a string of 100 emojis into a varchar(100) column, but the actual disk space used will be around 300 bytes plus overhead. This almost never causes an issue, but it’s critical to understand if you’re ever doing low-level checks on string length in bytes or worrying about very tight storage constraints. The bouncer only counts heads (characters), not how much space their hats (bytes) take up.