Back to campField Journalby Zin
All entries

go · typescript · javascript

Go: Zero to Hero, for JavaScript & TypeScript Developers

A guided walk from your first Go program to goroutines, generics and a JSON API — every example explained line by line, and runnable right on the page.

23 min read0 likes

Go: Zero to Hero, JavaScript & TypeScript developer တွေအတွက်

ပထမဆုံး Go program ကနေ goroutine၊ generics နဲ့ JSON API အထိ တစ်ဆင့်ချင်း လမ်းပြပေးမယ့် guide — example တိုင်းကို line by line ရှင်းပြထားပြီး ဒီစာမျက်နှာပေါ်မှာတင် တိုက်ရိုက် run ကြည့်လို့ရပါတယ်။

ဖတ်ချိန် 25 မိနစ်0 likes

If you already think in JavaScript and TypeScript, you know more Go than you expect. Functions, closures, JSON, HTTP, async work: the ideas carry over. What changes is the shape: Go is compiled, statically typed without a separate type layer, and built around a very small set of features that fit together tightly.

This guide goes from zero to the point where you can read real Go code and write a small service. Each section sets a Go idea beside the JS/TS one you already know, and every example has line-by-line notes under it.

Why Go, for a JS/TS developer

Go was designed at Google in 2007 by Robert Griesemer, Rob Pike and Ken Thompson and released as open source in 2009. Its goal was practical: fast builds, simple code that large teams can read, and first-class concurrency for networked servers.

Here is the mental shift, side by side:

  • Runtime → binary. Node runs your JS on V8 with a JIT. Go compiles ahead of time to a single native binary: no node_modules on the server, no runtime to install.
  • Types are the language. TS types are erased at build time; Go types exist at run time and decide memory layout. No any escapes by accident, and no type gymnastics either.
  • Errors are values. No throw; functions return an error and you check it.
  • Concurrency is built in. Instead of one event loop with async/await, Go runs thousands of cheap goroutines across all CPU cores and connects them with channels.
  • One way to do things. One loop keyword (for), one formatter (gofmt), a big standard library. Less choice, less bikeshedding.

Hello, Go

Every runnable Go program is a main package with a main function. Run it, then try changing the name.

goEditable: change it and press Run

Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor

Line by line

  1. Lines starting with // are comments, exactly like JS.
  2. Every file belongs to a package: Go's unit of code, like an ES module folder. main is special: it builds into an executable rather than a library.
  3. Imports name packages by path. fmt (formatting and printing) and strings come from the standard library: no npm install. An unused import is a compile error, so imports never rot.
  4. func declares a function. main() in package main is the entry point; the program ends when it returns. There's no top-level code that runs on import, as in a JS module.
  5. := declares a new variable and infers its type (string) from the value: think let name = "Gopher". It only works inside functions.
  6. Sprintf formats into a string with verbs like %s (string), %d (integer), %q (quoted). It plays the role of a JS template literal.
  7. Println prints its arguments with spaces and a newline: console.log. Exported names start with a capital letter; that capital is Go's export keyword.
  8. Package functions are called as package.Name, like a namespace import.
  9. len of a string counts bytes, not characters. Printf prints without adding a newline, so the format ends in \n.

Variables, constants and zero values

TypeScript gives you let, const, and undefined for anything not yet set. Go has no undefined: every variable starts at its type's zero value, so there's always something valid to read.

goEditable: change it and press Run

Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor

Line by line

  1. A const is fixed at compile time and may only hold numbers, strings, booleans or runes, unlike a JS const that can hold an object.
  2. var name Type declares without a value. Type annotations come after the name, as in TS (let count: number), just without the colon.
  3. The zero values: "" for strings, false for booleans, nil for slices, maps, pointers and interfaces. A nil slice behaves like an empty one: you can range over it and append to it.
  4. Prints 0 true false 0. Nothing crashed, nothing was undefined.
  5. Short declarations infer int, float64 and string. Untyped numbers become int or float64 by their look.
  6. %T prints a value's type: handy while learning.
  7. Several variables in one statement; = (not :=) assigns to ones that exist. The swap evaluates the right side first, like [a, b] = [b, a].
  8. Go's types are fixed for a variable's life. views is an int forever; there are no union types, and assigning a string is a compile error.
ts
let count: number;        // undefined until assignedconst tags: string[] = []; // the binding is const, the array isn'tlet views = 42;            // inferred: numberviews = "many";            // error in TS, but legal at run time in JS

Line by line

  1. In TS reading count here is an error, and in JS it's undefined. In Go it would just be 0.
  2. TS stops this at compile time, but the JS it emits would run it. In Go there's no lower layer: the compiled program is exactly as typed as the source.

Numbers, strings and runes

JavaScript has one number (a 64-bit float). Go makes you choose: int, int64, uint8, float64 and more. Strings are immutable bytes, almost always UTF-8, which matters the moment you handle Burmese or emoji.

goEditable: change it and press Run

Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor

Line by line

  1. int is a 64-bit integer on modern machines, float64 is JS's number, uint8 (alias byte) holds 0–255. Explicit types here: := would infer int and float64 anyway.
  2. Go never converts numbers silently. likes * price would not compile; you write the conversion float64(likes). Annoying for a day, then it saves you from whole classes of bugs.
  3. ++ is a statement in Go, not an expression: you can't write x = y++. Fixed-size integers wrap: 255 + 1 in a uint8 is 0.
  4. A Go source file is UTF-8, so this Burmese word is fine. len(word) counts bytes (21), since each Myanmar code point takes 3 bytes in UTF-8.
  5. A rune is one Unicode code point (an int32). Converting to []rune and taking the length counts code points: 7. JS's .length counts UTF-16 units, a third answer.
  6. range over a string decodes UTF-8 for you: i is the byte offset, r the rune. %c prints a rune as a character. This is for...of with indexes.
  7. An array's length is part of its type: [7]string and [6]string are different types. Arrays are rare in everyday Go; you'll use slices (below) instead.

Functions: multiple returns, closures and defer

Functions look familiar, but three features change everyday code: returning several values, defer, and the fact that the type comes after every parameter.

goEditable: change it and press Run

Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor

Line by line

  1. Parameters are name type. The result list (first, last string) declares two named results, both strings. Where TS returns an object or tuple, Go returns several values.
  2. Fields splits on any run of whitespace: full.trim().split(/\s+/).
  3. if takes no parentheses, but braces are required. Early returns are the idiomatic style: keep the happy path unindented.
  4. Returning both values. Go has no negative indexing (parts.at(-1)), so the last element is len(parts)-1.
  5. A variadic parameter: inside, nums is an []int, just like ...nums: number[].
  6. range gives index and value; _ is the blank identifier that throws the index away. Go refuses unused variables, so _ is how you say "I don't need it".
  7. Functions are first-class values. The return type func() int is the TS type () => number.
  8. A closure: the anonymous function captures n and keeps it alive between calls, exactly as in JS.
  9. defer schedules a call for when the surrounding function returns, even if it panics. It's Go's finally, usually written right after opening something: f, _ := os.Open(...), then defer f.Close().
  10. Receiving two results. If you only wanted one: first, _ := split(...).
  11. slice... spreads a slice into a variadic call: sum(...[10, 20]) in JS.
  12. Each counter() call makes a fresh n. Three calls later it prints count: 3, and only then does the deferred line run.

Errors are values, not exceptions

This is the biggest habit change. Go has no try/catch for ordinary failures. A function that can fail returns an error as its last result, and the caller decides right there what to do.

goEditable: change it and press Run

Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor

Line by line

  1. A sentinel error: one shared value that callers can compare against. By convention its name starts with Err.
  2. A custom error type carrying data. Any type with an Error() string method is an error; there's no extends Error.
  3. A method: the (e *ValidationError) before the name is the receiver, Go's this. Having this method makes the type satisfy the built-in error interface.
  4. The Go signature for "may fail": the value plus an error. In TS you'd write Promise<string> and hope callers remember the catch.
  5. Atoi (ASCII to integer) returns (int, error). The if err != nil { return ... } shape is everywhere in Go: explicit, greppable, and nothing is hidden in a stack unwind.
  6. %w wraps an error: the message gains context (post 7: not found) while the original stays inside for checking. Like new Error(msg, { cause }).
  7. Success returns nil as the error. nil is the zero value for interfaces like error.
  8. A variable for errors.As to fill in if the error turns out to be this type.
  9. A switch with no value tests each case as a boolean: a tidy if / else if chain. Cases don't fall through, so no break is needed.
  10. errors.Is looks through every wrapped layer for that exact value, so the wrapped post 7: not found still matches.
  11. errors.As finds the first error of that type and stores it, like if (err instanceof ValidationError) plus a cast.
ts
try {  const title = await findPost(id);  console.log("found:", title);} catch (err) {  if (err instanceof NotFoundError) console.log("404");  else throw err;}

Line by line

  1. The TS version: failure travels invisibly up the stack until something catches it. The signature findPost(id): Promise<string> doesn't tell you it can fail.
  2. err is unknown in a TS catch, so you narrow it with instanceof. Go's errors.As does the same job.

Structs and methods instead of classes

Go has no classes and no inheritance. You define data with a struct, attach behaviour with methods, and reuse code by embedding one struct in another. It turns out you don't miss extends.

goEditable: change it and press Run

Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor

Line by line

  1. type Name struct { ... } is like a TS interface for data, but it's a real type with a fixed memory layout. Name is capitalised, so it's exported.
  2. A method with a value receiver: a is a copy of the Author it's called on. Methods live outside the struct body, anywhere in the same package.
  3. Author with no field name is embedded: a Post gets the Author's fields and methods, so post.Name and post.Byline() work. That's composition with inheritance's convenience.
  4. A pointer receiver (*Post): the method changes the original, not a copy. Rule of thumb: if a method mutates, or the struct is big, use a pointer receiver.
  5. No constructors in Go; a function named NewX is the convention. title, author string shares one type between two parameters.
  6. A composite literal builds the struct with named fields (missing ones get zero values). & returns its address, so callers share one Post.
  7. Go takes the address automatically when calling a pointer method on an addressable value, so the call reads like JS.
  8. Byline is promoted from the embedded Author.
  9. %+v prints a struct with field names: great for debugging, like console.dir. *post reads the value the pointer points at.
ts
class Post extends Author {  likes = 0;  constructor(public title: string, name: string) { super(name); }  like() { this.likes++; }}

Line by line

  1. The class version. extends creates an is-a relationship; Go's embedding is a has-a relationship that still promotes fields and methods.

Interfaces, satisfied implicitly

Good news for TS developers: Go interfaces are structural, just like TS types. A type satisfies an interface simply by having the methods: no implements keyword. The difference is that Go checks it at compile time and keeps the information at run time.

goEditable: change it and press Run

Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor

Line by line

  1. An interface is a set of method signatures. Keep them small: the standard library's most-used interfaces (io.Reader, error, fmt.Stringer) have one method each.
  2. Two unrelated structs. Fields of the same type can share a line: W, H float64.
  3. Each type gets an Area() float64 method, so both now satisfy Shape, without ever naming it. You can even satisfy interfaces from packages written after yours.
  4. Adding String() makes Circle a fmt.Stringer, so fmt.Println prints it this way, like overriding toString().
  5. Accept interfaces, return concrete types: a classic Go guideline. total works for any shape, present or future.
  6. A slice of the interface type holds different concrete types. Rect{3, 4} fills fields in order (fine for tiny structs; named fields are clearer).
  7. A type switch asks which concrete type sits inside an interface. In each case, v has that type: TS-style narrowing, done at run time.
  8. Prints Circle(r=1), because Println noticed the String() method.

Slices and maps: Go's arrays and objects

A slice is what you'll use wherever JS uses an array, and a map is Go's Map/Record. Slices hide one important detail: several slices can share the same underlying array.

goEditable: change it and press Run

Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor

Line by line

  1. The slices package (Go 1.21+) brings generic helpers: Sort, Contains, Index, Reverse, Max…
  2. []int{...} is a slice literal: no length in the brackets. Under the hood: a pointer to an array, a length and a capacity.
  3. append returns the (possibly new) slice: always assign it back. When capacity runs out it allocates a bigger array and copies, much as V8 does inside your arrays.
  4. Sorts in place, numerically. (No JS sort() surprise of comparing numbers as strings.)
  5. Prints [1 2 5 8] 4 6: four elements, room for six before the next reallocation.
  6. The classic gotcha. nums[:2] is a new view onto the same array, so writing firstTwo[0] changes nums too ([99 2 5 8]). JS's slice() copies; use slices.Clone when you need a copy.
  7. No .filter() or .map() methods on slices: a plain loop with append is the idiom, and it's just as fast as it looks. (Generics let you write your own Map; see below.)
  8. A map literal: map[KeyType]ValueType. Like new Map<string, number>() but with literal syntax.
  9. Reading a missing key gives the zero value (0), so incrementing a new key just works: no (m.get(k) ?? 0) + 1.
  10. The comma-ok form tells missing apart from zero. if init; condition scopes n and ok to this if.
  11. delete is a built-in function, map.delete(k) in JS. Map iteration order is deliberately random, so never rely on it.

