On 19 August 2026 I wrote my first Go program. It printed three lines to a terminal. Three weeks later there are 83 files and about 2,300 lines of Go on my machine, and this post is the start of writing down what happened in between.
Why Go
I wanted to understand what sits behind an application — not the part a user sees, but the part that takes a request, talks to a database, does several things at once, and stays up while doing it.
Go pulled me in for a few reasons:
- The syntax is small enough to hold in your head
- Concurrency is built into the language, not bolted on
- It compiles fast enough that you stay in the loop
- The standard library covers most of what a backend needs
- It is what a lot of infrastructure is actually written in
What I did not expect to like as much as I do: Go pushes you toward the plain solution. There is usually one obvious way to write something, and it is usually the boring one.
The first program
Here it is, exactly as I wrote it:
package main
import "fmt"
func main() {
fmt.Println("Hello Shashwat")
fmt.Println("Today 19Aug i started learning Go")
fmt.Println("I think it is Cool")
}Nothing clever. But it taught me four things I have used in every file since:
- An executable program starts with
package main main()is where it begins- Packages like
fmtare imported and reused go runcompiles and runs in one step, fast enough that you keep trying things
The third line is indented with spaces while the others use a tab. I have left
it that way. gofmt would fix it in a second, and that is roughly how long it
took me to learn that gofmt exists.
Where I have got to
Three weeks in, sorted by folder:
| Topic | Files |
|---|---|
| Basics — variables, types, loops, defer | 13 |
| Functions, multiple returns, variadics | 8 |
| Pointers and pointer receivers | 8 |
| Structs and methods | 4 |
| Interfaces | 4 |
| Maps | 2 |
Errors, wrapping, errors.Is |
11 |
| Goroutines, WaitGroup, Mutex | 10 |
Channels, buffered and unbuffered, select |
8 |
HTTP APIs with net/http, and PostgreSQL |
13 |
Some of these took several attempts. Pointers made sense on the fourth try. Unbuffered channels made sense only after I wrote a deadlock and had to sit and work out why the program had simply stopped.
Why write any of this down
Because the useful part is not the working code. It is the twenty minutes before it worked.
I already have a file called unbuffered_channel_deadlock.go. I have another
where I built an error, forgot to return it, and let a bank account go to
minus a thousand rupees. Those are the parts worth keeping, and they are
exactly the parts that vanish once something works.
So this blog is a log, not a tutorial. Posts will cover:
- A concept I have just understood, and what finally made it click
- The code that got me there
- The bug I hit, and why it happened
- Backend projects as I build them
I am not writing these as an expert. I am writing them as someone three weeks in, who would rather have a record than a clean-looking résumé.
Next
The goal now is to stop writing standalone files and build one real thing.
What I want to learn next:
- Structuring a Go application into packages
context.Context— cancellation and timeouts- Testing, properly
- REST API design beyond a single handler
- PostgreSQL with transactions
- Authentication
- Docker, and getting it deployed
That is the plan. This is entry one.