description:
Insights on pointers, methods, resource management, and HTTP clients while building an open-source Go library.
tags:
golang, backend, webdev, learning
A few days ago, a couple of friends and I decided to take a detour from our usual routines to build something practical: a Go library for payment APIs.
The goal is simple. Instead of every Go developer writing custom, repetitive code to integrate payment gateways, they can just import our open-source library straight from GitHub.
Building a library forces you to think deeply about architecture, developer experience, and language fundamentals. Here is everything this project has taught me so far.
1. Client Structure and a Sharper Grip on Pointers
Setting up the base client structure required a lot of intentional data modeling. In doing so, I gained a much clearer understanding of pointers in Go. Deciding when to pass a struct by value versus passing a pointer to mutating states is a critical choice when designing an API client that other developers will rely on.
2. Deconstructing the Authentication Flow
Implementing authentication gave me a front-row seat to the full request-response lifecycle. I broke down the exact steps needed to establish a secure connection:
- URL Construction: Building clean, dynamic endpoints.
- Request Creation: Preparing payloads and setting secure headers.
- Validation: Verifying our data before it leaves the application.
- Transmission & Response: Sending the request to the admin server and handling the incoming payload cleanly.
3. Functions vs. Methods: The Concrete Difference
While the terms are often used interchangeably in other languages, Go draws a distinct line between functions and methods.
- A Function is a standalone block of code used to execute a program routine.
- A Method is simply a function that contains a receiver, meaning it belongs to a specific type.
// This is a standalone function
func ClearCache() {
// logic here
}
// This is a method belonging to the 'User' struct type
func (u *User) DeactivateAccount() {
// logic here
}
4. Resource Management: Why You Must defer Body.Close()
One of the biggest gotchas for newer Go developers is resource leakage via HTTP responses. When you make a network request, the server keeps the connection open until you explicitly close it.
Using defer response.Body.Close() right after checking for errors is mandatory.
Think of it like borrowing a book from a library. If you keep borrowing books and never return them, your state keeps looping on "borrowing." Eventually, the library runs out of space, and you cannot borrow anymore. In Go, failing to close the body will cause your application to run out of memory and file descriptors.
5. Staying Flexible with http.Client.Do()
When interacting with different payment endpoints, you encounter various HTTP verbs like POST, GET, PUT, and DELETE.
Instead of wrapping our client in strict, specialized helpers, we relied on Go's native http.Client.Do() method. Because .Do() accepts a completely custom *http.Request object, it accommodates any HTTP method seamlessly. If an upstream payment provider suddenly changes a route from a POST to a PUT, our underlying architecture remains intact and resilient.
Wrapping Up
This project is turning out to be an amazing engineering journey. By building this library, I am moving past just "using" payment APIs to truly understanding how they function under the hood.
I'll be sharing more updates as my team moves closer to our alpha release. If you have any tips on building clean SDKs in Go, let me know in the comments below!

Top comments (0)