Pointers, without the fear

JavaScript passes objects by reference and primitives by value, and you never see the difference spelled out. Go makes it explicit: everything is passed by value (copied), and a pointer is how you share.

goEditable: change it and press Run

Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor

Line by line

  1. This receives a copy of the struct. Changing c.N changes only the copy, a real difference from JS, where an object argument is shared.
  2. *Counter is "pointer to Counter": the function gets the address and edits the caller's struct.
  3. Still 0: the copy was bumped and thrown away.
  4. &c takes the address of c. Now it prints 1.
  5. p is a *Counter. There's no pointer arithmetic in Go (unlike C), so pointers are safe: they only ever point at a valid value or nil.
  6. Go dereferences automatically for field access: p.N means (*p).N.
  7. *p reads the whole value behind the pointer. Both print 10: same struct.
  8. A pointer's zero value is nil. Reading a field through a nil pointer panics: Go's version of Cannot read properties of null.

Generics, the Go way

Go added generics in 1.18 (2022). They're deliberately simpler than TypeScript's: no conditional or mapped types, just type parameters with constraints. Enough for containers and helpers, without turning types into a second program.

goEditable: change it and press Run

Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor

Line by line

  1. A constraint is an interface listing types. | reads like a TS union; ~int means "int or any type whose underlying type is int".
  2. Type parameters go in square brackets: Sum[T Number] ≈ sum<T extends number>. Callers rarely write T; it's inferred.
  3. The zero value works for any type, so total starts at 0 for ints and floats alike.
  4. any (an alias for interface{}) is the TS unknown: no constraint at all. This is a typed Array.prototype.map.
  5. make creates a slice with length 0 and capacity len(in): one allocation instead of several as we append.
  6. Generic types too: Stack[T] ≈ class Stack<T>.
  7. Returning (value, ok) instead of T | undefined: the caller can't forget the empty case.
  8. The usual trick for "return nothing of type T": its zero value.
  9. A named type built on float64. It's a different type from float64, but ~float64 in the constraint still accepts it.
  10. T is inferred from the arguments, int then Celsius: prints 6 41.5.
  11. Generic types are instantiated with brackets. The zero Stack is ready to use: no constructor needed.

Concurrency: goroutines and channels

Node gives you one thread and an event loop: await pauses a function, never the process. Go gives you goroutines: functions running concurrently, scheduled by the Go runtime across every CPU core. They start with a few kilobytes of stack, so a server can run hundreds of thousands. Channels pass values between them safely.

Do not communicate by sharing memory; instead, share memory by communicating.

— Effective Go

Run this one and watch the output arrive: the delays are real.

goEditable: change it and press Run

Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor

Line by line

  1. A pretend network call. time.Sleep blocks this goroutine only; the others keep running. There's no async colouring: any function can be run concurrently.
  2. time.Now() and time.Since measure elapsed time, performance.now() style.
  3. A channel of strings, made with make. Sending (ch <- v) and receiving (<-ch) on this unbuffered channel wait for each other: a built-in handoff.
  4. A WaitGroup counts running goroutines: Add before starting, Done when finished, Wait until zero.
  5. An anonymous struct slice: a quick inline list of jobs, like an array of object literals.
  6. The go keyword starts a goroutine: this function runs concurrently and the loop continues at once. Like calling an async function without await. Since Go 1.22 each iteration has its own job, so the closure is safe.
  7. Mark this job finished when the goroutine returns, whatever happens.
  8. Send the result into the channel. It waits until someone receives it.
  9. A helper goroutine waits for all three, then closes the channel to signal "no more values".
  10. range over a channel receives until it's closed. Results arrive in finish order (posts, likes, users), as with for await over settled promises.
  11. About 300 ms, not 600: the three sleeps overlapped. That's Promise.all, without promises.
  12. Start a slow job (800 ms) in the background.
  13. select waits on several channel operations and runs whichever is ready first: Promise.race. time.After returns a channel that delivers after 500 ms, so this is a timeout. (In real code, context.Context carries deadlines and cancellation.)
ts
const results = await Promise.all([  fetchJob("users", 300),  fetchJob("posts", 100),  fetchJob("likes", 200),]);const report = await Promise.race([fetchJob("report", 800), timeout(500)]);

Line by line

  1. The TS equivalent: Promise.all waits for all, but returns results in input order, after the slowest one. Go's channel version handled each as it finished.
  2. Promise.race with a timeout promise: the same idea as select with time.After.

A JSON API with net/http

Go's standard library includes a production-grade HTTP server. Since Go 1.22 its router understands methods and path parameters, so a small API needs no framework, not even an Express equivalent.

goEditable: change it and press Run

Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor

Line by line

  1. Everything here is standard library: JSON, HTTP client and server, and a test server.
  2. Struct tags (the back-quoted json:"id") tell encoding/json which key to use. Exported (capitalised) fields are the only ones encoded.
  3. A handler: like Express's (req, res) => {}, but the order is (w, r), writer first.
  4. Streams the slice as JSON straight into the response: res.json(posts).
  5. Reads the {id} path parameter: req.params.id.
  6. Writes a status code and a body in one call. http.StatusNotFound is the constant 404.
  7. A mux (router): express().
  8. Patterns with a method and wildcards (Go 1.22+): app.get("/api/posts/:id", ...).
  9. In a real program this one line starts the server. Each request runs in its own goroutine automatically, across all cores.
  10. The playground can't open public ports, so httptest starts the server on a local address inside the program: the same tool you'd use in tests.
  11. The built-in HTTP client: fetch, minus the promise. Note the err check again.
  12. Response bodies must be closed to free the connection; in longer code you'd defer res.Body.Close().
ts
import express from "express";const app = express();app.get("/api/posts", (req, res) => res.json(posts));app.get("/api/posts/:id", (req, res) => {  const post = posts.find((p) => String(p.id) === req.params.id);  post ? res.json(post) : res.status(404).json({ error: "not found" });});app.listen(8080);

Line by line

  1. The Express version needs a dependency; Go's needs none. Both are about the same length.
  2. Node serves every request on one thread unless you add clustering or workers; Go spreads requests across all cores by default.

Tooling, modules and tests

The go command replaces a whole JS toolchain. There's no config to choose and nothing to install beyond Go itself.

  • go mod init example.com/app → npm init (creates go.mod, your package.json)
  • go get github.com/x/y → npm install (pins versions in go.mod / go.sum, your lockfile)
  • go run . → ts-node / tsx (compile and run in one step)
  • go build → tsc + a bundler (outputs one static binary)
  • gofmt / go fmt → Prettier (one style, no options, no debates)
  • go vet → ESLint for real bugs (wrong Printf verbs, unreachable code…)
  • go test ./... → Jest / Vitest (built in, plus benchmarks and fuzzing)
  • GOOS=linux GOARCH=arm64 go build → cross-compile for another OS/CPU in one command
bash
go mod init example.com/journalgo get github.com/go-chi/chi/v5go run .go test ./... -race -coverGOOS=linux GOARCH=amd64 go build -o journal-api

Line by line

  1. Starts a module named by its import path (usually where the repo lives).
  2. Builds and runs the package in the current folder.
  3. Runs every test in every package, with the race detector and coverage.
  4. Builds a Linux binary from your Mac: copy one file to a server, or into a tiny Docker image, and it runs.

Tests are plain functions in _test.go files. The playground runs a program that has tests and no main as go test, so you can try this one too: break Slugify and watch it fail.

goEditable: change it and press Run

Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor

Line by line

  1. The standard testing package: no test runner to install. (In a real project this would be package journal in slug_test.go.)
  2. The code under test: lowercase, split on whitespace, join with dashes.
  3. Any func TestXxx(t *testing.T) is a test. t reports failures: no describe/it/expect DSL.
  4. Table-driven tests: a slice of inputs and expected outputs, the most common Go testing pattern.
  5. Compare with plain !=. Go doesn't encourage assertion libraries, so the failure message is yours to write.
  6. Errorf marks the test failed and keeps going, so you see every failing case at once.

How Go is built: the language architecture

Knowing what happens under go build explains most of Go's trade-offs. Compare it with the path your TypeScript takes: tsc erases types, emits JS, and V8 parses, interprets and JIT-compiles that JS while it runs.

1. The compiler: source to one native binary

  1. Parse and type-check every package. Go's grammar was designed to parse fast, and an unused import or variable is an error, so builds stay lean.
  2. Convert to SSA (static single assignment) and optimise: inlining, escape analysis, dead-code removal, bounds-check elimination.
  3. Generate machine code for the target OS and CPU (GOOS/GOARCH), then link everything, including the Go runtime, into a single statically linked executable.
  4. Packages compile independently and are cached, so rebuilds take seconds even on large codebases: Go was born to fix slow C++ builds at Google.

2. The runtime: small, but always there

There's no VM, but every Go binary carries a runtime (a few MB) that provides the scheduler, the garbage collector, goroutine stacks, maps, channels and reflection. Think of it as a tiny, compiled-in slice of what Node gets from V8 and libuv.

3. The scheduler: G, M and P

  • G: a goroutine: its stack, its instruction pointer, its state.
  • M: a machine thread: a real OS thread.
  • P: a processor: a scheduling slot holding a queue of runnable Gs. There are GOMAXPROCS of them, by default one per CPU core.
  • Each M needs a P to run Gs. When a goroutine blocks on I/O, the runtime parks it and runs another; network I/O uses the OS's epoll/kqueue under the hood (the same idea as libuv's event loop), but you write ordinary blocking code. Idle Ps steal work from busy ones, and long-running goroutines are preempted so none can hog a core.

4. Memory and garbage collection

  • Escape analysis decides at compile time whether a value can live on the goroutine's stack (free to clean up) or must go on the heap. Returning &Post{} makes it escape; a local struct used only inside a function doesn't.
  • Goroutine stacks start small (a few KB) and grow and shrink as needed, which is why goroutines are cheap.
  • The garbage collector is concurrent, tri-colour mark-and-sweep, tuned for low latency: pauses are typically well under a millisecond. It trades some CPU and memory headroom for short pauses, which suits servers. GOGC and GOMEMLIMIT tune it.
  • Structs are laid out contiguously, like C structs. A []Point is one block of memory, not an array of pointers to objects: cache-friendly and GC-friendly.

5. The memory model

Go defines exactly when one goroutine's write is guaranteed visible to another: through channel operations, sync primitives or sync/atomic. Outside those, concurrent access to the same variable is a data race, and go test -race will find it.

Pros and cons

Where Go shines

  • Simplicity: about 25 keywords; you can read almost any Go codebase within weeks. Code reviews focus on logic, not cleverness.
  • Performance: compiled, with value types and a low-latency GC. Typically far less memory and CPU than an equivalent Node service, and predictable latency.
  • Concurrency: goroutines and channels make parallel I/O and CPU work natural, using every core with no worker-thread plumbing.
  • Deployment: one static binary, fast cross-compilation, tiny container images (FROM scratch works).
  • Fast builds and great tooling: gofmt, go vet, go test, the race detector, pprof profiling, all built in.
  • Standard library: HTTP/2 server and client, JSON, crypto, TLS, templates, testing: production-ready without dependencies.
  • Stability: the Go 1 compatibility promise means code from 2012 still compiles. No yearly framework churn.

Where it hurts

  • Verbosity: if err != nil everywhere, and loops where JS has .map().filter().
  • A less expressive type system: no union types, no enums (only const + iota), no mapped or conditional types; generics are intentionally basic.
  • nil pitfalls: nil pointers and nil maps panic; an interface holding a nil pointer is not itself nil, a famous trap.
  • No browser story: Go compiles to WebAssembly, but the binaries are large; the front end stays TS/JS.
  • A GC language: great for services, but for hard real-time or the very last bit of latency, Rust or C++ win.
  • Smaller UI and data-science ecosystems than JS or Python.

What you can build with Go

  • Web APIs and microservices: REST, GraphQL, gRPC. Go's sweet spot; Uber runs thousands of Go services.
  • Cloud infrastructure: Docker, Kubernetes, Terraform and Prometheus are all written in Go.
  • Command-line tools: a single binary for every OS; think gh, hugo, k9s.
  • Developer tooling for JS/TS itself: esbuild, and now the TypeScript 7 compiler.
  • Networking and proxies: load balancers, DNS, VPNs, API gateways (Caddy, Traefik).
  • Data pipelines and workers: queues, ETL, stream processing, schedulers.
  • Serverless functions: AWS Lambda, Google Cloud Run, Vercel's Go runtime.
  • WebAssembly and embedded (via TinyGo) for the adventurous.

Go and TypeScript, side by side at major companies

