Get AI summaries of any video or article — Sign up free
The case against SQL thumbnail

The case against SQL

Theo - t3․gg·
6 min read

Based on Theo - t3․gg's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.

TL;DR

SQL is treated as the wrong abstraction for application code because it’s a textual, console-era interface rather than a typed function-like API.

Briefing

SQL’s biggest sin isn’t that it’s inherently unsafe—it’s that it’s the wrong abstraction for application code. The core claim driving the discussion is that developers shouldn’t build business logic by stitching together raw SQL strings; instead, application layers should call stable, typed operations (functions/methods) that hide database internals while still letting the database remain the system’s state and truth.

The argument starts with SQL’s origin: it was designed as a console/reporting language, not as a programming interface. Passing textual queries through application layers forces programmers to treat data access like string construction, even though the database engine ultimately compiles SQL into internal representations and executes it via function-like mechanisms. That mismatch leads to a proliferation of “layers” (ORMs, query builders, sanitization patterns) that often don’t provide the right developer experience—especially around type safety, schema evolution, and preventing subtle bugs.

A major thread is the security angle. The harshest version of the “anti-SQL” stance frames SQL injection as a structural failure: if user-controlled text can reach a SQL engine, unauthorized access becomes possible. The counterpoint offered here is that modern frameworks and parameterized query mechanisms largely mitigate SQL injection when raw SQL strings aren’t built unsafely. The broader security lesson shifts toward the reality that injection-like risks exist wherever untrusted input is rendered or executed—HTML/JS contexts (XSS) being an example—so focusing solely on SQL can become a crutch.

From there, the discussion pivots to what SQL does well and where it breaks down. SQL’s strengths include standardization across engines (SQLite, Postgres, MySQL, Cassandra), enabling tooling and portability, and allowing deep optimization between the SQL layer and the database engine. It also enables industry-wide innovation such as sharding and features like row-level security or real-time update pathways.

Where the critique sharpens is in application integration. Raw SQL in app code tends to produce weak typing (results often become any/unknown), disconnects schema changes from compile-time feedback, and encourages “stringly typed” data access. The response favors alternatives that keep SQL’s power but move the interface upward: typed query builders/ORM-like layers (the transcript calls out Drizzle as an example) and higher-level data access objects that expose explicit operations like “get by id” or “mutate update subscription,” with types inferred from schema definitions.

The strongest practical examples come from newer database/application architectures. The transcript praises systems like Gel (EdgeDB) for a more application-shaped query syntax, and Convex for treating application logic as a pipeline from database state to UI updates—reducing client/server out-of-sync problems by pushing updates through a transaction-aware abstraction. Even so, the speaker draws a line: the goal isn’t to eliminate database abstraction entirely, but to avoid writing raw SQL directly in application code and to reduce stateful, hard-to-debug service objects.

In the end, the shared conclusion is narrow but firm: SQL is the wrong abstraction to write directly in application code. The database should remain the source of truth, but the interface should be a typed, intention-revealing layer that turns data into experiences with fewer opportunities for brittle glue code and hidden failure modes.

Cornell Notes

SQL’s central problem in application development is abstraction mismatch: it was built for console-style querying, yet app code often treats it like a programmable interface by constructing textual queries. The discussion argues that raw SQL in application code invites weak typing, brittle schema coupling, and hard-to-debug layers, even if SQL injection is largely mitigated by modern parameterization. SQL still earns its keep as a standardized, optimizable query language for data engines and cross-database tooling. The preferred direction is a typed access layer—query builders, data objects, or higher-level database platforms—that exposes explicit operations and keeps schema changes aligned with compile-time feedback. The practical payoff is fewer security footguns, fewer out-of-sync client/server states, and easier debugging through clearer input/output boundaries.

Why does the transcript treat SQL as a “bad programming interface” even though databases execute SQL internally via compiled representations?

SQL is ultimately parsed, planned, and executed through internal representations and engine function calls. The critique is that application code shouldn’t be responsible for constructing a textual query language as its interface. When developers pass strings into an API, they recreate a teletype-era workflow inside modern software, and that encourages weak typing and brittle coupling to syntax. The transcript’s proposed fix is not “ban SQL everywhere,” but replace raw SQL strings in app logic with typed, intention-revealing operations that hide the SQL text while still leveraging the database engine’s optimization.

How does the security argument evolve from “SQL injection is structural” to “focus on the real risk surface”?

