Go: from zero to your own blog
Fifty lessons, with an announcement and a preface beside them. At the end your own blog runs on the internet at your own domain. Every lesson is open: free, no sign-up.
·
Every lesson is open: reading needs no account.
-
A free programming course in Kazakh: Go from scratch
A course of 50 lessons: from "how the internet works" to a blog of your own, written in Go and live on the internet at your own domain. The course is written in full, and reading it needs neither an account nor a payment. Kazakh first, with Russian and English beside it. Inside: what comes out at the end, why Go, and who writes in it.
-
Before you start: what the course needs and what it does not
A preface to the Go course. What you actually need — a computer, the right to install software and half an hour a day — and what you do not: English, mathematics or a powerful machine. Which terminal you are in and why that matters, what to do when installing software is forbidden, how much of it is free, and what the last step actually costs.
-
How the internet works: HTTP request and response in 15 min
The first lesson of the Go course. What a browser asks a server and what counts as an answer: client and server, domain, IP and port, method and path, headers, and the codes 200, 404 and 500. One line in a terminal shows you a real HTTP response before you have read a word about it. School never taught you any of this.
-
Installing Go and setting up VS Code: your workspace
The second lesson of the Go course. What to install before the first line of code: Go 1.27 itself, the VS Code editor with the Go extension, a project folder and the go mod init command. We sort out which tool is answerable for what — language, editor, extension, terminal — and end with your first program greeting the world.
-
Your first Go program: a net/http web server in 20 minutes
The third lesson of the Go course. A first program in Go and a working web server: package main, func main, http.HandleFunc, http.ListenAndServe and port 8080. Open localhost:8080 in a browser and see your own program answer it before you have learned a single rule of the language. No framework to install, and what to do if 8080 is taken.
-
Variables and types in Go: string, int, float64 and bool
The fourth lesson of the Go course. What a variable is and why the language insists on types: string, int, float64 and bool, declaring with := and var, zero values, and why a type is chosen once and never changes. We also read the "declared and not used" error you will meet on day one, and how to name things readably.
-
Functions in Go: parameters, return and two values at once
The fifth lesson of the Go course. How to declare a function, and what a parameter, an argument and return actually are. The heart of the lesson: a Go function returns two values at once — the result, and whether it worked. That is why the language has no exceptions. We read the result, ok := idiom and the underscore.
-
Branching and loops in Go: if, else and the only for
The sixth lesson of the Go course. How a program picks between two paths: if, else if and else, comparisons, and the AND, OR and NOT that join them. And the loop — Go has exactly one, the word for, in three shapes: with a counter, on a condition, and endless with a break. The language has no while, no do-while and no foreach.
-
Strings and runes in Go: why a Kazakh letter is two bytes
The seventh lesson of the Go course. Why len("шаңырақ") comes back as 14 rather than 7, how a byte differs from a letter, and what a rune is. Walking a string with range, counting letters honestly with utf8.RuneCountInString, and the trap that matters: cutting a string by bytes splits a Kazakh letter clean in half.
-
Arrays and slices in Go: a list that grows, append, len and cap
The eighth lesson of the Go course. The array a slice is made of, and the slice itself: []string, append, and why the result must always be assigned back. How an array differs from a slice on assignment, how length differs from capacity, and the trap that matters most — a piece of a slice looks into the same memory as the original.
-
Maps in Go: key and value
The ninth lesson of the Go course. A map is a pair of key and value that finds an entry at once instead of walking a list. How to write and read one, why a missing key hands back zero rather than an error, what comma-ok is, why Go shuffles the iteration order on purpose, and how a nil map is more dangerous than a nil slice.
-
Your first test in Go: go test and the _test.go file
The tenth lesson of the Go course. A check that stays written down: a _test.go file beside the code, a TestXxx function and the go test command. How to read a failure — the test name, the file, the line, "got" and "wanted" — how t.Errorf differs from t.Fatalf, and why you test the edges of a list rather than its middle.
-
Structs in Go: your own type instead of five variables
The eleventh lesson of the Go course. A struct is a type of your own: type Article struct in place of three maps that had to be kept in step by hand. Fields and the dot, literals with and without field names, the zero struct, a slice of structs, and the trap that matters: a struct is passed as a copy, so a change made inside range disappears.
-
Pointers and methods in Go: why the change never stuck
The twelfth lesson of the Go course. The answer to last lesson's trap: a pointer is the address of a value, not a copy of it. How & and * actually work, how a method differs from a plain function, when the receiver takes a pointer and when a value, and why for i := range fixes the very thing that for _, a := range quietly breaks.
-
Interfaces in Go: a contract about what a thing can do
The thirteenth lesson of the Go course. An interface is a list of abilities: one function works with any store as long as the store can do what the contract says. Why implementing is never declared but simply happens, how to have it checked at build time, and why a stand-in store is what makes tests possible without a database.
-
Errors in Go: not an exception, just an ordinary value
The fourteenth lesson of the Go course. An error here does not interrupt the program; it comes back as the second value, and it is the first thing you check. error as an interface with one method, errors.New and fmt.Errorf, wrapping with %w and errors.Is, your own error as a value — and why panic is not for any of this.
-
Packages and modules in Go: go mod and the capital letter
The fifteenth lesson of the Go course. A package is a folder, and a capital letter in a name is the door out of it. How to split a program across folders, what go.mod and go.sum hold, how to bring in someone else's package with go get, what the internal folder is for, and why go mod tidy is run before the code goes anywhere.
-
Git: the history of your code and the way back
The sixteenth lesson of the Go course. Git keeps not the latest version of a file but every state you marked, and any of them can be brought back. git init, the three places — working folder, index and history — git status as the command you will type most, what to write in a message, what .gitignore is for, and how to fetch yesterday's file.
-
GitHub: code that people can see
The seventeenth lesson of the Go course. GitHub is not a backup but the same history, made visible to other people. An account and a first repository, git remote and git push, why a password is no longer accepted there and what replaced it, the README as the first thing a person reads, and the things that must never be published anywhere public.
-
Putting it together: a console notebook out of what you know
The eighteenth lesson of the Go course and the end of the module on the language. One finished program made of familiar parts: a struct, a slice, a map, methods with a pointer receiver, errors, a package of your own and tests. Nothing new but reading commands from the keyboard: putting what you learned into a working thing is a skill of its own.
-
HTTP for real: method, path, headers and status code
The nineteenth lesson of the Go course and the start of the module on the web. A request is a struct you can read: method, path, headers, parameters. A response goes out one way — headers, status, body, in that order. What happens to a header set too late, why 404 and 405 are different answers, and what the superfluous WriteHeader message means.
-
Routing in Go: who handles which address
The twentieth lesson of the Go course. ServeMux sends requests to handlers: the method and the path are written on one line, {slug} pulls a piece out of the address, and {$} stops the front page from catching everything. The router answers 404 and 405 on its own, and two patterns that conflict are caught at start-up rather than in production.
-
Handlers and middleware: one wrapper for every route
The twenty-first lesson of the Go course. A wrapper around the router does the same thing for every request: a log line, a response time, a check. What http.Handler is, why a wrapper takes a handler and returns a handler, which order two of them run in, and how to learn the status code that ResponseWriter refuses to hand back.
-
Templates in Go: html/template and a page instead of strings
The twenty-second lesson of the Go course. HTML moves out of the code into a file of its own: the dot as the name of the data, range over a slice, if and else. Why html/template makes somebody else's text safe for you, why templates are parsed once at start-up, and why a page is assembled in a buffer rather than straight into the response.
-
Forms in Go: taking what the reader sent
The twenty-third lesson of the Go course. For the first time data travels the other way — from the reader to the server. The method and the field names, r.FormValue, why a check in the browser is not protection, and why a handler that changed something answers with a redirect instead of a page: otherwise F5 adds a second article.
-
Static files in Go: CSS, fonts and images
The twenty-fourth lesson of the Go course. The site starts to look like something instead of merely working. http.FileServerFS, why files from disk need StripPrefix and embedded ones do not, where Content-Type comes from, why the whole static folder is on show, and why the browser downloads an embedded file again every single time.
-
Errors and logs in Go: seeing what broke
The twenty-fifth lesson of the Go course. A panic in a handler does not bring the server down — it brings the answer down, and the reader gets nothing at all. recover in one wrapper, a reference number on the page and in the log, log/slog instead of printing to the console, and why an answer that has already left cannot be rescued.
-
Configuration in Go: flags, environment variables and a password outside the code
The twenty-sixth lesson of the Go course. The address, the database file and the signing key move out of the code. The flag package and what -h gives you, why an environment variable makes a convenient default for a flag, how an empty variable differs from an unset one, and why a missing required setting should stop the program at start-up.
-
SQL: the first table, INSERT and SELECT
The twenty-seventh lesson of the Go course. Articles finally survive a restart. Why the course uses SQLite rather than Postgres, how to create a table and put a row into it: insert with returning, select with where and order by, an update whose where was forgotten, and the difference between = null and is null.
-
SQL: two tables, JOIN and foreign keys
The twenty-eighth lesson of the Go course. An article acquires comments, and they do not fit inside it. A second table and the key between them, join against left join, cascading deletes, and the SQLite trap that leaves foreign keys silent and rows orphaned.
-
SQLite and database/sql: the first row from a database, not a slice
The twenty-ninth lesson of the Go course. The blog finally reads its articles out of a database file. The package and the driver, why sql.Open opens nothing, what a connection pool is, and why the foreign-keys pragma belongs in the connection string rather than in a query of its own. Plus ErrNoRows and checking for an error after the loop.
-
CRUD: four actions on an article, and life without a slice
The thirtieth lesson of the Go course. Articles move out of a slice and into a database, and survive a restart. A Store type that hides the database from the rest of the code, four actions on an article, and one non-obvious thing worth a section of its own: updating what is not there is not an error, and RowsAffected is the only way to find out.
-
Migrations: the history of a database, not `create table if not exists`
The thirty-first lesson of the Go course. The program stops creating the schema at start-up: numbered migration files appear, a journal of what has been applied, and the rule that each one runs exactly once. Plus a measured trap: a not null column with no default is accepted by an empty database and refused by a working one.
-
Transactions and SQL injection: a query is not a string you glue together
The thirty-second lesson of the Go course. Two rules that keep a database whole. Data is never glued into the text of a query: a tampered address returns three rows instead of one, and through a parameter it returns none. And what has to happen together is wrapped in a transaction: without one, a counter went up for a move that never took place.
-
Tags: the third table nobody sees on the page
The thirty-third lesson of the Go course. An article has many tags and a tag has many articles — and that is kept in a table of its own rather than a list in a column. A composite key, on conflict for the sake of an id, a cascade on delete. And the promised way to ask an error for its code instead of reading its text.
-
Search: FTS5, and what a parameter does not save you from
The thirty-fourth lesson of the Go course. A search that works: an FTS5 virtual table, three triggers, ranking and highlighting. Plus a measured surprise: a parameter protects you from injection, but not from a person typing the word OR into the search box.
-
Layout: one frame for every page
The thirty-fifth lesson of the Go course. Seven templates in the blog hold seven copies of the same start of a page. Today one frame is left: base with blocks, the folders pages and partials, semantic markup — and the measured trap that makes every page in somebody's blog suddenly show the same one.
-
Registration: what is kept instead of a password
The thirty-sixth lesson of the Go course. A password is not kept in the database; a bcrypt hash is, with the salt inside it and the cost outside. Measured: sha256 does nine million hashes a second, bcrypt does ten. Plus the 72-byte limit a Kazakh password runs into at its thirty-seventh letter.
-
Sessions and cookies: how a server remembers who signed in
The thirty-seventh lesson of the Go course. HTTP remembers nothing: every request arrives from a stranger. A cookie holding a user number is forged in a second. So the browser carries a random token, the database keeps its hash, and three words close the rest: HttpOnly, Secure, SameSite.
-
Permissions: who may change what
The thirty-eighth lesson of the Go course. Hiding a button is not a guard: it is measured here how a stranger edits your article with a direct request while never seeing the button. The check lives in the handler and in one place, and the refusals differ: a guest is sent to sign in, a stranger gets 403, and what does not exist gets 404.
-
CSRF and XSS: three guards everything gets
The thirty-ninth lesson of the Go course. A stranger's script on your page, and another site submitting a form in your reader's name. Measured here: how html/template escapes by context — in text, in a link and inside a script — and how a token tells your form from somebody else's.
-
Uploading images: somebody else's file on your disk
The fortieth lesson of the Go course. A form with a file in it, and four things checked before that file reaches the disk: its size, its type by content, a name of our own instead of the one sent, and where to put it. And /static gets a shape at last: brand, css, images, js, uploads.
-
Tests: knowing what an edit broke
The forty-first lesson of the Go course. The table promised back in lesson 10: the cases go into a list, t.Run gives each of them a name, and a failure says which case broke. Plus checking a handler without starting a server, and an honest word about what -cover really shows.
-
Goroutines and channels: what the web needs them for
The forty-second lesson of the Go course. Your server has been concurrent since the HTTP lesson and you did not choose it: every request is a goroutine of its own. Measured here: a counter without a lock first loses one reading out of five hundred, then brings the whole server down, and go test -race finds it beforehand.
-
`context`: stopping work in time
The forty-third lesson of the Go course. The reader closed the tab, and the handler learns of it by itself: measured here, it drops the work after two hundred milliseconds. A deadline for a query, a key of your own type instead of a string, and the promised move of the signed-in person into the request's context.
-
JSON and an API of your own: a blog is not read by browsers alone
The forty-fourth lesson of the Go course. The tags on a struct decide what a field is called outside and what is hidden altogether; an extra field in somebody's JSON stays silent unless you ask otherwise; a number with no type becomes a float64. And a handler whose errors are JSON too.
-
Your own logo, name and icon: the blog becomes yours
The forty-fifth lesson of the Go course. The blog wears somebody else's name, mark and colours — until this lesson. The name moves out of eleven templates into one setting, the logo becomes your file, the palette is nine lines, and the tab icon becomes yours. The contrast is counted too, and two mistakes of ours are fixed.
-
Putting the blog where people can reach it: free hosting
The forty-sixth lesson of the Go course. A host decides three things for you: the port arrives in PORT, the service has to listen on every address rather than on itself, and the disk of a free plan is wiped on every deploy — measured: the same password gives 303 before and 401 after. Plus a build into one static file.
-
A domain of your own and HTTPS: so the blog is opened by name
The forty-seventh lesson of the Go course. A domain is one record in DNS that points at your address, and HTTPS is not a tick-box but a necessity: measured, in an open channel a reader's password is there in full, all 222 bytes of it, and in TLS it is not in those bytes at all. Plus a proxy in front and a header nobody should trust.
-
A service that survives a reboot: systemd and a tidy stop
The forty-eighth lesson of the Go course. A program is stopped by a signal rather than a button, and the difference is measured: cut off, a reader gets an EOF; stopped tidily, an answer of 200 after 270 ms. Plus a unit file the real systemd checked, a start after every reboot, and the order in which things close.
-
Before the launch: a checklist, not a hope
The forty-ninth lesson of the Go course. A server with no timeouts holds a connection for ever — measured: a slow client took one, and it was we who gave in first. Plus a copy of the database taken while the blog was running, a description of every page for search, and a list of thirteen items to go through before handing anyone the link.
-
Where to go next: generics and the road after the course
The fiftieth and last lesson of the Go course. Generics are one function instead of three, and the condition on the type is checked by the compiler: measured, a `string` does not get through before the program runs. Measured too is what they gain over `any`: 534 nanoseconds against 1801. Plus where to go from here.