In practice it's rarely Go or TypeScript. The common pattern is a TypeScript front end (React, Next.js) talking to Go services, with a shared contract (OpenAPI or Protocol Buffers) so both sides agree on the data. Some notable examples:

  • Microsoft: TypeScript itself is now built in Go. In March 2025 Anders Hejlsberg announced a native port of the TypeScript compiler written in Go, about 10x faster on full builds (announcement). It shipped as TypeScript 7.0 in July 2026, with 8–12x faster full builds, and you still install it with npm install -D typescript (release). Every TS developer now runs Go code daily.
  • Google: Go was created there and runs in production across Search indexing, Firebase Hosting, the Chrome Optimization Guide backend and SRE tooling (go.dev), alongside TypeScript front ends such as Angular, also Google-made.
  • Uber: by 2022 its Go monorepo held about 50 million lines and roughly 2,100 Go services (Uber Engineering), while its web products use React and its own React component library, Base Web.
  • Cloudflare: uses Go at the heart of services like compression, DNS and SSL (go.dev), while its Workers platform treats TypeScript as a first-class language (docs).
  • PayPal: rewrote its NoSQL system in Go and runs build pipelines and load balancers in Go, while Java and Node.js remain first-class languages there (go.dev).
  • American Express: chose Go for payments and rewards microservices after benchmarking it against C++, Java and Node.js (go.dev).
  • Dropbox: moved performance-critical back ends from Python to Go for concurrency and speed (go.dev).
  • Twitch: uses Go for many of its busiest systems; chat was rewritten from Python to Go in 2013 (Twitch blog).
  • Vercel: the home of Next.js offers a Go runtime for serverless functions and can run a Go server beside a Next.js app in one project (docs).

The tooling story goes both ways. esbuild, the bundler written in Go, made JS builds dramatically faster and powered Vite's dev pre-bundling up to Vite 7; Vite 8 has since moved to Rolldown, written in Rust (Vite 8). Turborepo went the same way, from Go to Rust (Vercel). The lesson for a JS/TS developer: native languages now build the tools you use every day, and Go is the most approachable of them.

To connect the two worlds, tools like Connect generate Go handlers for net/http and TypeScript clients that feel like fetch from one Protocol Buffers schema: type safety across the network. And in the 2025 Go Developer Survey, Go developers named Python, Rust and TypeScript as the other languages they most enjoy (go.dev).

Your roadmap from zero to hero

  1. Week 1: the language. Work through A Tour of Go and re-run every example above with your own changes.
  2. Week 2: idioms. Read Effective Go and browse Go by Example. Practise errors, interfaces and table-driven tests.
  3. Week 3: a real service. Build a JSON API with net/http, a database (database/sql + pgx), configuration and graceful shutdown with context.
  4. Week 4: concurrency in anger. Worker pools, context cancellation, errgroup, rate limiting, and go test -race on all of it.
  5. Then connect it to your stack: put a Next.js or React front end on your Go API and share the contract with OpenAPI or Connect. Profile with pprof, ship a 10 MB container, and enjoy the quiet pager.

Go rewards writing the obvious thing. Coming from TypeScript you'll miss a few type tricks for a while, and then notice how rarely you needed them.

JavaScript နဲ့ TypeScript နဲ့ စဉ်းစားတတ်နေပြီဆိုရင် သင်ထင်ထားတာထက် Go ကို ပိုသိနေပြီးသားပါ။ Function, closure, JSON, HTTP, async အလုပ်တွေ — idea တွေက အတူတူပါပဲ။ ပြောင်းသွားတာက ပုံသဏ္ဌာန် ပါ: Go က compile လုပ်ရတဲ့ language ဖြစ်တယ်၊ သီးခြား type layer မလိုဘဲ statically typed ဖြစ်တယ်၊ အချင်းချင်း တင်းတင်းကျပ်ကျပ် ချိတ်ဆက်နေတဲ့ feature အနည်းငယ်လေးပေါ်မှာ တည်ဆောက်ထားတာပါ။

ဒီ guide က သုညကနေစပြီး တကယ့် Go code ကို ဖတ်နိုင်၊ service သေးသေးလေးတစ်ခု ရေးနိုင်တဲ့အထိ ခေါ်သွားပါမယ်။ Section တိုင်းမှာ Go idea တစ်ခုကို သင်သိပြီးသား JS/TS idea နဲ့ ဘေးချင်းယှဉ်ပြထားပြီး example တိုင်းရဲ့ အောက်မှာ line တစ်ကြောင်းချင်း ရှင်းလင်းချက် တွေ ပါပါတယ်။

JS/TS developer အတွက် ဘာလို့ Go လဲ

Go ကို Google မှာ 2007 ခုနှစ်က Robert Griesemer, Rob Pike နဲ့ Ken Thompson တို့ design လုပ်ခဲ့ပြီး 2009 မှာ open source အဖြစ် ထုတ်ခဲ့ပါတယ်။ ရည်ရွယ်ချက်က လက်တွေ့ကျပါတယ်: build မြန်ဖို့၊ team ကြီးတွေ ဖတ်ရလွယ်တဲ့ ရိုးရှင်းတဲ့ code ဖြစ်ဖို့နဲ့ network server တွေအတွက် concurrency ကို language ထဲမှာကတည်းက ပထမတန်းစား support ပေးဖို့ပါ။

စဉ်းစားပုံ ပြောင်းရမယ့်အချက်တွေကို ဘေးချင်းယှဉ်ကြည့်ရအောင်:

  • Runtime → binary. Node က သင့် JS ကို V8 ပေါ်မှာ JIT နဲ့ run ပါတယ်။ Go ကတော့ ကြိုတင် compile လုပ်ပြီး native binary တစ်ခုတည်း ထွက်လာပါတယ်: server ပေါ်မှာ node_modules မလို၊ install လုပ်ရမယ့် runtime လည်း မလိုပါဘူး။
  • Type တွေက language ကိုယ်တိုင်ပါ။ TS type တွေက build time မှာ ဖျက်ပစ်ခံရပါတယ်၊ Go type တွေကတော့ run time မှာလည်း ရှိနေပြီး memory layout ကို ဆုံးဖြတ်ပါတယ်။ မတော်တဆ any နဲ့ လွတ်ထွက်သွားတာ မရှိသလို type gymnastics လည်း မလိုပါဘူး။
  • Error တွေက value တွေပါ။ throw မရှိပါဘူး; function တွေက error တစ်ခု return ပြန်ပြီး သင်က စစ်ရပါတယ်။
  • Concurrency က built-in ပါ။ async/await နဲ့ event loop တစ်ခုတည်း အစား Go က ပေါ့ပါးတဲ့ goroutine ထောင်ချီကို CPU core အားလုံးပေါ်မှာ run ပြီး channel တွေနဲ့ ချိတ်ဆက်ပေးပါတယ်။
  • လုပ်နည်း တစ်နည်းတည်း။ Loop keyword တစ်ခုတည်း (for)၊ formatter တစ်ခုတည်း (gofmt)၊ standard library ကြီးကြီး။ ရွေးစရာနည်းတော့ အသေးအဖွဲအတွက် ငြင်းခုံရတာ (bikeshedding) လည်း နည်းပါတယ်။

Hello, Go

Run လို့ရတဲ့ Go program တိုင်းက main function ပါတဲ့ main package တစ်ခုပါ။ Run ကြည့်ပြီး နာမည်ကို ပြောင်းကြည့်ပါ။

goပြင်ပြီး Run ကို နှိပ်ကြည့်ပါ

Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်

တစ်ကြောင်းချင်း ရှင်းလင်းချက်

  1. // နဲ့ စတဲ့ line တွေက comment တွေပါ၊ JS နဲ့ အတူတူပါပဲ။
  2. File တိုင်းက package တစ်ခုထဲမှာ ပါပါတယ်: package က Go ရဲ့ code unit ဖြစ်ပြီး ES module folder တစ်ခုလိုပါပဲ။ main က အထူးပါ: library အစား executable အဖြစ် build ထွက်ပါတယ်။
  3. Import တွေက package တွေကို path နဲ့ ခေါ်ပါတယ်။ fmt (format လုပ်ခြင်းနဲ့ print ထုတ်ခြင်း) နဲ့ strings က standard library ကလာတာပါ: npm install မလိုပါဘူး။ မသုံးတဲ့ import က compile error ဖြစ်တဲ့အတွက် import တွေ ဘယ်တော့မှ ပုပ်မနေပါဘူး။
  4. func က function တစ်ခုကို declare လုပ်ပါတယ်။ Package main ထဲက main() က entry point ဖြစ်ပြီး အဲဒါ return ပြန်တာနဲ့ program ပြီးသွားပါတယ်။ JS module မှာလို import လုပ်တာနဲ့ run သွားမယ့် top-level code မရှိပါဘူး။
  5. := က variable အသစ်တစ်ခု declare လုပ်ပြီး value ကနေ type (string) ကို infer လုပ်ပါတယ်: let name = "Gopher" လို့ စဉ်းစားကြည့်ပါ။ Function ထဲမှာပဲ သုံးလို့ရပါတယ်။
  6. Sprintf က %s (string)၊ %d (integer)၊ %q (quoted) လို verb တွေနဲ့ string တစ်ခုအဖြစ် format လုပ်ပေးပါတယ်။ JS template literal ရဲ့ အခန်းကဏ္ဍကို ယူထားတာပါ။
  7. Println က argument တွေကို space ခြားပြီး newline နဲ့ print ထုတ်ပါတယ်: console.log ပါပဲ။ Export လုပ်ထားတဲ့ name တွေက စာလုံးကြီး နဲ့ စပါတယ်; အဲဒီ စာလုံးကြီးက Go ရဲ့ export keyword ပါပဲ။
  8. Package function တွေကို package.Name ပုံစံနဲ့ ခေါ်ရပါတယ်၊ namespace import လိုပါပဲ။
  9. String ရဲ့ len က character မဟုတ်ဘဲ byte တွေကို ရေတွက်ပါတယ်။ Printf က newline မထည့်ပေးတဲ့အတွက် format ကို \n နဲ့ အဆုံးသတ်ထားပါတယ်။

Variable, constant နဲ့ zero value

TypeScript မှာ let, const နဲ့ မသတ်မှတ်ရသေးတာမှန်သမျှအတွက် undefined ရှိပါတယ်။ Go မှာ undefined မရှိပါဘူး: variable တိုင်းက သူ့ type ရဲ့ zero value နဲ့ စတဲ့အတွက် ဖတ်လို့ရတဲ့ valid value တစ်ခု အမြဲရှိနေပါတယ်။

goပြင်ပြီး Run ကို နှိပ်ကြည့်ပါ

Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်

တစ်ကြောင်းချင်း ရှင်းလင်းချက်

  1. const က compile time မှာ သတ်မှတ်ပြီးသားဖြစ်ပြီး number, string, boolean ဒါမှမဟုတ် rune တွေကိုပဲ သိမ်းလို့ရပါတယ်၊ object သိမ်းလို့ရတဲ့ JS const နဲ့ မတူပါဘူး။
  2. var name Type က value မပေးဘဲ declare လုပ်တာပါ။ Type annotation က name ရဲ့ နောက်မှာ လာပါတယ်၊ TS (let count: number) လိုပါပဲ၊ colon မပါတာပဲ ကွာပါတယ်။
  3. Zero value တွေက: string အတွက် ""၊ boolean အတွက် false၊ slice, map, pointer နဲ့ interface တွေအတွက် nil။ nil slice က empty slice လိုပဲ အလုပ်လုပ်ပါတယ်: range လုပ်လို့ရပြီး append လည်း လုပ်လို့ရပါတယ်။
  4. 0 true false 0 ကို print ထုတ်ပါတယ်။ ဘာမှ crash မဖြစ်သလို ဘာမှလည်း undefined မဖြစ်ပါဘူး။
  5. Short declaration တွေက int, float64 နဲ့ string ကို infer လုပ်ပါတယ်။ Type မသတ်မှတ်ထားတဲ့ number တွေက သူတို့ပုံစံကိုကြည့်ပြီး int ဒါမှမဟုတ် float64 ဖြစ်သွားပါတယ်။
  6. %T က value ရဲ့ type ကို print ထုတ်ပေးပါတယ်: လေ့လာနေတုန်း အသုံးဝင်ပါတယ်။
  7. Statement တစ်ခုထဲမှာ variable အများကြီး; = (:= မဟုတ်) က ရှိပြီးသား variable တွေကို assign လုပ်ပါတယ်။ Swap မှာ ညာဘက်ကို အရင် evaluate လုပ်ပါတယ်၊ [a, b] = [b, a] လိုပါပဲ။
  8. Go မှာ variable တစ်ခုရဲ့ type က သူ့သက်တမ်းတစ်လျှောက် မပြောင်းပါဘူး။ views က အမြဲတမ်း int ပါ; union type မရှိသလို string assign လုပ်ရင် compile error ဖြစ်ပါတယ်။
ts
let count: number;        // undefined until assignedconst tags: string[] = []; // the binding is const, the array isn'tlet views = 42;            // inferred: numberviews = "many";            // error in TS, but legal at run time in JS

