Why we import SQL drivers as the blank identifier ( _ ) in Go

When I first started writing Go code I was working on a relatively simple web application backed by an SQL database, so the first thing I did was Google “golang sql” which lead me to to the database/sql package.

While reading those docs I notice this line in particular.

The sql package must be used in conjunction with a database driver. See https://golang.org/s/sqldrivers for a list of drivers.

What? I need something else to go along with the database/sql package?

“No worries”, I thought to myself. “I’ll just follow the link and use one of the drivers.”

So I did just that. I clicked the link, found the driver I needed, installed it with go get and then started looking for some code samples to get started. After a few minutes I ran into this little beauty.

import (
  "database/sql"

  _ "github.com/lib/pq"
)

At this point I stopped typing and just stared for a minute. Why the hell am I including a package that doesn’t get used? What purpose could that possibly serve? I thought Ruby on Rails had an exclusive license on being magical.

After that I kinda just let it go. I didn’t really need to understand what was going on for my code to work, so I just let it be.

Everything was jolly until I recently started writing the book Web Development with Go; I was working on a chapter that introduces using the database/sql package and thought to myself, “How can I possibly expect every reader to just let this go? Surely some of them will be asking questions, and others will forget about this unused import. I need to address it.” So that is what I am doing today. Addressing the question on many developers mind when they start using SQL with Go:

Why am I including a package that never gets used in my code?

Digging into the database/sql/driver package

My first stop in understanding this was the database/sql package, which quickly lead me to the database/sql/driver package.

Apparently this mystery had something to do with implementing the driver package, so it sounded like a sane place to start.

If you look through the database/sql/driver package you will quickly notice that there really isn’t much functional code there. Instead we have a large variety of interfaces being declared without any implementations.

Every database would need to implement each interface a little differently, so it makes sense to provide a common interface that each SQL variant can implement, but nothing here seemed to answer my question. I still didnt know how my code was actually learning about the driver being imported, so I headed back to the database/sql package.

Aha! I found it!

And then I saw it. The second function listed in the package answered all of my questions. Register(name string, driver driver.Driver) needed to be called to tell the database/sql package about a driver.

After that all I needed was a simple search on the github.com/lib/pq package to verify my suspicions.

The reason we don’t actually need to do anything with an sql driver is because the init() function gets run before our own main() function ever gets run. This means means that before our code ever runs the lib/pq package registers an sql driver that our code then uses, regardless of whether or not we directly interact with the lib/pq package.

That is why we need to pass a driver name as the first argument of the sql.Open() function.

Nothing magic is happening; Instead it is just looking up the driver from an internal map that stores all registered drivers along with their name.

And this is just one reason why I love Go

I know this isn’t revolutionary by any means, but what really inspires me about this personal story is just how easy it is to navigate code written in Go. It took me less than 15 minutes of digging through both standard libraries and third party packages to find out what was going on and to truly understand what was happening.

That is unheard of in most other languages. I have dug through countless Ruby and JavaScript libraries trying to debug issues or figure out why something was behaving weirdly only to jump through 10 layers of abstraction, monkey patching, or whatever else. If I am lucky I eventually find what I was looking for, but I end up just giving up more often than I care to admit.

It simply isn’t easy to dig through a complicated library or application that you didn’t write in most other languages, but in Go it truly is.

That simply blows my mind after so many years of using languages where it wasn’t true.

So I just want to say kudos to the people behind Go. I am both impressed and thankful that you work so hard to keep Go as approachable as it is. Oh, and you should try out Go if you haven’t already 😉

Learn Web Development with Go!

Sign up for my mailing list and I'll send you a FREE sample from my course - Web Development with Go. The sample includes 19 screencasts and the first few chapters from the book.

You will also receive emails from me about Go coding techniques, upcoming courses (including FREE ones), and course discounts.

Avatar of Jon Calhoun
Written by
Jon Calhoun

Jon Calhoun is a full stack web developer who teaches about Go, web development, algorithms, and anything programming. If you haven't already, you should totally check out his Go courses.

Previously, Jon worked at several statups including co-founding EasyPost, a shipping API used by several fortune 500 companies. Prior to that Jon worked at Google, competed at world finals in programming competitions, and has been programming since he was a child.

Related articles

Spread the word

Did you find this page helpful? Let others know about it!

Sharing helps me continue to create both free and premium Go resources.

Want to discuss the article?

See something that is wrong, think this article could be improved, or just want to say thanks? I'd love to hear what you have to say!

You can reach me via email or via twitter.

Recent Articles All Articles Mini-Series Progress Updates Tags About Me Go Courses

©2024 Jonathan Calhoun. All rights reserved.