← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

MySQL String Types: VARCHAR vs TEXT vs CHAR

Database Beginner
debt(d5/e3/b5/t7)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5). The detection_hints list semgrep as the tool, with the specific code_pattern being TEXT columns with UNIQUE or DEFAULT, or CHAR on variable-length fields. This is not caught by the compiler or default linters but requires a configured semgrep rule — placing it squarely at d5.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix is a column type swap (VARCHAR(255) for most, TEXT for large content, CHAR for fixed-length). This is a small refactor: an ALTER TABLE statement per affected column, potentially touching schema migration files, but not a cross-cutting codebase change. Slightly above a one-liner due to the utf8mb4 index limit gotcha that may require row format changes.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). The choice applies to all web and CLI contexts, and wrong column types affect every query, index, and migration touching those columns. Using TEXT for email/slug blocks default values and full-column indexes; using CHAR(255) wastes storage on every row. This imposes an ongoing tax on schema design and query performance but doesn't reshape the entire architecture.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception field states that TEXT and VARCHAR are considered interchangeable by many developers, yet TEXT cannot have a DEFAULT value, cannot be fully indexed, and stores data off the main row page — contradicting the intuitive assumption that they are equivalent string storage. The utf8mb4 + VARCHAR(255) index limit is an additional non-obvious gotcha. This contradicts expectations formed from other databases or general programming experience, warranting t7.

About DEBT scoring →

Also Known As

VARCHAR vs TEXT MySQL CHAR VARCHAR TEXT MySQL string column types

TL;DR

CHAR is fixed-length, VARCHAR is variable-length up to 65,535 bytes, TEXT variants store large content — each with different indexing and storage behaviour.

Explanation

CHAR(n) pads shorter values with spaces — slightly faster for fixed-length data. VARCHAR(n) stores actual length + 1-2 bytes overhead. TEXT (64KB), MEDIUMTEXT (16MB), LONGTEXT (4GB) store large content off the main row page — cannot be fully indexed without a prefix length and cannot have DEFAULT values. VARCHAR up to 767 bytes (InnoDB row format default) supports full-column indexing. For utf8mb4, the index limit is 191 characters on older InnoDB row formats.

Common Misconception

TEXT and VARCHAR are interchangeable. TEXT cannot have a DEFAULT value, cannot be fully indexed, and stores data off the main row page — impacting row fetch performance.

Why It Matters

Using TEXT for short columns like email prevents full-column indexes and default values. Using CHAR(255) for variable names pads every row to 255 bytes — wasting storage on every row.

Common Mistakes

  • Using TEXT for email, name, or slug columns — no DEFAULT, no full-column index.
  • Using CHAR(255) for variable-length data — pads every row regardless of actual content length.
  • Forgetting that utf8mb4 VARCHAR(255) requires 255×4=1020 bytes — exceeds the 767-byte InnoDB index limit on older row formats.

Avoid When

  • Avoid TEXT for columns that need DEFAULT values or full-column unique indexes.
  • Avoid CHAR for variable-length content — every row is padded to the declared length.

When To Use

  • Use VARCHAR(255) as the default for most string columns — fully indexable and flexible.
  • Use TEXT/MEDIUMTEXT/LONGTEXT for article bodies, descriptions, or any content over 1KB.
  • Use CHAR for truly fixed-length values: country codes (CHAR(2)), currency codes (CHAR(3)), UUIDs (CHAR(36)).

Code Examples

✗ Vulnerable
-- TEXT for short fields: no DEFAULT, no full index
email TEXT NOT NULL,
-- CHAR(100) wastes 100 bytes even for 'Bob'
name CHAR(100) NOT NULL
✓ Fixed
CREATE TABLE users (
    id           INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    email        VARCHAR(255) NOT NULL UNIQUE,
    country_code CHAR(2)      NOT NULL DEFAULT 'GB',
    bio          TEXT,
    slug         VARCHAR(100) NOT NULL UNIQUE
) ENGINE=InnoDB CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;

Added 31 Mar 2026
Views 40
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 1 ping W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 2 pings T 2 pings F 0 pings S 1 ping S 1 ping M 1 ping T 0 pings W 0 pings T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 1 ping T 0 pings W
No pings yet today
PetalBot 1
Google 6 Perplexity 4 Scrapy 4 ChatGPT 3 Ahrefs 3 SEMrush 3 Unknown AI 2 Meta AI 2 Claude 1 Majestic 1 PetalBot 1
crawler 26 crawler_json 4
DEV INTEL Tools & Severity
⚙ Fix effort: Low
⚡ Quick Fix
Use VARCHAR(255) for most string columns, TEXT for large content, CHAR only for truly fixed-length values like country codes
📦 Applies To
web cli
🔗 Prerequisites
🔍 Detection Hints
TEXT column with UNIQUE or DEFAULT, or CHAR on variable-length fields
Auto-detectable: ✓ Yes semgrep
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Low ✓ Auto-fixable Fix: Low Context: Line


✓ schema.org compliant