တစ်ကြောင်းချင်း ရှင်းလင်းချက်

  1. TS မှာ ဒီနေရာမှာ count ကို ဖတ်ရင် error ဖြစ်ပြီး JS မှာတော့ undefined ပါ။ Go မှာဆိုရင် 0 ပဲ ဖြစ်မှာပါ။
  2. TS က ဒါကို compile time မှာ တားပေမယ့် သူ emit လုပ်တဲ့ JS ကတော့ run သွားမှာပါ။ Go မှာ အောက်ခံ layer မရှိပါဘူး: compile လုပ်ပြီးသား program က source code အတိုင်း type အပြည့်အစုံ ရှိနေပါတယ်။

Number, string နဲ့ rune

JavaScript မှာ number တစ်မျိုးတည်း (64-bit float) ရှိပါတယ်။ Go ကတော့ သင့်ကို ရွေးခိုင်းပါတယ်: int, int64, uint8, float64 စသဖြင့်။ String တွေက ပြောင်းလဲလို့မရတဲ့ byte တွေဖြစ်ပြီး အများအားဖြင့် UTF-8 ပါ၊ မြန်မာစာ ဒါမှမဟုတ် emoji ကို ကိုင်တွယ်ရတဲ့အချိန်ကျရင် ဒါက ချက်ချင်း အရေးကြီးလာပါတယ်။

goပြင်ပြီး Run ကို နှိပ်ကြည့်ပါ

Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်

တစ်ကြောင်းချင်း ရှင်းလင်းချက်

  1. int က ခေတ်ပေါ်စက်တွေမှာ 64-bit integer ဖြစ်ပြီး float64 က JS ရဲ့ number ပါ၊ uint8 (alias byte) က 0–255 ကို သိမ်းပါတယ်။ ဒီမှာ type ကို တိုက်ရိုက်ရေးထားတာပါ: := သုံးရင်လည်း int နဲ့ float64 ကို infer လုပ်မှာပါပဲ။
  2. Go က number တွေကို အသံတိတ် convert မလုပ်ပါဘူး။ likes * price က compile မဖြစ်ပါဘူး; float64(likes) ဆိုပြီး conversion ကို ကိုယ်တိုင်ရေးရပါတယ်။ တစ်ရက်လောက် စိတ်ညစ်ရပေမယ့် bug အမျိုးအစား အများကြီးကနေ ကာကွယ်ပေးပါတယ်။
  3. Go မှာ ++ က expression မဟုတ်ဘဲ statement ပါ: x = y++ လို့ ရေးလို့မရပါဘူး။ Fixed-size integer တွေက wrap ဖြစ်ပါတယ်: uint8 မှာ 255 + 1 က 0 ပါ။
  4. Go source file က UTF-8 ဖြစ်တဲ့အတွက် ဒီမြန်မာစကားလုံးက ပြဿနာမရှိပါဘူး။ len(word) က byte (21) ကို ရေတွက်ပါတယ်၊ မြန်မာ code point တစ်ခုစီက UTF-8 မှာ 3 byte ယူလို့ပါ။
  5. rune က Unicode code point တစ်ခု (int32 တစ်ခု) ပါ။ []rune ကို convert လုပ်ပြီး length ယူရင် code point တွေကို ရေတွက်ပါတယ်: 7။ JS ရဲ့ .length က UTF-16 unit တွေကို ရေတွက်တဲ့အတွက် တတိယ အဖြေတစ်ခု ထပ်ထွက်လာပါတယ်။
  6. String ကို range လုပ်ရင် UTF-8 ကို decode လုပ်ပေးပါတယ်: i က byte offset၊ r က rune ပါ။ %c က rune ကို character အဖြစ် print ထုတ်ပါတယ်။ ဒါက index ပါတဲ့ for...of ပါပဲ။
  7. Array ရဲ့ length က သူ့ type ရဲ့ အစိတ်အပိုင်းပါ: [7]string နဲ့ [6]string က type မတူပါဘူး။ နေ့စဉ် Go code မှာ array ကို ရှားရှားပါးပါးပဲ သုံးပါတယ်; အဲဒီအစား slice (အောက်မှာ) ကို သုံးမှာပါ။

Function: multiple return, closure နဲ့ defer

Function တွေက ရင်းနှီးပြီးသားလို ထင်ရပေမယ့် feature သုံးခုက နေ့စဉ် code ကို ပြောင်းလဲစေပါတယ်: value အများကြီး return ပြန်နိုင်တာ၊ defer နဲ့ parameter တိုင်းရဲ့ နောက်မှာ type လာတာပါ။

goပြင်ပြီး Run ကို နှိပ်ကြည့်ပါ

Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်

တစ်ကြောင်းချင်း ရှင်းလင်းချက်

  1. Parameter တွေက name type ပုံစံပါ။ Result list (first, last string) က string နှစ်ခုဖြစ်တဲ့ named result နှစ်ခု ကို declare လုပ်ပါတယ်။ TS မှာ object ဒါမှမဟုတ် tuple return ပြန်တဲ့နေရာမှာ Go က value အများကြီး return ပြန်ပါတယ်။
  2. Fields က ဆက်တိုက်ဖြစ်နေတဲ့ whitespace ဘယ်လောက်ပဲရှိရှိ ခွဲပေးပါတယ်: full.trim().split(/\s+/) ပါပဲ။
  3. if မှာ ကွင်းစကွင်းပိတ် မလိုပေမယ့် brace တွေကတော့ မဖြစ်မနေ လိုပါတယ်။ Early return က idiomatic style ပါ: happy path ကို indent မဖြစ်အောင် ထားပါ။
  4. Value နှစ်ခုလုံးကို return ပြန်တာပါ။ Go မှာ negative indexing (parts.at(-1)) မရှိတဲ့အတွက် နောက်ဆုံး element က len(parts)-1 ပါ။
  5. Variadic parameter တစ်ခုပါ: function ထဲမှာ nums က []int ဖြစ်ပါတယ်၊ ...nums: number[] လိုပါပဲ။
  6. range က index နဲ့ value ကို ပေးပါတယ်; _ က index ကို လွှင့်ပစ်တဲ့ blank identifier ပါ။ Go က မသုံးတဲ့ variable တွေကို လက်မခံတဲ့အတွက် _ က "ဒါ မလိုဘူး" လို့ ပြောတဲ့ နည်းပါ။
  7. Function တွေက first-class value တွေပါ။ Return type func() int က TS type () => number ပါပဲ။
  8. Closure တစ်ခုပါ: anonymous function က n ကို capture လုပ်ပြီး call တစ်ခုနဲ့တစ်ခုကြား ဆက်ရှင်နေအောင် ထိန်းထားပါတယ်၊ JS မှာလိုပါပဲ။
  9. defer က ဝန်းရံထားတဲ့ function return ပြန်တဲ့အချိန်မှာ call တစ်ခုကို run ဖို့ စီစဉ်ထားပါတယ်၊ panic ဖြစ်ရင်တောင် run ပါတယ်။ ဒါက Go ရဲ့ finally ပါ၊ အများအားဖြင့် တစ်ခုခုကို ဖွင့်ပြီးတာနဲ့ ချက်ချင်း ရေးလေ့ရှိပါတယ်: f, _ := os.Open(...)၊ ပြီးရင် defer f.Close()။
  10. Result နှစ်ခုကို လက်ခံတာပါ။ တစ်ခုပဲ လိုချင်ရင်: first, _ := split(...)။
  11. slice... က slice တစ်ခုကို variadic call ထဲ ဖြန့်ထည့်ပေးပါတယ်: JS မှာဆို sum(...[10, 20]) ပါ။
  12. counter() ကို ခေါ်တိုင်း n အသစ်တစ်ခု ဖန်တီးပါတယ်။ သုံးကြိမ်ခေါ်ပြီးရင် count: 3 ကို print ထုတ်ပြီး အဲဒီနောက်မှသာ defer လုပ်ထားတဲ့ line က run ပါတယ်။

Error တွေက exception မဟုတ်ဘဲ value တွေပါ

ဒါက အကြီးဆုံး အလေ့အထ ပြောင်းလဲမှုပါ။ Go မှာ သာမန် failure တွေအတွက် try/catch မရှိပါဘူး။ Fail ဖြစ်နိုင်တဲ့ function က နောက်ဆုံး result အဖြစ် error တစ်ခု return ပြန်ပြီး ခေါ်တဲ့သူက အဲဒီနေရာမှာတင် ဘာလုပ်မလဲ ဆုံးဖြတ်ပါတယ်။

goပြင်ပြီး Run ကို နှိပ်ကြည့်ပါ

Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်

တစ်ကြောင်းချင်း ရှင်းလင်းချက်

  1. Sentinel error တစ်ခုပါ: ခေါ်တဲ့သူတွေ နှိုင်းယှဉ်စစ်ဆေးလို့ရတဲ့ မျှဝေသုံးတဲ့ value တစ်ခု။ Convention အရ နာမည်ကို Err နဲ့ စပါတယ်။
  2. Data သယ်ဆောင်တဲ့ custom error type တစ်ခုပါ။ Error() string method ရှိတဲ့ ဘယ် type မဆို error ဖြစ်ပါတယ်; extends Error မရှိပါဘူး။
  3. Method တစ်ခုပါ: နာမည်ရှေ့က (e *ValidationError) က receiver ဖြစ်ပြီး Go ရဲ့ this ပါ။ ဒီ method ရှိတာကြောင့် type က built-in error interface ကို satisfy ဖြစ်ပါတယ်။
  4. "Fail ဖြစ်နိုင်တယ်" ဆိုတာအတွက် Go signature: value နဲ့ error တစ်ခု။ TS မှာဆို Promise<string> လို့ရေးပြီး ခေါ်တဲ့သူတွေ catch ကို မမေ့ဖို့ မျှော်လင့်ရုံပါပဲ။
  5. Atoi (ASCII to integer) က (int, error) ကို return ပြန်ပါတယ်။ if err != nil { return ... } ပုံစံကို Go မှာ နေရာတိုင်း တွေ့ရပါတယ်: ရှင်းရှင်းလင်းလင်း ရှိတယ်၊ grep လုပ်လို့ရတယ်၊ stack unwind ထဲမှာ ဘာမှ ဝှက်မထားပါဘူး။
  6. %w က error ကို wrap လုပ်ပါတယ်: message မှာ context (post 7: not found) ထပ်ပါလာပြီး မူရင်း error က စစ်ဆေးလို့ရအောင် အထဲမှာ ဆက်ရှိနေပါတယ်။ new Error(msg, { cause }) လိုပါပဲ။
  7. အောင်မြင်ရင် error အဖြစ် nil ကို return ပြန်ပါတယ်။ nil က error လို interface တွေရဲ့ zero value ပါ။
  8. Error က ဒီ type ဖြစ်နေခဲ့ရင် errors.As က ဖြည့်ပေးဖို့ variable တစ်ခုပါ။
  9. Value မပါတဲ့ switch က case တစ်ခုစီကို boolean အဖြစ် စစ်ပါတယ်: သပ်ရပ်တဲ့ if / else if chain တစ်ခုပါပဲ။ Case တွေက fall through မဖြစ်တဲ့အတွက် break မလိုပါဘူး။
  10. errors.Is က wrap ထားတဲ့ layer တိုင်းကို ဖောက်ကြည့်ပြီး အဲဒီ value အတိအကျကို ရှာတဲ့အတွက် wrap ထားတဲ့ post 7: not found က match ဖြစ်နေဆဲပါ။
  11. errors.As က အဲဒီ type ရဲ့ ပထမဆုံး error ကို ရှာပြီး သိမ်းပေးပါတယ်၊ if (err instanceof ValidationError) နဲ့ cast တစ်ခု ပေါင်းထားသလိုပါပဲ။
ts
try {  const title = await findPost(id);  console.log("found:", title);} catch (err) {  if (err instanceof NotFoundError) console.log("404");  else throw err;}

တစ်ကြောင်းချင်း ရှင်းလင်းချက်

  1. TS version ပါ: failure က တစ်ခုခုက catch မလုပ်မချင်း stack အပေါ်ကို မမြင်ရဘဲ တက်သွားပါတယ်။ findPost(id): Promise<string> signature က fail ဖြစ်နိုင်တယ်ဆိုတာ မပြောပြပါဘူး။
  2. TS catch ထဲမှာ err က unknown ဖြစ်တဲ့အတွက် instanceof နဲ့ narrow လုပ်ရပါတယ်။ Go ရဲ့ errors.As က အဲဒီအလုပ်ကိုပဲ လုပ်ပါတယ်။

Class အစား struct နဲ့ method

Go မှာ class မရှိသလို inheritance လည်း မရှိပါဘူး။ Data ကို struct နဲ့ သတ်မှတ်၊ behaviour ကို method တွေနဲ့ တွဲထည့်ပြီး struct တစ်ခုကို နောက်တစ်ခုထဲ embed လုပ်ပြီး code ကို ပြန်သုံးပါတယ်။ extends ကို လွမ်းမနေမိဘူးဆိုတာ သိလာပါလိမ့်မယ်။

goပြင်ပြီး Run ကို နှိပ်ကြည့်ပါ

Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်

