Stage 1 · Code
Working with Databases
Raw SQL, Query Builders, and ORMs
There is no single 'grown-up' way to access a database. This lesson shows what you gain and give up with raw `database/sql`, a structured query builder style, and a full ORM approach.
Three Ways to Talk to a Database
Think about three different ways to give directions in a city. First, you can name every turn yourself: left on Oak Street, right at the pharmacy, second driveway after the bridge. That is raw SQL. Second, you can fill out a structured route form: destination, avoid tolls, shortest path, scenic route. That feels like a query builder. Third, you can tell a driver, 'Take me to the station,' and trust a larger system to choose the streets for you. That is closer to an ORM.
None of those are automatically right or wrong. The useful question is where you want the complexity to live. Raw SQL keeps the database conversation extremely visible. Query builders reduce hand-written string assembly. ORMs reduce boilerplate by mapping rows to structs and relationships more automatically. Each choice changes how much control, surprise, and convenience your team will live with every day.
| Approach | Main strength | Main cost | Common fit |
|---|---|---|---|
Raw database/sql | Full control over SQL and performance | Most boilerplate for scanning and query assembly | Teams that care deeply about explicit queries |
| Query builder | More structured query construction without hiding SQL entirely | Still some ceremony and another abstraction to learn | Teams with lots of dynamic filters |
| ORM | Less repetitive CRUD code and automatic struct mapping | More magic, surprise queries, and harder edge-case tuning | Teams optimizing for speed of common data-access work |
Raw SQL and a Small Builder
Raw database/sql is the simplest to reason about because the SQL is exactly what runs. That is especially helpful when queries are performance-sensitive or already complex. The tradeoff is repetition: add a filter, update the SQL string; add a returned column, update the scan targets; add another sort option, update more string-building logic.
A query builder sits one step higher. It usually does not pretend SQL disappeared. Instead, it helps you assemble valid SQL more systematically. Even a tiny homemade builder can show the idea: you describe columns, table, and conditions in smaller pieces, then let the builder join them into one statement and one argument list.
An ORM goes further. It often wants to map table rows onto structs, infer relationships, and give you higher-level operations like 'load this project with its tasks.' That can remove a lot of repetition. But the more work the tool does for you, the more important it becomes to understand what SQL it is secretly generating on your behalf.
The N+1 Query Problem
The classic ORM surprise is the N+1 query problem. The name sounds abstract, but the shape is concrete: one query to load a list of parent rows, then one extra query per parent while you loop and fetch each child set. If there are 25 parents, you do 26 queries. If there are 200 parents, you do 201 queries. The code may look innocent while the database groans.
Eager loading is the common escape hatch. Instead of waiting until the loop to fetch child rows, you ask for related data up front, often with a join or a second batched query. The goal is not 'always one query.' The goal is 'do not let query count quietly scale with the number of parent rows unless that was a deliberate choice.'
Choosing the Right Level of Abstraction
A practical decision framework starts with three questions. First: how complex are the queries? Second: how many teammates will be reading and changing the data-access layer? Third: how sensitive is the workload to hidden performance costs? The answers often matter more than ideology.
- Choose raw
database/sqlwhen query shape and performance visibility matter most, or when your team already thinks comfortably in SQL. - Choose a query builder when you have many optional filters, sorts, or search forms and you want structured composition without losing sight of the SQL.
- Choose an ORM when most work is straightforward CRUD, team speed matters more than hand-tuned SQL in the common case, and the team is willing to learn how to inspect generated queries.
Convenience is real, but so is surprise. If an abstraction saves hours during feature work and costs days during incidents, the tradeoff may not be worth it for that service.
Quiz and Practice
What does a query builder usually try to improve compared with raw SQL?
Hands-On Project
Finish the builder's `Where` method so it appends the condition, stores the argument, and returns the builder for chaining.
Summary and Key Takeaways
- Raw SQL gives maximum visibility and usually the least abstraction surprise.
- Query builders help compose dynamic SQL without fully hiding the database model.
- ORMs reduce repetitive mapping work, but can generate unexpected queries if you stop paying attention.
- The N+1 query problem is about query count scaling with parent rows, not just about one slow query.
- Pick the abstraction your team can understand, tune, and debug under real pressure.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.