The harsh version claims SQL is an ultimate security breach because user-controlled text can reach the SQL engine and grant broad access. The transcript then challenges that framing: SQL injection is mostly solved when code uses parameterized queries / safe templating rather than concatenating raw user intent into SQL strings. It argues that injection risks don’t disappear—they move to other execution/rendering contexts like XSS, where untrusted input is rendered into HTML/JS. The takeaway is that SQL-specific fear can become a crutch if other untrusted-input pathways are ignored.

What are SQL’s strongest reasons for success, and why do they not fully rescue it as an app-layer interface?

SQL succeeds because it’s standardized across engines (SQLite, Postgres, MySQL, Cassandra), enabling portability and shared tooling. It also allows powerful optimization between the SQL layer and the database implementation. Those benefits don’t automatically translate to application code, where the interface needs strong typing, schema-aware feedback, and stable function-like inputs/outputs. The transcript argues that raw SQL in app code fails those needs even if SQL remains excellent inside the database engine.

What does “type safety” mean in this context, and why does raw SQL push results toward any/unknown?

When app code sends a free-form SQL string, the compiler can’t know what columns and types will come back. The transcript notes that this often forces results to be typed as any or unknown, losing compile-time guarantees. A typed query builder (example: Drizzle) ties schema definitions in TypeScript to query results, so renaming a field (e.g., name → full name/username) triggers type errors at the component that renders the data—not silently at runtime.

Why does the transcript favor higher-level abstractions like Convex or Gel instead of “just call the database API directly”?

Calling the database directly still leaves developers responsible for building the right application-shaped interface and for handling synchronization and update propagation. Convex is praised for reducing client/server out-of-sync issues by using a transaction-aware abstraction that can push updates to clients without manual refetch logic. Gel is praised for a query syntax that feels closer to application data access patterns (nested relations, limits) than traditional SQL. The shared theme is that the interface should match application needs, not just database capabilities.

What is the “debuggability” argument against stateful service objects and for pipeline-style functions?

The transcript contrasts functional/pipeline-style code (clear input → database update → clear output) with OOP-style service objects that hold internal state across calls. Stateful objects can be in different configurations at runtime, obscuring order-of-operations and making it harder to determine why a bug occurred. Pipeline-style boundaries make it easier to trace where incorrect data was persisted or transformed. The claim isn’t that bugs vanish, but that they become easier to locate when state ownership and data flow are explicit.

Review Questions

  1. What specific developer problems does the transcript associate with writing raw SQL directly in application code (typing, schema coupling, debugging, etc.)?
  2. How does the transcript reconcile SQL’s security history with the claim that SQL injection is mostly solved today?
  3. Compare the roles of SQL, an ORM/query builder, and a higher-level platform like Convex in the transcript’s ideal architecture. What changes and what stays the same?

Key Points

  1. 1

    SQL is treated as the wrong abstraction for application code because it’s a textual, console-era interface rather than a typed function-like API.

  2. 2

    Modern parameterization and safe templating reduce SQL injection risk, so “stop using SQL” is not the only (or best) security lesson.

  3. 3

    SQL remains valuable inside databases for standardization and deep query optimization across engines.

  4. 4

    Typed query builders and data-access objects (e.g., Drizzle-style schema-driven typing) connect database structure to application compile-time feedback.

  5. 5

    Higher-level platforms like Convex aim to prevent client/server out-of-sync bugs by treating data updates as pipeline outputs with transaction-aware synchronization.

  6. 6

    Debugging improves when application logic is structured as clear input/output pipelines rather than stateful service objects with hidden internal state.

  7. 7

    The practical consensus is narrow: avoid writing raw SQL strings directly in application code, even if SQL stays central as the database’s query language.

Highlights

SQL’s internal execution path (parse → plan → engine bytecode) is used to argue that apps shouldn’t expose the SQL text as their interface.
The security discussion shifts from “SQL injection is inevitable” to “untrusted input is dangerous everywhere,” with XSS offered as a more common real-world risk.
Typed query builders are praised for turning schema changes into compile-time errors rather than runtime surprises.
Convex is presented as a way to eliminate a whole class of out-of-sync UI problems by pushing updates automatically after database mutations.
The transcript’s architecture preference is pipeline-style logic: explicit inputs, database state changes, and deterministic outputs that make bugs easier to trace.

Topics

  • SQL Abstraction
  • SQL Injection
  • Type-Safe Query Builders
  • Database State
  • Client-Server Sync
  • Convex
  • Gel
  • Debuggability

Mentioned