တစ်ကြောင်းချင်း ရှင်းလင်းချက်

  1. type Name struct { ... } က data အတွက် TS interface လိုပါပဲ၊ ဒါပေမယ့် fixed memory layout ရှိတဲ့ တကယ့် type တစ်ခုပါ။ Name က စာလုံးကြီးနဲ့ စတဲ့အတွက် export ဖြစ်ပါတယ်။
  2. Value receiver ပါတဲ့ method တစ်ခုပါ: a က ခေါ်လိုက်တဲ့ Author ရဲ့ copy တစ်ခုပါ။ Method တွေက struct body အပြင်ဘက်မှာ ရှိပြီး package တစ်ခုတည်းထဲမှာ ဘယ်နေရာမဆို ရေးလို့ရပါတယ်။
  3. Field name မပါတဲ့ Author က embed လုပ်ထားတာပါ: Post က Author ရဲ့ field တွေနဲ့ method တွေကို ရရှိတဲ့အတွက် post.Name နဲ့ post.Byline() အလုပ်လုပ်ပါတယ်။ ဒါက inheritance ရဲ့ အဆင်ပြေမှုပါတဲ့ composition ပါ။
  4. Pointer receiver (*Post): method က copy ကို မဟုတ်ဘဲ မူရင်းကို ပြောင်းပါတယ်။ လက်တွေ့စည်းမျဉ်း: method က mutate လုပ်ရင် ဒါမှမဟုတ် struct က ကြီးရင် pointer receiver ကို သုံးပါ။
  5. Go မှာ constructor မရှိပါဘူး; NewX လို့ နာမည်ပေးထားတဲ့ function က convention ပါ။ title, author string က parameter နှစ်ခုကြား type တစ်ခုကို မျှသုံးထားတာပါ။
  6. Composite literal က named field တွေနဲ့ struct ကို တည်ဆောက်ပါတယ် (မပါတဲ့ field တွေက zero value ရပါတယ်)။ & က သူ့ address ကို ပြန်ပေးတဲ့အတွက် ခေါ်တဲ့သူတွေ Post တစ်ခုတည်းကို မျှသုံးကြပါတယ်။
  7. Address ယူလို့ရတဲ့ value ပေါ်မှာ pointer method ကို ခေါ်တဲ့အခါ Go က address ကို အလိုအလျောက် ယူပေးတဲ့အတွက် call က JS လိုပဲ ဖတ်ရပါတယ်။
  8. Byline က embed လုပ်ထားတဲ့ Author ကနေ promote ဖြစ်လာတာပါ။
  9. %+v က struct ကို field name တွေနဲ့တကွ print ထုတ်ပါတယ်: debug လုပ်ဖို့ အရမ်းကောင်းပါတယ်၊ console.dir လိုပါပဲ။ *post က pointer ညွှန်ပြနေတဲ့ value ကို ဖတ်ပါတယ်။
ts
class Post extends Author {  likes = 0;  constructor(public title: string, name: string) { super(name); }  like() { this.likes++; }}

တစ်ကြောင်းချင်း ရှင်းလင်းချက်

  1. Class version ပါ။ extends က is-a relationship ကို ဖန်တီးပါတယ်; Go ရဲ့ embedding ကတော့ field နဲ့ method တွေကို promote လုပ်ပေးဆဲဖြစ်တဲ့ has-a relationship ပါ။

Interface: ကြေညာစရာမလိုဘဲ satisfy ဖြစ်တယ်

TS developer တွေအတွက် သတင်းကောင်းပါ: Go interface တွေဟာ TS type တွေလိုပဲ structural ဖြစ်ပါတယ်။ type တစ်ခုမှာ လိုအပ်တဲ့ method တွေ ရှိနေရုံနဲ့ interface ကို satisfy ဖြစ်ပါပြီ: implements keyword မလိုပါဘူး။ ကွာခြားချက်က Go က အဲဒါကို compile time မှာ စစ်ပေးပြီးတော့ run time မှာလည်း အဲဒီ အချက်အလက်ကို ဆက်သိမ်းထားတာပါ။

goပြင်ပြီး Run ကို နှိပ်ကြည့်ပါ

Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်

တစ်ကြောင်းချင်း ရှင်းလင်းချက်

  1. Interface ဆိုတာ method signature တွေရဲ့ အစုအဝေးပါ။ သေးသေးလေးပဲ ထားပါ: standard library မှာ အသုံးအများဆုံး interface တွေ (io.Reader, error, fmt.Stringer) မှာ method တစ်ခုစီပဲ ရှိပါတယ်။
  2. တစ်ခုနဲ့တစ်ခု မဆက်စပ်တဲ့ struct နှစ်ခု။ type တူတဲ့ field တွေကို တစ်ကြောင်းတည်းမှာ ရေးလို့ရပါတယ်: W, H float64။
  3. type တစ်ခုစီမှာ Area() float64 method ရှိလို့ နှစ်ခုလုံး Shape ကို satisfy ဖြစ်သွားပါပြီ၊ နာမည်တောင် တစ်ခါမှ မဖော်ပြဘဲနဲ့ပါ။ သင့် package ထက် နောက်မှ ရေးထားတဲ့ package တွေက interface တွေကိုတောင် satisfy ဖြစ်အောင် လုပ်နိုင်ပါတယ်။
  4. String() ထည့်လိုက်တော့ Circle က fmt.Stringer ဖြစ်သွားလို့ fmt.Println က ဒီပုံစံအတိုင်း print ထုတ်ပါတယ်၊ toString() ကို override လုပ်သလိုပါပဲ။
  5. Interface တွေကို လက်ခံပြီး concrete type တွေကို return ပြန်ပါ: Go ရဲ့ classic guideline တစ်ခုပါ။ total က အခုရှိတဲ့ shape ဖြစ်ဖြစ်၊ နောင်လာမယ့် shape ဖြစ်ဖြစ် ဘယ် shape နဲ့မဆို အလုပ်လုပ်ပါတယ်။
  6. Interface type ရဲ့ slice တစ်ခုထဲမှာ concrete type အမျိုးမျိုးကို ထည့်ထားနိုင်ပါတယ်။ Rect{3, 4} က field တွေကို အစဉ်လိုက် ဖြည့်ပါတယ် (struct သေးသေးလေးတွေအတွက် ရပါတယ်၊ field နာမည်တွေနဲ့ ရေးရင် ပိုရှင်းပါတယ်)။
  7. Type switch က interface ထဲမှာ ဘယ် concrete type ရှိနေလဲလို့ မေးပါတယ်။ case တစ်ခုစီထဲမှာ v က အဲဒီ type ဖြစ်နေပါတယ်: TS ပုံစံ narrowing ကို run time မှာ လုပ်တာပါ။
  8. Println က String() method ကို သတိထားမိလို့ Circle(r=1) လို့ print ထုတ်ပါတယ်။

Slice နဲ့ map: Go ရဲ့ array နဲ့ object တွေ

JS မှာ array သုံးတဲ့ နေရာတိုင်းမှာ Go မှာတော့ slice ကို သုံးရမှာဖြစ်ပြီး map ကတော့ Go ရဲ့ Map/Record ပါ။ Slice တွေမှာ အရေးကြီးတဲ့ အသေးစိတ်တစ်ခု ကွယ်နေပါတယ်: slice အများအပြားက underlying array တစ်ခုတည်းကို မျှသုံးနေနိုင်ပါတယ်။

goပြင်ပြီး Run ကို နှိပ်ကြည့်ပါ

Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်

တစ်ကြောင်းချင်း ရှင်းလင်းချက်

  1. slices package (Go 1.21+) က generic helper တွေကို ယူလာပေးပါတယ်: Sort, Contains, Index, Reverse, Max…
  2. []int{...} က slice literal ပါ: bracket ထဲမှာ length မပါပါဘူး။ နောက်ကွယ်မှာတော့ array ကို ညွှန်တဲ့ pointer တစ်ခု၊ length တစ်ခုနဲ့ capacity တစ်ခု ပါပါတယ်။
  3. append က (အသစ်ဖြစ်ကောင်းဖြစ်နိုင်တဲ့) slice ကို return ပြန်ပါတယ်: အမြဲ ပြန် assign လုပ်ပါ။ Capacity ကုန်သွားရင် ပိုကြီးတဲ့ array အသစ်ကို allocate လုပ်ပြီး copy ကူးပါတယ်၊ V8 က သင့် array တွေရဲ့ အတွင်းမှာ လုပ်သလိုပါပဲ။
  4. နေရာမှာတင် (in place) ဂဏန်းတန်ဖိုးအလိုက် sort လုပ်ပါတယ်။ (ဂဏန်းတွေကို string အဖြစ် နှိုင်းယှဉ်တဲ့ JS sort() ရဲ့ အံ့အားသင့်စရာ မရှိပါဘူး။)
  5. [1 2 5 8] 4 6 လို့ print ထုတ်ပါတယ်: element လေးခု၊ နောက်တစ်ကြိမ် reallocate မလုပ်ခင် ခြောက်ခုစာ နေရာရှိပါတယ်။
  6. Classic gotcha။ nums[:2] က array တစ်ခုတည်း ပေါ်ကို ကြည့်တဲ့ view အသစ်ပါ၊ ဒါကြောင့် firstTwo[0] ကို ရေးလိုက်ရင် nums ပါ ပြောင်းသွားပါတယ် ([99 2 5 8])။ JS ရဲ့ slice() က copy ကူးပါတယ်၊ copy လိုရင် slices.Clone ကို သုံးပါ။
  7. Slice တွေမှာ .filter() ဒါမှမဟုတ် .map() method မရှိပါဘူး: append နဲ့ ရိုးရိုး loop ရေးတာက idiom ဖြစ်ပြီး မြင်ရသလောက်ပဲ မြန်ပါတယ်။ (Generics နဲ့ ကိုယ်ပိုင် Map ရေးလို့ရပါတယ်၊ အောက်မှာ ကြည့်ပါ။)
  8. Map literal တစ်ခု: map[KeyType]ValueType။ new Map<string, number>() နဲ့ တူပေမယ့် literal syntax နဲ့ ရေးလို့ရပါတယ်။
  9. မရှိတဲ့ key ကို ဖတ်ရင် zero value (0) ရပါတယ်၊ ဒါကြောင့် key အသစ်ကို increment လုပ်တာ ဒီအတိုင်းပဲ အလုပ်ဖြစ်ပါတယ်: (m.get(k) ?? 0) + 1 မလိုပါဘူး။
  10. Comma-ok ပုံစံက မရှိတာ နဲ့ zero ဖြစ်နေတာ ကို ခွဲခြားပေးပါတယ်။ if init; condition က n နဲ့ ok ကို ဒီ if အတွင်းမှာပဲ scope ဖြစ်အောင် လုပ်ပေးပါတယ်။
  11. delete က built-in function ပါ၊ JS မှာဆိုရင် map.delete(k) ပါ။ Map iteration order ကို တမင် random ဖြစ်အောင် လုပ်ထားလို့ ဘယ်တော့မှ အားမကိုးပါနဲ့။

ကြောက်စရာမလိုတဲ့ pointer တွေ

JavaScript က object တွေကို reference နဲ့ pass လုပ်ပြီး primitive တွေကို value နဲ့ pass လုပ်ပါတယ်၊ ဒီကွာခြားချက်ကို ရှင်းရှင်းလင်းလင်း ရေးပြထားတာ ဘယ်တော့မှ မမြင်ရပါဘူး။ Go ကတော့ ပြတ်ပြတ်သားသား ပြပါတယ်: အရာအားလုံးကို value နဲ့ (copy ကူးပြီး) pass လုပ်ပြီး မျှသုံးချင်ရင် pointer ကို သုံးရပါတယ်။

goပြင်ပြီး Run ကို နှိပ်ကြည့်ပါ

Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်

တစ်ကြောင်းချင်း ရှင်းလင်းချက်

  1. ဒီ function က struct ရဲ့ copy ကို လက်ခံပါတယ်။ c.N ကို ပြောင်းရင် copy ကိုပဲ ပြောင်းတာပါ၊ object argument ကို မျှသုံးတဲ့ JS နဲ့ တကယ်ကွာတဲ့ အချက်ပါ။
  2. *Counter က "Counter ကို ညွှန်တဲ့ pointer" ပါ: function က address ကို ရပြီး ခေါ်တဲ့ဘက်က struct ကို ပြင်ပါတယ်။
  3. 0 ပဲ ရှိနေဆဲပါ: copy ကို တိုးပြီးမှ လွှင့်ပစ်လိုက်တာပါ။
  4. &c က c ရဲ့ address ကို ယူပါတယ်။ အခု 1 လို့ print ထုတ်ပါပြီ။
  5. p က *Counter ပါ။ Go မှာ (C နဲ့မတူဘဲ) pointer arithmetic မရှိလို့ pointer တွေက safe ဖြစ်ပါတယ်: valid value တစ်ခု ဒါမှမဟုတ် nil ကိုပဲ အမြဲ ညွှန်ပါတယ်။
  6. Field access အတွက် Go က အလိုအလျောက် dereference လုပ်ပေးပါတယ်: p.N ဆိုတာ (*p).N ပါ။
  7. *p က pointer နောက်ကွယ်က value တစ်ခုလုံးကို ဖတ်ပါတယ်။ နှစ်ခုလုံး 10 လို့ print ထုတ်ပါတယ်: struct တစ်ခုတည်းမို့ပါ။
  8. Pointer ရဲ့ zero value က nil ပါ။ nil pointer ကနေ field ကို ဖတ်ရင် panic ဖြစ်ပါတယ်: Go ရဲ့ Cannot read properties of null ပါပဲ။

