Stop Fighting Go and Started Listening
For a long time, I wrote Go the way I wrote other languages: by instinct, by habit, and, honestly, by muscle memory from years of reaching for dependencies to solve my problems.
It wasn’t that I didn’t trust Go’s standard library. I just didn’t think about it. Need logging? Grab a package. Need an HTTP client? Surely, there’s a better one than the built-in net/http
. I treated the standard library like a set of training wheels, something to outgrow once I needed “real” functionality.
But Go had other plans for me.
The Moment It Clicked
It started with a simple problem: logging.
I needed to log messages to a file. Easy enough. I wrote a function like this:
func logMessage(message string, file *os.File) {
timestamp := time.Now().Format("2006-01-02 15:04:05")
logLine := fmt.Sprintf("%s %s\n", timestamp, message)
file.Write([]byte(logLine))
}
It worked. But something about it felt… brittle. What if I wanted to log to the console instead? Or store logs in memory for testing? Or send them to a remote service? Each new destination meant rewriting this function.
That’s when I noticed something about os.File
. It implemented io.Writer
. And io.Writer
wasn’t just for files—it was for anything that could accept a stream of bytes.
So I rewrote the function:
func logMessage(message string, writer io.Writer) {
timestamp := time.Now().Format("2006-01-02 15:04:05")
logLine := fmt.Sprintf("%s %s\n", timestamp, message)
writer.Write([]byte(logLine))
}
And just like that, my little function transformed.
Now, it didn’t care where the logs went. It could write to a file, sure, but also to os.Stdout
, a bytes.Buffer
, or even a network connection. I hadn’t added complexity. I had removed assumptions and in doing so, I had made my code far more powerful.
Go Was Trying to Teach Me Something
I had spent so much time trying to make Go work the way I wanted that I had ignored the patterns it was trying to show me.
Interfaces like io.Writer
weren’t just there for convenience; they were a way of thinking. A way to decouple code, to make it composable, to let it flow.
I started seeing the same lesson everywhere:
- The
http.Handler
interface made middleware trivial. io.Reader
andio.Writer
allowed me to compose data transformations like UNIX pipes.- The built-in
context
package helped me control timeouts and cancellations without bolting on extra dependencies.
Go’s standard library wasn’t just a set of tools—it was a philosophy. One built around simplicity, composability, and minimal assumptions.
The Shift in Mindset
I used to reach for dependencies first. Now, I pause. I look at the standard library. I ask myself, What is Go trying to show me here?
Most of the time, the answer is already there—I just wasn’t listening.
Takeaway: The real power isn’t in external libraries; it’s in understanding the language’s own patterns. Once you stop fighting Go and start listening to it, everything changes.
Did I make a mistake? Please consider Send Email With Subject