{"post":{"slug":"varchar-vs-text-2026","title":"Varchar vs Text: A Length Limit Is a Constraint, Not a Speedup","excerpt":"Declaring a column as varchar(255) does not make it smaller or faster in most databases. It adds a constraint, and constraints should be chosen deliberately.","category":"code","date":"January 6, 2026","author":"ByteLedger Team","content":"<span class=\"dropcap\">A</span>lmost every schema contains a `varchar(255)` somewhere, usually for a name or an email address. Ask why 255 and the answer is usually that it is what everyone uses.\n\nIn many modern databases, that limit provides no storage or performance benefit at all. The column stores exactly the bytes present regardless of the declared maximum, and the limit is enforced as a check on write.\n\n## What changed in 2026\n\n- **The convention persisted despite being understood.** Awareness that 255 is arbitrary did not stop it being copied into new schemas.\n- **Application-level validation became the norm.** Length enforcement moved largely to application code and API schemas.\n- **Migration pain got documented.** The cost of changing a length limit on a large table became better known.\n- **Unicode handling clarified.** The distinction between character count and byte length continued to catch people out.\n\n## Where 255 comes from\n\nIt is not a modern constraint. It comes from an older storage format where the length of a variable-length field was recorded in a single byte, which can represent values up to 255. Exceeding that required two bytes, so 255 was the largest length with the cheapest length prefix.\n\nThat storage detail is irrelevant in most current databases. The number survived as convention long after the reason disappeared, which is why you see it applied to fields where it makes no sense — a country code capped at 255 characters, a URL capped at 255 when real URLs exceed it.\n\n## What the limit actually does\n\n| | Varchar with limit | Text / unlimited |\n| --- | --- | --- |\n| Storage used | Bytes present | Bytes present |\n| Performance | Same | Same |\n| Enforces maximum length | Yes | No |\n| Changing the limit | May rewrite the table | N/A |\n| Catches oversized input | Yes, at the database | No |\n\nThis is engine-specific and worth verifying rather than assuming: some databases do store fixed-width or otherwise differentiate, and behaviour around increasing versus decreasing a limit differs. The general point holds for the widely-used engines — the limit is a constraint, not a storage decision.\n\nWhich reframes the question. It is not \"which type is faster\" but \"do I want a length constraint enforced in the database, and if so, what value\".\n\n## When a limit is worth having\n\nThere are genuine reasons to constrain length.\n\n**A real domain limit exists.** A country code is two or three characters. A currency code is three. Encoding that in the schema documents it and catches errors.\n\n**Preventing accidental large inserts.** A bug writing an entire document into a name field is caught immediately rather than discovered later.\n\n**A downstream system has a limit.** If data flows to something that truncates at a length, enforcing it at the source is better than discovering truncation later.\n\n**Storage predictability.** Where you need to bound a table's worst-case size.\n\nWhat is not a good reason: because the previous schema did it, or because a limit feels tidier than no limit.\n\nChoose the value from the actual domain, and choose generously — a name field limited to 50 characters excludes real names, and this is a recurring source of user-facing bugs. Where you cannot state a domain reason, an unlimited type is the honest choice.\n\n## The Unicode wrinkle\n\nLength limits are typically expressed in characters, and storage is in bytes. A multi-byte character consumes more storage than a Latin character while counting as one toward the limit.\n\nThat matters when a downstream system limits *bytes* rather than characters. A field passing a 100-character check can exceed a 100-byte limit elsewhere, and the failure appears at the boundary rather than at the source.\n\nWhere a byte limit is what actually matters, enforce that specifically rather than relying on a character count.\n\n## Common mistakes\n\n- **255 by default.** Legacy artefact applied without thought.\n- **Assuming a limit improves performance.** It generally does not.\n- **Limits too tight for real data.** Names, addresses, and URLs exceed intuitive maximums.\n- **Confusing character and byte limits.** Different constraints.\n- **Changing limits casually on large tables.** May rewrite.\n- **No limit anywhere, including the application.** Invites accidental large values.\n\n## FAQ\n\n**Should I use text everywhere?**\nWhere no domain limit exists, that is a defensible default with application-level validation. Where a real limit exists, encoding it in the schema documents and enforces it.\n\n**Is changing a length limit expensive?**\nDepends on engine and direction — increasing is often cheap metadata, decreasing usually requires validating existing rows. Check before running it on a large table.\n\n**What about indexing?**\nIndexes on very long values are limited in most engines. If you index a column, that is a genuine reason to constrain its length.\n\n**Does this apply to all databases?**\nThe general point does; the specifics vary. Some engines differentiate more than others, so verify for yours rather than generalising.\n\n## Where to go next\n\nFor enforcing rules beyond length, read [check constraints](/blog/check-constraints-explained-2026). For handling large values, [TOAST storage](/blog/toast-storage-explained-2026), and for changing column types safely, [expand and contract migrations](/blog/expand-contract-migrations-2026).","hue":301,"metaTitle":"Varchar vs Text: A Constraint, Not an Optimisation","metaDescription":"Why varchar length limits rarely improve storage or performance, where the 255 convention comes from, and when a length constraint is genuinely worth having.","keywords":["varchar vs text","varchar 255","string column length","database column types","length constraint","schema design decisions","column type migration","database design 2026"],"tldr":["**In many engines varchar and text are stored identically** — the length limit adds a check, not an optimisation.","**255 is a legacy artefact** — it comes from an older storage format, not from any modern requirement.","**Changing a length limit later can require a table rewrite** — depending on the engine and direction.","**A length limit is still worth having as a data-quality constraint** — just choose it for a reason.","**Skip:** arbitrary length limits copied from another schema. They will be wrong for your data."],"readTime":"4 min read","wordCount":856},"related":[{"slug":"enum-vs-lookup-table-2026","title":"Enum vs Lookup Table: Where Should Your Fixed Values Live?","excerpt":"A status column with five possible values can be a database enum, a lookup table, or a check constraint. The right answer depends almost entirely on who needs to change the list.","category":"code","date":"January 9, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":315},{"slug":"expand-contract-migrations-2026","title":"Expand and Contract: Schema Changes Without Downtime","excerpt":"Renaming a column in one deploy breaks every running instance of the old code. Splitting the change into three deploys makes it invisible to users.","category":"code","date":"August 13, 2026","author":"ByteLedger Team","readTime":"5 min read","hue":320},{"slug":"soft-delete-patterns-2026","title":"Soft Delete: The Flag That Infects Every Query","excerpt":"Adding a deleted_at column looks like the safe choice. Then every query needs a filter, every unique constraint breaks, and one forgotten WHERE clause leaks deleted data.","category":"code","date":"August 13, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":302}],"sameCategory":[{"slug":"bazel-build-explained-2026","title":"Bazel Explained: What Hermetic Builds Actually Cost","excerpt":"Bazel delivers reproducible, precisely cached builds and demands that you describe everything. Here is what you get, what it costs, and who should say no.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":145},{"slug":"graphql-federation-2026","title":"GraphQL Federation: One Graph, Many Teams","excerpt":"Federation composes independently-owned subgraphs into a single API. Here is what it solves, the operational cost it adds, and when a simpler gateway is enough.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":300},{"slug":"hermetic-builds-explained-2026","title":"Hermetic Builds Explained: Same Inputs, Same Output","excerpt":"A hermetic build depends only on declared inputs. Here is what that property gives you, the common leaks that break it, and how to move toward it incrementally.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":130},{"slug":"monorepo-tooling-compared-2026","title":"Monorepo Tooling Compared: What You Actually Need","excerpt":"Monorepo tools solve task orchestration and caching, not repository layout. Here is what each tier provides and how to tell when you have outgrown the simple option.","category":"code","date":"August 19, 2026","author":"ByteLedger Team","readTime":"3 min read","hue":150}],"newer":{"slug":"replication-lag-explained-2026","title":"Replication Lag: When the Replica Has Not Caught Up","excerpt":"Routing reads to a replica scales your read capacity and introduces a window where the replica does not yet have data the primary already committed.","category":"code","date":"January 6, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":316},"older":{"slug":"agent-reflection-loops-2026","title":"Agent Reflection: When Self-Critique Helps and When It Does Not","excerpt":"Asking a model to review its own work sounds like free quality. It helps on tasks with checkable output and produces confident agreement on everything else.","category":"ai","date":"January 5, 2026","author":"ByteLedger Team","readTime":"4 min read","hue":205}}