Go နည်း Generics

Go က generics ကို 1.18 (2022) မှာ ထည့်ခဲ့ပါတယ်။ TypeScript ထက် တမင် ပိုရိုးရှင်းအောင် လုပ်ထားပါတယ်: conditional type တွေ၊ mapped type တွေ မရှိဘဲ constraint တွေနဲ့ type parameter တွေပဲ ရှိပါတယ်။ Type တွေကို ဒုတိယ program တစ်ခုလို မဖြစ်သွားစေဘဲ container တွေနဲ့ helper တွေအတွက် လုံလောက်ပါတယ်။

goပြင်ပြီး Run ကို နှိပ်ကြည့်ပါ

Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်

တစ်ကြောင်းချင်း ရှင်းလင်းချက်

  1. Constraint ဆိုတာ type တွေကို စာရင်းပြုထားတဲ့ interface ပါ။ | က TS union လိုပဲ ဖတ်ရပါတယ်၊ ~int ဆိုတာ "int ဒါမှမဟုတ် underlying type က int ဖြစ်တဲ့ ဘယ် type မဆို" လို့ ဆိုလိုပါတယ်။
  2. Type parameter တွေကို square bracket ထဲမှာ ရေးပါတယ်: Sum[T Number] ≈ sum<T extends number>။ ခေါ်တဲ့သူက T ကို ရေးရခဲပါတယ်၊ infer လုပ်ပေးလို့ပါ။
  3. Zero value က ဘယ် type အတွက်မဆို အလုပ်လုပ်လို့ total က int ဖြစ်ဖြစ် float ဖြစ်ဖြစ် 0 ကနေ စပါတယ်။
  4. any (interface{} ရဲ့ alias) က TS ရဲ့ unknown ပါ: constraint လုံးဝ မရှိပါဘူး။ ဒါက typed Array.prototype.map တစ်ခုပါ။
  5. make က length 0 နဲ့ capacity len(in) ရှိတဲ့ slice ကို ဖန်တီးပါတယ်: append လုပ်ရင်း အကြိမ်ကြိမ် allocate လုပ်မယ့်အစား တစ်ကြိမ်တည်း allocate လုပ်ပါတယ်။
  6. Generic type တွေလည်း ရပါတယ်: Stack[T] ≈ class Stack<T>။
  7. T | undefined အစား (value, ok) ကို return ပြန်ပါတယ်: ခေါ်တဲ့သူက empty case ကို မေ့လို့မရပါဘူး။
  8. "Type T ရဲ့ ဘာမှမရှိတာကို return ပြန်ဖို့" ပုံမှန်သုံးတဲ့ နည်းလမ်း: သူ့ရဲ့ zero value ပါ။
  9. float64 ပေါ်မှာ တည်ဆောက်ထားတဲ့ named type တစ်ခုပါ။ float64 နဲ့ မတူတဲ့ type ဖြစ်ပေမယ့် constraint ထဲက ~float64 က လက်ခံပါသေးတယ်။
  10. T ကို argument တွေကနေ infer လုပ်ပါတယ်၊ int ပြီးတော့ Celsius: 6 41.5 လို့ print ထုတ်ပါတယ်။
  11. Generic type တွေကို bracket နဲ့ instantiate လုပ်ပါတယ်။ Zero Stack က ချက်ချင်း သုံးလို့ရပါတယ်: constructor မလိုပါဘူး။

Concurrency: goroutine နဲ့ channel များ

Node က thread တစ်ခုနဲ့ event loop ကို ပေးပါတယ်: await က function ကို ခေတ္တရပ်ပေမယ့် process ကို ဘယ်တော့မှ မရပ်ပါဘူး။ Go ကတော့ goroutine တွေကို ပေးပါတယ်: Go runtime က CPU core အားလုံးပေါ်မှာ schedule လုပ်ပေးတဲ့၊ concurrent run နေတဲ့ function တွေပါ။ Stack kilobyte အနည်းငယ်နဲ့ပဲ စတာမို့ server တစ်ခုက goroutine သိန်းနဲ့ချီ run နိုင်ပါတယ်။ Channel တွေက သူတို့ကြားမှာ value တွေကို safe ဖြစ်ဖြစ် ပို့ပေးပါတယ်။

Memory ကို မျှသုံးခြင်းဖြင့် ဆက်သွယ်ပါနဲ့၊ ဆက်သွယ်ခြင်းဖြင့် memory ကို မျှသုံးပါ။

— Effective Go

ဒါကို run ကြည့်ပြီး output ရောက်လာတာကို စောင့်ကြည့်ပါ: delay တွေက တကယ့် delay တွေပါ။

goပြင်ပြီး Run ကို နှိပ်ကြည့်ပါ

Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်

တစ်ကြောင်းချင်း ရှင်းလင်းချက်

  1. Network call အတု တစ်ခု။ time.Sleep က ဒီ goroutine ကိုပဲ block လုပ်ပါတယ်၊ ကျန်တာတွေက ဆက် run နေပါတယ်။ async colouring မရှိပါဘူး: ဘယ် function ကိုမဆို concurrent run လို့ရပါတယ်။
  2. time.Now() နဲ့ time.Since က ကြာချိန်ကို performance.now() ပုံစံ တိုင်းပါတယ်။
  3. make နဲ့ ဖန်တီးထားတဲ့ string channel တစ်ခုပါ။ ဒီ unbuffered channel ပေါ်မှာ ပို့တာ (ch <- v) နဲ့ လက်ခံတာ (<-ch) က တစ်ခုနဲ့တစ်ခု စောင့်ကြပါတယ်: built-in လက်လွှဲမှုတစ်ခုပါ။
  4. WaitGroup က run နေတဲ့ goroutine တွေကို ရေတွက်ပါတယ်: မစခင် Add၊ ပြီးရင် Done၊ သုည ဖြစ်တဲ့အထိ Wait။
  5. Anonymous struct slice တစ်ခု: object literal array လိုမျိုး job စာရင်းကို inline မြန်မြန်ရေးတာပါ။
  6. go keyword က goroutine တစ်ခုကို စတင်ပါတယ်: ဒီ function က concurrent run ပြီး loop က ချက်ချင်း ဆက်သွားပါတယ်။ async function ကို await မပါဘဲ ခေါ်သလိုပါပဲ။ Go 1.22 ကစပြီး iteration တစ်ခုစီမှာ ကိုယ်ပိုင် job ရှိလို့ closure က safe ဖြစ်ပါတယ်။
  7. ဘာပဲဖြစ်ဖြစ် goroutine က return ပြန်တဲ့အခါ ဒီ job ပြီးပြီလို့ မှတ်ပါ။
  8. Result ကို channel ထဲ ပို့ပါ။ တစ်ယောက်ယောက်က လက်ခံတဲ့အထိ စောင့်ပါတယ်။
  9. Helper goroutine တစ်ခုက သုံးခုလုံးကို စောင့်ပြီးမှ "value တွေ ထပ်မလာတော့ဘူး" လို့ အချက်ပြဖို့ channel ကို close လုပ်ပါတယ်။
  10. Channel ပေါ်မှာ range က close မဖြစ်မချင်း လက်ခံပါတယ်။ Result တွေက ပြီးတဲ့အစဉ်အတိုင်း (posts, likes, users) ရောက်လာပါတယ်၊ settled ဖြစ်ပြီးတဲ့ promise တွေပေါ်မှာ for await လုပ်သလိုပါပဲ။
  11. 600 မဟုတ်ဘဲ 300 ms လောက်ပဲ ကြာပါတယ်: sleep သုံးခုက တစ်ပြိုင်နက် ထပ်နေခဲ့လို့ပါ။ Promise မပါတဲ့ Promise.all ပါပဲ။
  12. နှေးတဲ့ job (800 ms) တစ်ခုကို background မှာ စတင်ပါ။
  13. select က channel operation အများအပြားကို စောင့်ပြီး အရင်အဆင်သင့်ဖြစ်တဲ့ တစ်ခုကို run ပါတယ်: Promise.race ပါ။ time.After က 500 ms ကြာရင် value ပို့ပေးမယ့် channel ကို return ပြန်လို့ ဒါက timeout ပါ။ (တကယ့် code မှာတော့ context.Context က deadline တွေနဲ့ cancellation ကို သယ်ဆောင်ပါတယ်။)
ts
const results = await Promise.all([  fetchJob("users", 300),  fetchJob("posts", 100),  fetchJob("likes", 200),]);const report = await Promise.race([fetchJob("report", 800), timeout(500)]);

တစ်ကြောင်းချင်း ရှင်းလင်းချက်

  1. TS နဲ့ ညီမျှတဲ့ code: Promise.all က အားလုံးကို စောင့်ပေမယ့် အနှေးဆုံးတစ်ခု ပြီးမှ result တွေကို input order အတိုင်း return ပြန်ပါတယ်။ Go ရဲ့ channel version ကတော့ တစ်ခုချင်းစီကို ပြီးတာနဲ့ handle လုပ်ခဲ့ပါတယ်။
  2. Timeout promise နဲ့ Promise.race: time.After နဲ့ select လိုပဲ idea တူတူပါ။

net/http နဲ့ JSON API တစ်ခု

Go ရဲ့ standard library မှာ production-grade HTTP server တစ်ခု ပါဝင်ပါတယ်။ Go 1.22 ကစပြီး သူ့ router က method တွေနဲ့ path parameter တွေကို နားလည်လို့ API သေးသေးလေးတစ်ခုအတွက် framework မလိုပါဘူး၊ Express နဲ့ ညီမျှတာတောင် မလိုပါဘူး။

goပြင်ပြီး Run ကို နှိပ်ကြည့်ပါ

Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်

တစ်ကြောင်းချင်း ရှင်းလင်းချက်

  1. ဒီမှာ အားလုံးက standard library ပါ: JSON၊ HTTP client နဲ့ server၊ test server တစ်ခု။
  2. Struct tag တွေ (back-quote နဲ့ရေးထားတဲ့ json:"id") က encoding/json ကို ဘယ် key သုံးရမလဲ ပြောပါတယ်။ Export လုပ်ထားတဲ့ (စာလုံးကြီးနဲ့စတဲ့) field တွေကိုပဲ encode လုပ်ပါတယ်။
  3. Handler တစ်ခု: Express ရဲ့ (req, res) => {} လိုပါပဲ၊ ဒါပေမဲ့ order က (w, r)၊ writer အရင်လာပါတယ်။
  4. Slice ကို JSON အဖြစ် response ထဲ တိုက်ရိုက် stream လုပ်ပါတယ်: res.json(posts)။
  5. {id} path parameter ကို ဖတ်ပါတယ်: req.params.id။
  6. Status code နဲ့ body ကို call တစ်ခုတည်းနဲ့ ရေးပါတယ်။ http.StatusNotFound က constant 404 ပါ။
  7. Mux (router) တစ်ခု: express()။
  8. Method နဲ့ wildcard ပါတဲ့ pattern တွေ (Go 1.22+): app.get("/api/posts/:id", ...)။
  9. တကယ့် program မှာဆိုရင် ဒီတစ်ကြောင်းက server ကို စတင်ပါတယ်။ Request တစ်ခုစီက core အားလုံးပေါ်မှာ ကိုယ်ပိုင် goroutine နဲ့ အလိုအလျောက် run ပါတယ်။
  10. Playground က public port တွေ ဖွင့်လို့မရလို့ httptest က program ထဲမှာပဲ local address ပေါ်မှာ server ကို စတင်ပါတယ်: test တွေမှာ သုံးမယ့် tool အတူတူပါပဲ။
  11. Built-in HTTP client: promise မပါတဲ့ fetch ပါ။ err check ကို ထပ်တွေ့ရပါတယ်။
  12. Connection ကို free ဖြစ်အောင် response body တွေကို close လုပ်ရပါမယ်၊ ပိုရှည်တဲ့ code မှာဆိုရင် defer res.Body.Close() လုပ်ပါလိမ့်မယ်။
ts
import express from "express";const app = express();app.get("/api/posts", (req, res) => res.json(posts));app.get("/api/posts/:id", (req, res) => {  const post = posts.find((p) => String(p.id) === req.params.id);  post ? res.json(post) : res.status(404).json({ error: "not found" });});app.listen(8080);

တစ်ကြောင်းချင်း ရှင်းလင်းချက်

  1. Express version က dependency လိုပါတယ်၊ Go version က ဘာမှ မလိုပါဘူး။ နှစ်ခုလုံး အရှည် သိပ်မကွာပါဘူး။
  2. Clustering ဒါမှမဟုတ် worker တွေ မထည့်ရင် Node က request အားလုံးကို thread တစ်ခုတည်းပေါ်မှာ serve လုပ်ပါတယ်၊ Go ကတော့ default အနေနဲ့ request တွေကို core အားလုံးပေါ် ဖြန့်ပေးပါတယ်။

Tooling၊ module နဲ့ test များ

go command တစ်ခုက JS toolchain တစ်ခုလုံးကို အစားထိုးပါတယ်။ ရွေးရမယ့် config မရှိသလို Go ကိုယ်တိုင်ကလွဲရင် install လုပ်စရာ ဘာမှ မလိုပါဘူး။

  • go mod init example.com/app → npm init (go.mod ကို ဖန်တီးပေးပါတယ်၊ သင့် package.json ပါ)
  • go get github.com/x/y → npm install (version တွေကို go.mod / go.sum မှာ pin လုပ်ပါတယ်၊ သင့် lockfile ပါ)
  • go run . → ts-node / tsx (compile နဲ့ run ကို တစ်ဆင့်တည်းမှာ)
  • go build → tsc + bundler တစ်ခု (static binary တစ်ခုတည်း ထုတ်ပေးပါတယ်)
  • gofmt / go fmt → Prettier (style တစ်မျိုးတည်း၊ option မရှိ၊ ငြင်းခုံစရာ မရှိ)
  • go vet → တကယ့် bug တွေအတွက် ESLint (မှားနေတဲ့ Printf verb တွေ၊ ဘယ်တော့မှ မရောက်နိုင်တဲ့ code…)
  • go test ./... → Jest / Vitest (built in ဖြစ်ပြီး benchmark နဲ့ fuzzing ပါ ပါဝင်ပါတယ်)
  • GOOS=linux GOARCH=arm64 go build → command တစ်ခုတည်းနဲ့ တခြား OS/CPU အတွက် cross-compile လုပ်ပါ
bash
go mod init example.com/journalgo get github.com/go-chi/chi/v5go run .go test ./... -race -coverGOOS=linux GOARCH=amd64 go build -o journal-api

တစ်ကြောင်းချင်း ရှင်းလင်းချက်

  1. Import path (များသောအားဖြင့် repo ရှိတဲ့နေရာ) နဲ့ နာမည်ပေးထားတဲ့ module တစ်ခုကို စတင်ပါတယ်။
  2. လက်ရှိ folder ထဲက package ကို build လုပ်ပြီး run ပါတယ်။
  3. Package တိုင်းထဲက test တိုင်းကို race detector နဲ့ coverage ပါ run ပါတယ်။
  4. Mac ကနေ Linux binary ကို build လုပ်ပါတယ်: file တစ်ခုတည်းကို server ပေါ် ဒါမှမဟုတ် Docker image သေးသေးလေးထဲ copy ကူးလိုက်ရင် run ပါပြီ။

Test တွေက _test.go file တွေထဲက ရိုးရိုး function တွေပါ။ Playground က test တွေပါပြီး main မပါတဲ့ program ကို go test အဖြစ် run ပေးလို့ ဒါကိုလည်း စမ်းကြည့်လို့ရပါတယ်: Slugify ကို တမင်မှားအောင် ပြင်ပြီး fail ဖြစ်တာကို ကြည့်ပါ။

goပြင်ပြီး Run ကို နှိပ်ကြည့်ပါ

Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်

တစ်ကြောင်းချင်း ရှင်းလင်းချက်

  1. Standard testing package: install လုပ်ရမယ့် test runner မလိုပါဘူး။ (တကယ့် project မှာဆိုရင် ဒါက slug_test.go ထဲက package journal ဖြစ်ပါလိမ့်မယ်။)
  2. Test လုပ်မယ့် code: lowercase ပြောင်း၊ whitespace နဲ့ ခွဲ၊ dash တွေနဲ့ ပြန်ဆက်။
  3. func TestXxx(t *testing.T) ပုံစံ ဘာမဆို test တစ်ခုပါ။ t က failure တွေကို report လုပ်ပါတယ်: describe/it/expect DSL မရှိပါဘူး။
  4. Table-driven test များ: input တွေနဲ့ မျှော်လင့်ထားတဲ့ output တွေရဲ့ slice တစ်ခု၊ Go မှာ အသုံးအများဆုံး testing pattern ပါ။
  5. ရိုးရိုး != နဲ့ နှိုင်းယှဉ်ပါ။ Go က assertion library တွေကို အားမပေးလို့ failure message ကို ကိုယ်တိုင် ရေးရပါတယ်။
  6. Errorf က test ကို failed လို့ မှတ်ပြီး ဆက်သွားလို့ fail ဖြစ်တဲ့ case အားလုံးကို တစ်ပြိုင်နက် မြင်ရပါတယ်။

Go ကို ဘယ်လိုတည်ဆောက်ထားလဲ: language architecture

go build နောက်ကွယ်မှာ ဘာတွေဖြစ်နေလဲ သိထားရင် Go ရဲ့ trade-off အများစုကို နားလည်သွားပါလိမ့်မယ်။ သင့် TypeScript ဖြတ်သွားတဲ့ လမ်းကြောင်းနဲ့ ယှဉ်ကြည့်ပါ: tsc က type တွေကို ဖျက်ပြီး JS ထုတ်ပေးတယ်၊ ပြီးတော့ V8 က အဲဒီ JS ကို run နေရင်းနဲ့ parse လုပ်၊ interpret လုပ်ပြီး JIT-compile လုပ်ပါတယ်။

1. Compiler: source code ကနေ native binary တစ်ခုတည်းဆီ

  1. Package တိုင်းကို parse လုပ်ပြီး type-check လုပ်ပါတယ်။ Go ရဲ့ grammar ကို မြန်မြန် parse လုပ်နိုင်အောင် ဒီဇိုင်းထုတ်ထားပြီး မသုံးတဲ့ import ဒါမှမဟုတ် variable ဆိုတာ error ဖြစ်တာကြောင့် build တွေ ပေါ့ပါးနေပါတယ်။
  2. SSA (static single assignment) အဖြစ် ပြောင်းပြီး optimise လုပ်ပါတယ်: inlining၊ escape analysis၊ dead-code ဖယ်ရှားခြင်း၊ bounds-check ဖယ်ရှားခြင်း။
  3. Target OS နဲ့ CPU (GOOS/GOARCH) အတွက် machine code ထုတ်ပေးပြီး၊ Go runtime အပါအဝင် အားလုံးကို statically linked executable တစ်ခုတည်းအဖြစ် link လုပ်ပါတယ်။
  4. Package တွေကို သီးခြားစီ compile လုပ်ပြီး cache လုပ်ထားတာကြောင့် codebase ကြီးတွေမှာတောင် rebuild က စက္ကန့်ပိုင်းပဲ ကြာပါတယ်: Go ဟာ Google မှာ နှေးကွေးတဲ့ C++ build တွေကို ဖြေရှင်းဖို့ မွေးဖွားလာခဲ့တာပါ။

2. Runtime: သေးငယ်ပေမယ့် အမြဲရှိနေတယ်

VM မရှိပါဘူး၊ ဒါပေမဲ့ Go binary တိုင်းမှာ scheduler၊ garbage collector၊ goroutine stack တွေ၊ map တွေ၊ channel တွေနဲ့ reflection ကို ပေးတဲ့ runtime (MB အနည်းငယ်) ပါလာပါတယ်။ Node က V8 နဲ့ libuv ဆီကနေ ရတာတွေရဲ့ သေးငယ်တဲ့ အစိတ်အပိုင်းလေးကို binary ထဲ compile လုပ်ထည့်ထားတယ်လို့ မြင်ကြည့်ပါ။

3. Scheduler: G၊ M နဲ့ P

  • G: goroutine တစ်ခု: သူ့ရဲ့ stack၊ instruction pointer နဲ့ state။
  • M: machine thread တစ်ခု: တကယ့် OS thread။
  • P: processor တစ်ခု: run လို့ရတဲ့ G တွေရဲ့ queue ကို ကိုင်ထားတဲ့ scheduling slot။ GOMAXPROCS အရေအတွက်ရှိပြီး default အားဖြင့် CPU core တစ်ခုကို တစ်ခုပါ။
  • M တစ်ခုက G တွေကို run ဖို့ P တစ်ခု လိုပါတယ်။ Goroutine တစ်ခု I/O ပေါ်မှာ block ဖြစ်နေရင် runtime က အဲဒါကို ခဏနားထားပြီး တခြားတစ်ခုကို run ပါတယ်; network I/O က နောက်ကွယ်မှာ OS ရဲ့ epoll/kqueue ကို သုံးပါတယ် (libuv ရဲ့ event loop နဲ့ အိုင်ဒီယာတူပါတယ်)၊ ဒါပေမဲ့ သင်ကတော့ ပုံမှန် blocking code ကိုပဲ ရေးရပါတယ်။ အလုပ်မရှိတဲ့ P တွေက အလုပ်များနေတဲ့ P တွေဆီက အလုပ်ကို ခိုးယူ (steal work) ပြီး ကြာကြာ run နေတဲ့ goroutine တွေကို preempt လုပ်တာကြောင့် ဘယ်တစ်ခုမှ core တစ်ခုကို လက်ဝါးကြီးအုပ် မထားနိုင်ပါဘူး။

4. Memory နဲ့ garbage collection

  • Escape analysis က value တစ်ခုကို goroutine ရဲ့ stack ပေါ်မှာ (ရှင်းလင်းရတာ အခမဲ့နီးပါး) ထားလို့ရမလား၊ heap ပေါ်ကို ရောက်ရမလားဆိုတာကို compile time မှာ ဆုံးဖြတ်ပါတယ်။ &Post{} ကို return ပြန်ရင် escape ဖြစ်သွားပါတယ်; function တစ်ခုအတွင်းမှာပဲ သုံးတဲ့ local struct ကတော့ escape မဖြစ်ပါဘူး။
  • Goroutine stack တွေက သေးသေးလေး (KB အနည်းငယ်) နဲ့ စပြီး လိုအပ်သလို ကြီးလာ၊ သေးသွား တတ်တာကြောင့် goroutine တွေက သက်သာပါတယ်။
  • Garbage collector က concurrent ဖြစ်တဲ့ tri-colour mark-and-sweep ဖြစ်ပြီး low latency အတွက် ချိန်ညှိထားပါတယ်: pause တွေက ပုံမှန်အားဖြင့် millisecond တစ်ခုထက် အများကြီး နည်းပါတယ်။ Pause တိုစေဖို့ CPU နဲ့ memory အပိုအချို့ကို အလဲအလှယ်ပေးထားတာဖြစ်လို့ server တွေနဲ့ ကိုက်ညီပါတယ်။ GOGC နဲ့ GOMEMLIMIT နဲ့ ချိန်ညှိလို့ရပါတယ်။
  • Struct တွေကို C struct တွေလိုပဲ memory ထဲမှာ ဆက်တိုက် စီထားပါတယ်။ []Point ဆိုတာ object တွေဆီ ညွှန်တဲ့ pointer array မဟုတ်ဘဲ memory block တစ်ခုတည်းပါ: cache အတွက်ရော GC အတွက်ပါ အဆင်ပြေပါတယ်။

5. Memory model

Goroutine တစ်ခုက write လုပ်တာကို နောက် goroutine တစ်ခုက ဘယ်အချိန်မှာ မြင်ရမယ်ဆိုတာ သေချာအာမခံနိုင်လဲကို Go က တိတိကျကျ သတ်မှတ်ထားပါတယ်: channel operation တွေ၊ sync primitive တွေ ဒါမှမဟုတ် sync/atomic ကနေတစ်ဆင့်ပါ။ အဲဒါတွေ ပြင်ပမှာ variable တစ်ခုတည်းကို တစ်ပြိုင်နက် access လုပ်တာဟာ data race ဖြစ်ပြီး go test -race က အဲဒါကို ရှာတွေ့ပေးပါလိမ့်မယ်။

အားသာချက်နဲ့ အားနည်းချက်

Go ရဲ့ အားသာချက်တွေ

  • ရိုးရှင်းမှု: keyword ၂၅ ခုလောက်ပဲရှိပါတယ်; Go codebase အများစုကို ရက်သတ္တပတ် အနည်းငယ်အတွင်း ဖတ်နားလည်နိုင်ပါတယ်။ Code review တွေက ပါးနပ်မှုထက် logic ကိုပဲ အာရုံစိုက်ပါတယ်။
  • Performance: compile လုပ်ထားတာ၊ value type တွေနဲ့ low-latency GC ပါတယ်။ ပုံမှန်အားဖြင့် တူညီတဲ့ Node service ထက် memory နဲ့ CPU အများကြီး နည်းနည်းပဲ သုံးပြီး latency ကိုလည်း ကြိုတင်ခန့်မှန်းနိုင်ပါတယ်။
  • Concurrency: goroutine နဲ့ channel တွေကြောင့် parallel I/O နဲ့ CPU အလုပ်တွေကို သဘာဝကျကျ လုပ်နိုင်ပြီး worker-thread တွေကို ကိုယ်တိုင် ချိတ်ဆက်စရာမလိုဘဲ core တိုင်းကို သုံးပါတယ်။
  • Deployment: static binary တစ်ခုတည်း၊ မြန်ဆန်တဲ့ cross-compilation၊ အလွန်သေးတဲ့ container image တွေ (FROM scratch တောင် အလုပ်လုပ်ပါတယ်)။
  • မြန်တဲ့ build နဲ့ ကောင်းမွန်တဲ့ tooling: gofmt၊ go vet၊ go test၊ race detector၊ pprof profiling အားလုံး built-in ပါပြီးသားပါ။
  • Standard library: HTTP/2 server နဲ့ client၊ JSON၊ crypto၊ TLS၊ template၊ testing: dependency မလိုဘဲ production-ready ဖြစ်ပါတယ်။
  • တည်ငြိမ်မှု: Go 1 compatibility promise ကြောင့် 2012 က code တွေ အခုထိ compile ဖြစ်ပါသေးတယ်။ နှစ်စဉ် framework အပြောင်းအလဲ ဗရုတ်သုတ်ခ မရှိပါဘူး။

Go ရဲ့ အားနည်းချက်တွေ

  • စကားများမှု (Verbosity): နေရာတိုင်းမှာ if err != nil၊ ပြီးတော့ JS မှာ .map().filter() သုံးမယ့်နေရာမှာ loop တွေ။
  • Expressive နည်းတဲ့ type system: union type မရှိ၊ enum မရှိ (const + iota ပဲရှိ)၊ mapped ဒါမှမဟုတ် conditional type မရှိ; generics ကိုလည်း ရည်ရွယ်ချက်ရှိရှိ အခြေခံအဆင့်ပဲ ထားပါတယ်။
  • nil ထောင်ချောက်တွေ: nil pointer နဲ့ nil map တွေက panic ဖြစ်စေပါတယ်; nil pointer ကို ကိုင်ထားတဲ့ interface က ကိုယ်တိုင် nil မဖြစ်ပါဘူး၊ ဒါက နာမည်ကြီး ထောင်ချောက်တစ်ခုပါ။
  • Browser အတွက် အဖြေမရှိ: Go ကို WebAssembly အဖြစ် compile လုပ်လို့ရပေမယ့် binary တွေက ကြီးပါတယ်; front end ကတော့ TS/JS ပဲ ဆက်ရှိနေပါတယ်။
  • GC language တစ်ခု: service တွေအတွက် အရမ်းကောင်းပေမယ့် hard real-time ဒါမှမဟုတ် latency ကို နောက်ဆုံးအစက်အထိ ညှစ်ထုတ်ချင်ရင်တော့ Rust ဒါမှမဟုတ် C++ က နိုင်ပါတယ်။
  • JS ဒါမှမဟုတ် Python ထက် UI နဲ့ data-science ecosystem တွေ သေးငယ်ပါတယ်။

Go နဲ့ ဘာတွေ တည်ဆောက်နိုင်လဲ

  • Web API နဲ့ microservices: REST၊ GraphQL၊ gRPC။ Go ရဲ့ အကောင်းဆုံးနေရာပါ; Uber က Go service ထောင်ပေါင်းများစွာကို run ထားပါတယ်။
  • Cloud infrastructure: Docker၊ Kubernetes၊ Terraform နဲ့ Prometheus အားလုံးကို Go နဲ့ ရေးထားတာပါ။
  • Command-line tool တွေ: OS တိုင်းအတွက် binary တစ်ခုတည်း; gh၊ hugo၊ k9s တို့ကို စဉ်းစားကြည့်ပါ။
  • JS/TS အတွက် developer tooling ကိုယ်တိုင်: esbuild၊ ပြီးတော့ အခု TypeScript 7 compiler။
  • Networking နဲ့ proxy တွေ: load balancer၊ DNS၊ VPN၊ API gateway (Caddy၊ Traefik)။
  • Data pipeline နဲ့ worker တွေ: queue၊ ETL၊ stream processing၊ scheduler။
  • Serverless function တွေ: AWS Lambda၊ Google Cloud Run၊ Vercel ရဲ့ Go runtime။
  • စွန့်စားချင်သူတွေအတွက် WebAssembly နဲ့ embedded (TinyGo ကနေတစ်ဆင့်)။

ကုမ္ပဏီကြီးတွေမှာ Go နဲ့ TypeScript ဘေးချင်းယှဉ်

လက်တွေ့မှာ Go သို့မဟုတ် TypeScript ဆိုပြီး တစ်ခုတည်း ရွေးရတာ ရှားပါတယ်။ အဖြစ်များတဲ့ pattern က TypeScript front end (React၊ Next.js) က Go service တွေနဲ့ ဆက်သွယ်ပြီး data အပေါ် နှစ်ဘက်စလုံး သဘောတူညီအောင် shared contract (OpenAPI ဒါမှမဟုတ် Protocol Buffers) တစ်ခု သုံးတာပါ။ ထင်ရှားတဲ့ ဥပမာအချို့:

  • Microsoft: TypeScript ကိုယ်တိုင်ကို အခု Go နဲ့ တည်ဆောက်ထားပါပြီ။ March 2025 မှာ Anders Hejlsberg က Go နဲ့ ရေးထားတဲ့ TypeScript compiler ရဲ့ native port ကို ကြေညာခဲ့ပြီး full build မှာ 10x လောက် ပိုမြန်ပါတယ် (ကြေညာချက်)။ July 2026 မှာ TypeScript 7.0 အဖြစ် ထွက်ရှိလာပြီး full build တွေ 8–12x ပိုမြန်ပါတယ်၊ install လုပ်တာကတော့ npm install -D typescript နဲ့ပဲ ဆက်လုပ်ရပါတယ် (release)။ TS developer တိုင်း အခု နေ့စဉ် Go code ကို run နေကြပါပြီ။
  • Google: Go ကို အဲဒီမှာ ဖန်တီးခဲ့တာဖြစ်ပြီး Search indexing၊ Firebase Hosting၊ Chrome Optimization Guide backend နဲ့ SRE tooling တွေမှာ production မှာ run နေပါတယ် (go.dev)၊ Google ကပဲ ဖန်တီးထားတဲ့ Angular လို TypeScript front end တွေနဲ့ တွဲလျက်ပါ။
  • Uber: 2022 ရောက်တော့ သူ့ရဲ့ Go monorepo မှာ line ပေါင်း 50 million လောက်နဲ့ Go service 2,100 ခုနီးပါး ရှိခဲ့ပါတယ် (Uber Engineering)၊ တစ်ဖက်မှာ web product တွေက React နဲ့ ကိုယ်ပိုင် React component library ဖြစ်တဲ့ Base Web ကို သုံးပါတယ်။
  • Cloudflare: compression၊ DNS နဲ့ SSL လို service တွေရဲ့ အချက်အချာမှာ Go ကို သုံးပါတယ် (go.dev)၊ တစ်ဖက်မှာ Workers platform က TypeScript ကို first-class language အဖြစ် သတ်မှတ်ထားပါတယ် (docs)။
  • PayPal: သူ့ရဲ့ NoSQL system ကို Go နဲ့ ပြန်ရေးခဲ့ပြီး build pipeline နဲ့ load balancer တွေကို Go နဲ့ run ပါတယ်၊ တစ်ဖက်မှာ Java နဲ့ Node.js ကလည်း အဲဒီမှာ first-class language တွေအဖြစ် ဆက်ရှိနေပါတယ် (go.dev)။
  • American Express: C++၊ Java နဲ့ Node.js တို့နဲ့ benchmark နှိုင်းယှဉ်ပြီးနောက် payment နဲ့ rewards microservices တွေအတွက် Go ကို ရွေးခဲ့ပါတယ် (go.dev)။
  • Dropbox: performance အရေးကြီးတဲ့ back end တွေကို concurrency နဲ့ speed အတွက် Python ကနေ Go ကို ပြောင်းခဲ့ပါတယ် (go.dev)။
  • Twitch: အလုပ်အများဆုံး system အများအပြားအတွက် Go ကို သုံးပါတယ်; chat ကို 2013 မှာ Python ကနေ Go နဲ့ ပြန်ရေးခဲ့ပါတယ် (Twitch blog)။
  • Vercel: Next.js ရဲ့ အိမ်ဖြစ်တဲ့ Vercel က serverless function တွေအတွက် Go runtime ကို ပေးထားပြီး project တစ်ခုတည်းထဲမှာ Next.js app ဘေးမှာ Go server ကို run လို့ရပါတယ် (docs)။

Tooling ဘက်က ဇာတ်လမ်းက နှစ်ဘက်လုံး သွားပါတယ်။ Go နဲ့ ရေးထားတဲ့ bundler esbuild က JS build တွေကို သိသိသာသာ ပိုမြန်စေခဲ့ပြီး Vite 7 အထိ Vite ရဲ့ dev pre-bundling ကို မောင်းနှင်ပေးခဲ့ပါတယ်; Vite 8 ကတော့ Rust နဲ့ ရေးထားတဲ့ Rolldown ကို ပြောင်းသွားပါပြီ (Vite 8)။ Turborepo လည်း အဲဒီလမ်းအတိုင်း Go ကနေ Rust ကို ပြောင်းသွားပါတယ် (Vercel)။ JS/TS developer တစ်ယောက်အတွက် သင်ခန်းစာက: သင် နေ့စဉ်သုံးနေတဲ့ tool တွေကို အခု native language တွေနဲ့ တည်ဆောက်နေကြပြီး အဲဒီထဲမှာ Go က အလွယ်ကူဆုံး ချဉ်းကပ်လို့ရတဲ့ language ပါ။

ကမ္ဘာနှစ်ခုကို ချိတ်ဆက်ဖို့ Connect လို tool တွေက Protocol Buffers schema တစ်ခုတည်းကနေ net/http အတွက် Go handler တွေနဲ့ fetch လို ခံစားရတဲ့ TypeScript client တွေကို generate လုပ်ပေးပါတယ်: network တစ်လျှောက် type safety ပါ။ ပြီးတော့ 2025 Go Developer Survey မှာ Go developer တွေက သူတို့ အနှစ်သက်ဆုံး တခြား language တွေအဖြစ် Python၊ Rust နဲ့ TypeScript ကို ဖော်ပြခဲ့ကြပါတယ် (go.dev)။

Zero ကနေ hero ဆီ သင့်ရဲ့ roadmap

  1. ပထမ အပတ်: language။ A Tour of Go ကို လေ့လာပြီး အပေါ်က ဥပမာတိုင်းကို ကိုယ်ပိုင် အပြောင်းအလဲတွေနဲ့ ပြန် run ကြည့်ပါ။
  2. ဒုတိယ အပတ်: idiom တွေ။ Effective Go ကို ဖတ်ပြီး Go by Example ကို လှန်လှောကြည့်ပါ။ Error၊ interface နဲ့ table-driven test တွေကို လေ့ကျင့်ပါ။
  3. တတိယ အပတ်: တကယ့် service တစ်ခု။ net/http၊ database (database/sql + pgx)၊ configuration နဲ့ context သုံးတဲ့ graceful shutdown တို့ပါတဲ့ JSON API တစ်ခု တည်ဆောက်ပါ။
  4. စတုတ္ထ အပတ်: concurrency ကို လက်တွေ့ အသုံးချ။ Worker pool၊ context cancellation၊ errgroup၊ rate limiting၊ ပြီးတော့ အဲဒါအားလုံးပေါ်မှာ go test -race။
  5. ပြီးရင် သင့် stack နဲ့ ချိတ်ပါ: သင့် Go API ပေါ်မှာ Next.js ဒါမှမဟုတ် React front end တစ်ခု တင်ပြီး contract ကို OpenAPI ဒါမှမဟုတ် Connect နဲ့ share ပါ။ pprof နဲ့ profile လုပ်၊ 10 MB container တစ်ခု ship လုပ်ပြီး ညဘက် pager မမြည်တဲ့ ငြိမ်သက်မှုကို ခံစားလိုက်ပါ။

Go က သိသာထင်ရှားတဲ့ အရာကို ရေးတာကို ဆုလာဘ်ပေးပါတယ်။ TypeScript ကနေ လာတဲ့သူအနေနဲ့ type လှည့်ကွက်အချို့ကို ခဏတော့ လွမ်းနေပါလိမ့်မယ်၊ ပြီးမှ အဲဒါတွေကို ဘယ်လောက် ရှားရှားပါးပါးပဲ လိုခဲ့လဲဆိုတာ သတိထားမိလာပါလိမ့်မယ်။

Filed undergotypescriptjavascriptbackend

Written byZin Min Htun (Wine Chanz)Senior Full-Stack Developer · Da Nang, VietnamSay hello at campMore entries