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_moduleson 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
anyescapes by accident, and no type gymnastics either. - Errors are values. No
throw; functions return anerrorand 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.
Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor
Line by line
- Lines starting with
//are comments, exactly like JS. - Every file belongs to a package: Go's unit of code, like an ES module folder.
mainis special: it builds into an executable rather than a library. - Imports name packages by path.
fmt(formatting and printing) andstringscome from the standard library: nonpm install. An unused import is a compile error, so imports never rot. funcdeclares a function.main()in packagemainis the entry point; the program ends when it returns. There's no top-level code that runs on import, as in a JS module.:=declares a new variable and infers its type (string) from the value: thinklet name = "Gopher". It only works inside functions.Sprintfformats into a string with verbs like%s(string),%d(integer),%q(quoted). It plays the role of a JS template literal.Printlnprints its arguments with spaces and a newline:console.log. Exported names start with a capital letter; that capital is Go'sexportkeyword.- Package functions are called as
package.Name, like a namespace import. lenof a string counts bytes, not characters.Printfprints 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.
Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor
Line by line
- A
constis fixed at compile time and may only hold numbers, strings, booleans or runes, unlike a JSconstthat can hold an object. var name Typedeclares without a value. Type annotations come after the name, as in TS (let count: number), just without the colon.- The zero values:
""for strings,falsefor booleans,nilfor slices, maps, pointers and interfaces. Anilslice behaves like an empty one: you can range over it andappendto it. - Prints
0 true false 0. Nothing crashed, nothing wasundefined. - Short declarations infer
int,float64andstring. Untyped numbers becomeintorfloat64by their look. %Tprints a value's type: handy while learning.- Several variables in one statement;
=(not:=) assigns to ones that exist. The swap evaluates the right side first, like[a, b] = [b, a]. - Go's types are fixed for a variable's life.
viewsis anintforever; there are no union types, and assigning a string is a compile error.
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 JSLine by line
- In TS reading
counthere is an error, and in JS it'sundefined. In Go it would just be0. - 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.
Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor
Line by line
intis a 64-bit integer on modern machines,float64is JS'snumber,uint8(aliasbyte) holds 0–255. Explicit types here::=would inferintandfloat64anyway.- Go never converts numbers silently.
likes * pricewould not compile; you write the conversionfloat64(likes). Annoying for a day, then it saves you from whole classes of bugs. ++is a statement in Go, not an expression: you can't writex = y++. Fixed-size integers wrap: 255 + 1 in auint8is 0.- 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. - A
runeis one Unicode code point (anint32). Converting to[]runeand taking the length counts code points: 7. JS's.lengthcounts UTF-16 units, a third answer. rangeover a string decodes UTF-8 for you:iis the byte offset,rthe rune.%cprints a rune as a character. This isfor...ofwith indexes.- An array's length is part of its type:
[7]stringand[6]stringare 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.
Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor
Line by line
- 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. Fieldssplits on any run of whitespace:full.trim().split(/\s+/).iftakes no parentheses, but braces are required. Early returns are the idiomatic style: keep the happy path unindented.- Returning both values. Go has no negative indexing (
parts.at(-1)), so the last element islen(parts)-1. - A variadic parameter: inside,
numsis an[]int, just like...nums: number[]. rangegives 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".- Functions are first-class values. The return type
func() intis the TS type() => number. - A closure: the anonymous function captures
nand keeps it alive between calls, exactly as in JS. deferschedules a call for when the surrounding function returns, even if it panics. It's Go'sfinally, usually written right after opening something:f, _ := os.Open(...), thendefer f.Close().- Receiving two results. If you only wanted one:
first, _ := split(...). slice...spreads a slice into a variadic call:sum(...[10, 20])in JS.- Each
counter()call makes a freshn. Three calls later it printscount: 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.
Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor
Line by line
- A sentinel error: one shared value that callers can compare against. By convention its name starts with
Err. - A custom error type carrying data. Any type with an
Error() stringmethod is anerror; there's noextends Error. - A method: the
(e *ValidationError)before the name is the receiver, Go'sthis. Having this method makes the type satisfy the built-inerrorinterface. - The Go signature for "may fail": the value plus an
error. In TS you'd writePromise<string>and hope callers remember thecatch. Atoi(ASCII to integer) returns(int, error). Theif err != nil { return ... }shape is everywhere in Go: explicit, greppable, and nothing is hidden in a stack unwind.%wwraps an error: the message gains context (post 7: not found) while the original stays inside for checking. Likenew Error(msg, { cause }).- Success returns
nilas the error.nilis the zero value for interfaces likeerror. - A variable for
errors.Asto fill in if the error turns out to be this type. - A
switchwith no value tests eachcaseas a boolean: a tidyif / else ifchain. Cases don't fall through, so nobreakis needed. errors.Islooks through every wrapped layer for that exact value, so the wrappedpost 7: not foundstill matches.errors.Asfinds the first error of that type and stores it, likeif (err instanceof ValidationError)plus a cast.
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
- 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. errisunknownin a TS catch, so you narrow it withinstanceof. Go'serrors.Asdoes 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.
Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor
Line by line
type Name struct { ... }is like a TSinterfacefor data, but it's a real type with a fixed memory layout.Nameis capitalised, so it's exported.- A method with a value receiver:
ais a copy of the Author it's called on. Methods live outside the struct body, anywhere in the same package. Authorwith no field name is embedded: aPostgets the Author's fields and methods, sopost.Nameandpost.Byline()work. That's composition with inheritance's convenience.- 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. - No constructors in Go; a function named
NewXis the convention.title, author stringshares one type between two parameters. - A composite literal builds the struct with named fields (missing ones get zero values).
&returns its address, so callers share one Post. - Go takes the address automatically when calling a pointer method on an addressable value, so the call reads like JS.
Bylineis promoted from the embeddedAuthor.%+vprints a struct with field names: great for debugging, likeconsole.dir.*postreads the value the pointer points at.
class Post extends Author { likes = 0; constructor(public title: string, name: string) { super(name); } like() { this.likes++; }}Line by line
- The class version.
extendscreates 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.
Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor
Line by line
- 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. - Two unrelated structs. Fields of the same type can share a line:
W, H float64. - Each type gets an
Area() float64method, so both now satisfyShape, without ever naming it. You can even satisfy interfaces from packages written after yours. - Adding
String()makesCircleafmt.Stringer, sofmt.Printlnprints it this way, like overridingtoString(). - Accept interfaces, return concrete types: a classic Go guideline.
totalworks for any shape, present or future. - 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). - A type switch asks which concrete type sits inside an interface. In each
case,vhas that type: TS-style narrowing, done at run time. - Prints
Circle(r=1), becausePrintlnnoticed theString()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.
Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor
Line by line
- The
slicespackage (Go 1.21+) brings generic helpers:Sort,Contains,Index,Reverse,Max… []int{...}is a slice literal: no length in the brackets. Under the hood: a pointer to an array, a length and a capacity.appendreturns 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.- Sorts in place, numerically. (No JS
sort()surprise of comparing numbers as strings.) - Prints
[1 2 5 8] 4 6: four elements, room for six before the next reallocation. - The classic gotcha.
nums[:2]is a new view onto the same array, so writingfirstTwo[0]changesnumstoo ([99 2 5 8]). JS'sslice()copies; useslices.Clonewhen you need a copy. - No
.filter()or.map()methods on slices: a plain loop withappendis the idiom, and it's just as fast as it looks. (Generics let you write your ownMap; see below.) - A map literal:
map[KeyType]ValueType. Likenew Map<string, number>()but with literal syntax. - Reading a missing key gives the zero value (
0), so incrementing a new key just works: no(m.get(k) ?? 0) + 1. - The comma-ok form tells missing apart from zero.
if init; conditionscopesnandokto thisif. deleteis 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.
Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor
Line by line
- This receives a copy of the struct. Changing
c.Nchanges only the copy, a real difference from JS, where an object argument is shared. *Counteris "pointer to Counter": the function gets the address and edits the caller's struct.- Still
0: the copy was bumped and thrown away. &ctakes the address ofc. Now it prints1.pis a*Counter. There's no pointer arithmetic in Go (unlike C), so pointers are safe: they only ever point at a valid value ornil.- Go dereferences automatically for field access:
p.Nmeans(*p).N. *preads the whole value behind the pointer. Both print 10: same struct.- A pointer's zero value is
nil. Reading a field through a nil pointer panics: Go's version ofCannot 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.
Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor
Line by line
- A constraint is an interface listing types.
|reads like a TS union;~intmeans "int or any type whose underlying type is int". - Type parameters go in square brackets:
Sum[T Number]≈sum<T extends number>. Callers rarely writeT; it's inferred. - The zero value works for any type, so
totalstarts at 0 for ints and floats alike. any(an alias forinterface{}) is the TSunknown: no constraint at all. This is a typedArray.prototype.map.makecreates a slice with length 0 and capacitylen(in): one allocation instead of several as we append.- Generic types too:
Stack[T]≈class Stack<T>. - Returning
(value, ok)instead ofT | undefined: the caller can't forget the empty case. - The usual trick for "return nothing of type T": its zero value.
- A named type built on
float64. It's a different type fromfloat64, but~float64in the constraint still accepts it. Tis inferred from the arguments,intthenCelsius: prints6 41.5.- Generic types are instantiated with brackets. The zero
Stackis 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.
Run this one and watch the output arrive: the delays are real.
Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor
Line by line
- A pretend network call.
time.Sleepblocks this goroutine only; the others keep running. There's noasynccolouring: any function can be run concurrently. time.Now()andtime.Sincemeasure elapsed time,performance.now()style.- 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. - A
WaitGroupcounts running goroutines:Addbefore starting,Donewhen finished,Waituntil zero. - An anonymous struct slice: a quick inline list of jobs, like an array of object literals.
- The
gokeyword starts a goroutine: this function runs concurrently and the loop continues at once. Like calling an async function withoutawait. Since Go 1.22 each iteration has its ownjob, so the closure is safe. - Mark this job finished when the goroutine returns, whatever happens.
- Send the result into the channel. It waits until someone receives it.
- A helper goroutine waits for all three, then closes the channel to signal "no more values".
rangeover a channel receives until it's closed. Results arrive in finish order (posts, likes, users), as withfor awaitover settled promises.- About 300 ms, not 600: the three sleeps overlapped. That's
Promise.all, without promises. - Start a slow job (800 ms) in the background.
selectwaits on several channel operations and runs whichever is ready first:Promise.race.time.Afterreturns a channel that delivers after 500 ms, so this is a timeout. (In real code,context.Contextcarries deadlines and cancellation.)
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
- The TS equivalent:
Promise.allwaits for all, but returns results in input order, after the slowest one. Go's channel version handled each as it finished. Promise.racewith a timeout promise: the same idea asselectwithtime.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.
Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor
Line by line
- Everything here is standard library: JSON, HTTP client and server, and a test server.
- Struct tags (the back-quoted
json:"id") tellencoding/jsonwhich key to use. Exported (capitalised) fields are the only ones encoded. - A handler: like Express's
(req, res) => {}, but the order is(w, r), writer first. - Streams the slice as JSON straight into the response:
res.json(posts). - Reads the
{id}path parameter:req.params.id. - Writes a status code and a body in one call.
http.StatusNotFoundis the constant 404. - A mux (router):
express(). - Patterns with a method and wildcards (Go 1.22+):
app.get("/api/posts/:id", ...). - In a real program this one line starts the server. Each request runs in its own goroutine automatically, across all cores.
- The playground can't open public ports, so
httpteststarts the server on a local address inside the program: the same tool you'd use in tests. - The built-in HTTP client:
fetch, minus the promise. Note theerrcheck again. - Response bodies must be closed to free the connection; in longer code you'd
defer res.Body.Close().
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
- The Express version needs a dependency; Go's needs none. Both are about the same length.
- 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(createsgo.mod, yourpackage.json)go get github.com/x/y→npm install(pins versions ingo.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 (wrongPrintfverbs, 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
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-apiLine by line
- Starts a module named by its import path (usually where the repo lives).
- Builds and runs the package in the current folder.
- Runs every test in every package, with the race detector and coverage.
- 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.
Tab indents · ⌘/Ctrl + Enter runs · Esc leaves the editor
Line by line
- The standard
testingpackage: no test runner to install. (In a real project this would bepackage journalinslug_test.go.) - The code under test: lowercase, split on whitespace, join with dashes.
- Any
func TestXxx(t *testing.T)is a test.treports failures: nodescribe/it/expectDSL. - Table-driven tests: a slice of inputs and expected outputs, the most common Go testing pattern.
- Compare with plain
!=. Go doesn't encourage assertion libraries, so the failure message is yours to write. Errorfmarks 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
- 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.
- Convert to SSA (static single assignment) and optimise: inlining, escape analysis, dead-code removal, bounds-check elimination.
- Generate machine code for the target OS and CPU (
GOOS/GOARCH), then link everything, including the Go runtime, into a single statically linked executable. - 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
GOMAXPROCSof 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.
GOGCandGOMEMLIMITtune it. - Structs are laid out contiguously, like C structs. A
[]Pointis 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 scratchworks). - Fast builds and great tooling:
gofmt,go vet,go test, the race detector,pprofprofiling, 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 != nileverywhere, 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. nilpitfalls: nil pointers and nil maps panic; an interface holding a nil pointer is not itselfnil, 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
- Week 1: the language. Work through A Tour of Go and re-run every example above with your own changes.
- Week 2: idioms. Read Effective Go and browse Go by Example. Practise errors, interfaces and table-driven tests.
- Week 3: a real service. Build a JSON API with
net/http, a database (database/sql+ pgx), configuration and graceful shutdown withcontext. - Week 4: concurrency in anger. Worker pools,
contextcancellation,errgroup, rate limiting, andgo test -raceon all of it. - 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 ကြည့်ပြီး နာမည်ကို ပြောင်းကြည့်ပါ။
Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်
တစ်ကြောင်းချင်း ရှင်းလင်းချက်
//နဲ့ စတဲ့ line တွေက comment တွေပါ၊ JS နဲ့ အတူတူပါပဲ။- File တိုင်းက package တစ်ခုထဲမှာ ပါပါတယ်: package က Go ရဲ့ code unit ဖြစ်ပြီး ES module folder တစ်ခုလိုပါပဲ။
mainက အထူးပါ: library အစား executable အဖြစ် build ထွက်ပါတယ်။ - Import တွေက package တွေကို path နဲ့ ခေါ်ပါတယ်။
fmt(format လုပ်ခြင်းနဲ့ print ထုတ်ခြင်း) နဲ့stringsက standard library ကလာတာပါ:npm installမလိုပါဘူး။ မသုံးတဲ့ import က compile error ဖြစ်တဲ့အတွက် import တွေ ဘယ်တော့မှ ပုပ်မနေပါဘူး။ funcက function တစ်ခုကို declare လုပ်ပါတယ်။ Packagemainထဲကmain()က entry point ဖြစ်ပြီး အဲဒါ return ပြန်တာနဲ့ program ပြီးသွားပါတယ်။ JS module မှာလို import လုပ်တာနဲ့ run သွားမယ့် top-level code မရှိပါဘူး။:=က variable အသစ်တစ်ခု declare လုပ်ပြီး value ကနေ type (string) ကို infer လုပ်ပါတယ်:let name = "Gopher"လို့ စဉ်းစားကြည့်ပါ။ Function ထဲမှာပဲ သုံးလို့ရပါတယ်။Sprintfက%s(string)၊%d(integer)၊%q(quoted) လို verb တွေနဲ့ string တစ်ခုအဖြစ် format လုပ်ပေးပါတယ်။ JS template literal ရဲ့ အခန်းကဏ္ဍကို ယူထားတာပါ။Printlnက argument တွေကို space ခြားပြီး newline နဲ့ print ထုတ်ပါတယ်:console.logပါပဲ။ Export လုပ်ထားတဲ့ name တွေက စာလုံးကြီး နဲ့ စပါတယ်; အဲဒီ စာလုံးကြီးက Go ရဲ့exportkeyword ပါပဲ။- Package function တွေကို
package.Nameပုံစံနဲ့ ခေါ်ရပါတယ်၊ namespace import လိုပါပဲ။ - String ရဲ့
lenက character မဟုတ်ဘဲ byte တွေကို ရေတွက်ပါတယ်။Printfက newline မထည့်ပေးတဲ့အတွက် format ကို\nနဲ့ အဆုံးသတ်ထားပါတယ်။
Variable, constant နဲ့ zero value
TypeScript မှာ let, const နဲ့ မသတ်မှတ်ရသေးတာမှန်သမျှအတွက် undefined ရှိပါတယ်။ Go မှာ undefined မရှိပါဘူး: variable တိုင်းက သူ့ type ရဲ့ zero value နဲ့ စတဲ့အတွက် ဖတ်လို့ရတဲ့ valid value တစ်ခု အမြဲရှိနေပါတယ်။
Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်
တစ်ကြောင်းချင်း ရှင်းလင်းချက်
constက compile time မှာ သတ်မှတ်ပြီးသားဖြစ်ပြီး number, string, boolean ဒါမှမဟုတ် rune တွေကိုပဲ သိမ်းလို့ရပါတယ်၊ object သိမ်းလို့ရတဲ့ JSconstနဲ့ မတူပါဘူး။var name Typeက value မပေးဘဲ declare လုပ်တာပါ။ Type annotation က name ရဲ့ နောက်မှာ လာပါတယ်၊ TS (let count: number) လိုပါပဲ၊ colon မပါတာပဲ ကွာပါတယ်။- Zero value တွေက: string အတွက်
""၊ boolean အတွက်false၊ slice, map, pointer နဲ့ interface တွေအတွက်nil။nilslice က empty slice လိုပဲ အလုပ်လုပ်ပါတယ်: range လုပ်လို့ရပြီးappendလည်း လုပ်လို့ရပါတယ်။ 0 true false 0ကို print ထုတ်ပါတယ်။ ဘာမှ crash မဖြစ်သလို ဘာမှလည်းundefinedမဖြစ်ပါဘူး။- Short declaration တွေက
int,float64နဲ့stringကို infer လုပ်ပါတယ်။ Type မသတ်မှတ်ထားတဲ့ number တွေက သူတို့ပုံစံကိုကြည့်ပြီးintဒါမှမဟုတ်float64ဖြစ်သွားပါတယ်။ %Tက value ရဲ့ type ကို print ထုတ်ပေးပါတယ်: လေ့လာနေတုန်း အသုံးဝင်ပါတယ်။- Statement တစ်ခုထဲမှာ variable အများကြီး;
=(:=မဟုတ်) က ရှိပြီးသား variable တွေကို assign လုပ်ပါတယ်။ Swap မှာ ညာဘက်ကို အရင် evaluate လုပ်ပါတယ်၊[a, b] = [b, a]လိုပါပဲ။ - Go မှာ variable တစ်ခုရဲ့ type က သူ့သက်တမ်းတစ်လျှောက် မပြောင်းပါဘူး။
viewsက အမြဲတမ်းintပါ; union type မရှိသလို string assign လုပ်ရင် compile error ဖြစ်ပါတယ်။
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တစ်ကြောင်းချင်း ရှင်းလင်းချက်
- TS မှာ ဒီနေရာမှာ
countကို ဖတ်ရင် error ဖြစ်ပြီး JS မှာတော့undefinedပါ။ Go မှာဆိုရင်0ပဲ ဖြစ်မှာပါ။ - 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 ကို ကိုင်တွယ်ရတဲ့အချိန်ကျရင် ဒါက ချက်ချင်း အရေးကြီးလာပါတယ်။
Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်
တစ်ကြောင်းချင်း ရှင်းလင်းချက်
intက ခေတ်ပေါ်စက်တွေမှာ 64-bit integer ဖြစ်ပြီးfloat64က JS ရဲ့numberပါ၊uint8(aliasbyte) က 0–255 ကို သိမ်းပါတယ်။ ဒီမှာ type ကို တိုက်ရိုက်ရေးထားတာပါ::=သုံးရင်လည်းintနဲ့float64ကို infer လုပ်မှာပါပဲ။- Go က number တွေကို အသံတိတ် convert မလုပ်ပါဘူး။
likes * priceက compile မဖြစ်ပါဘူး;float64(likes)ဆိုပြီး conversion ကို ကိုယ်တိုင်ရေးရပါတယ်။ တစ်ရက်လောက် စိတ်ညစ်ရပေမယ့် bug အမျိုးအစား အများကြီးကနေ ကာကွယ်ပေးပါတယ်။ - Go မှာ
++က expression မဟုတ်ဘဲ statement ပါ:x = y++လို့ ရေးလို့မရပါဘူး။ Fixed-size integer တွေက wrap ဖြစ်ပါတယ်:uint8မှာ 255 + 1 က 0 ပါ။ - Go source file က UTF-8 ဖြစ်တဲ့အတွက် ဒီမြန်မာစကားလုံးက ပြဿနာမရှိပါဘူး။
len(word)က byte (21) ကို ရေတွက်ပါတယ်၊ မြန်မာ code point တစ်ခုစီက UTF-8 မှာ 3 byte ယူလို့ပါ။ runeက Unicode code point တစ်ခု (int32တစ်ခု) ပါ။[]runeကို convert လုပ်ပြီး length ယူရင် code point တွေကို ရေတွက်ပါတယ်: 7။ JS ရဲ့.lengthက UTF-16 unit တွေကို ရေတွက်တဲ့အတွက် တတိယ အဖြေတစ်ခု ထပ်ထွက်လာပါတယ်။- String ကို
rangeလုပ်ရင် UTF-8 ကို decode လုပ်ပေးပါတယ်:iက byte offset၊rက rune ပါ။%cက rune ကို character အဖြစ် print ထုတ်ပါတယ်။ ဒါက index ပါတဲ့for...ofပါပဲ။ - 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 လာတာပါ။
Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်
တစ်ကြောင်းချင်း ရှင်းလင်းချက်
- Parameter တွေက
name typeပုံစံပါ။ Result list(first, last string)က string နှစ်ခုဖြစ်တဲ့ named result နှစ်ခု ကို declare လုပ်ပါတယ်။ TS မှာ object ဒါမှမဟုတ် tuple return ပြန်တဲ့နေရာမှာ Go က value အများကြီး return ပြန်ပါတယ်။ Fieldsက ဆက်တိုက်ဖြစ်နေတဲ့ whitespace ဘယ်လောက်ပဲရှိရှိ ခွဲပေးပါတယ်:full.trim().split(/\s+/)ပါပဲ။ifမှာ ကွင်းစကွင်းပိတ် မလိုပေမယ့် brace တွေကတော့ မဖြစ်မနေ လိုပါတယ်။ Early return က idiomatic style ပါ: happy path ကို indent မဖြစ်အောင် ထားပါ။- Value နှစ်ခုလုံးကို return ပြန်တာပါ။ Go မှာ negative indexing (
parts.at(-1)) မရှိတဲ့အတွက် နောက်ဆုံး element ကlen(parts)-1ပါ။ - Variadic parameter တစ်ခုပါ: function ထဲမှာ
numsက[]intဖြစ်ပါတယ်၊...nums: number[]လိုပါပဲ။ rangeက index နဲ့ value ကို ပေးပါတယ်;_က index ကို လွှင့်ပစ်တဲ့ blank identifier ပါ။ Go က မသုံးတဲ့ variable တွေကို လက်မခံတဲ့အတွက်_က "ဒါ မလိုဘူး" လို့ ပြောတဲ့ နည်းပါ။- Function တွေက first-class value တွေပါ။ Return type
func() intက TS type() => numberပါပဲ။ - Closure တစ်ခုပါ: anonymous function က
nကို capture လုပ်ပြီး call တစ်ခုနဲ့တစ်ခုကြား ဆက်ရှင်နေအောင် ထိန်းထားပါတယ်၊ JS မှာလိုပါပဲ။ deferက ဝန်းရံထားတဲ့ function return ပြန်တဲ့အချိန်မှာ call တစ်ခုကို run ဖို့ စီစဉ်ထားပါတယ်၊ panic ဖြစ်ရင်တောင် run ပါတယ်။ ဒါက Go ရဲ့finallyပါ၊ အများအားဖြင့် တစ်ခုခုကို ဖွင့်ပြီးတာနဲ့ ချက်ချင်း ရေးလေ့ရှိပါတယ်:f, _ := os.Open(...)၊ ပြီးရင်defer f.Close()။- Result နှစ်ခုကို လက်ခံတာပါ။ တစ်ခုပဲ လိုချင်ရင်:
first, _ := split(...)။ slice...က slice တစ်ခုကို variadic call ထဲ ဖြန့်ထည့်ပေးပါတယ်: JS မှာဆိုsum(...[10, 20])ပါ။counter()ကို ခေါ်တိုင်းnအသစ်တစ်ခု ဖန်တီးပါတယ်။ သုံးကြိမ်ခေါ်ပြီးရင်count: 3ကို print ထုတ်ပြီး အဲဒီနောက်မှသာ defer လုပ်ထားတဲ့ line က run ပါတယ်။
Error တွေက exception မဟုတ်ဘဲ value တွေပါ
ဒါက အကြီးဆုံး အလေ့အထ ပြောင်းလဲမှုပါ။ Go မှာ သာမန် failure တွေအတွက် try/catch မရှိပါဘူး။ Fail ဖြစ်နိုင်တဲ့ function က နောက်ဆုံး result အဖြစ် error တစ်ခု return ပြန်ပြီး ခေါ်တဲ့သူက အဲဒီနေရာမှာတင် ဘာလုပ်မလဲ ဆုံးဖြတ်ပါတယ်။
Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်
တစ်ကြောင်းချင်း ရှင်းလင်းချက်
- Sentinel error တစ်ခုပါ: ခေါ်တဲ့သူတွေ နှိုင်းယှဉ်စစ်ဆေးလို့ရတဲ့ မျှဝေသုံးတဲ့ value တစ်ခု။ Convention အရ နာမည်ကို
Errနဲ့ စပါတယ်။ - Data သယ်ဆောင်တဲ့ custom error type တစ်ခုပါ။
Error() stringmethod ရှိတဲ့ ဘယ် type မဆိုerrorဖြစ်ပါတယ်;extends Errorမရှိပါဘူး။ - Method တစ်ခုပါ: နာမည်ရှေ့က
(e *ValidationError)က receiver ဖြစ်ပြီး Go ရဲ့thisပါ။ ဒီ method ရှိတာကြောင့် type က built-inerrorinterface ကို satisfy ဖြစ်ပါတယ်။ - "Fail ဖြစ်နိုင်တယ်" ဆိုတာအတွက် Go signature: value နဲ့
errorတစ်ခု။ TS မှာဆိုPromise<string>လို့ရေးပြီး ခေါ်တဲ့သူတွေcatchကို မမေ့ဖို့ မျှော်လင့်ရုံပါပဲ။ Atoi(ASCII to integer) က(int, error)ကို return ပြန်ပါတယ်။if err != nil { return ... }ပုံစံကို Go မှာ နေရာတိုင်း တွေ့ရပါတယ်: ရှင်းရှင်းလင်းလင်း ရှိတယ်၊ grep လုပ်လို့ရတယ်၊ stack unwind ထဲမှာ ဘာမှ ဝှက်မထားပါဘူး။%wက error ကို wrap လုပ်ပါတယ်: message မှာ context (post 7: not found) ထပ်ပါလာပြီး မူရင်း error က စစ်ဆေးလို့ရအောင် အထဲမှာ ဆက်ရှိနေပါတယ်။new Error(msg, { cause })လိုပါပဲ။- အောင်မြင်ရင် error အဖြစ်
nilကို return ပြန်ပါတယ်။nilကerrorလို interface တွေရဲ့ zero value ပါ။ - Error က ဒီ type ဖြစ်နေခဲ့ရင်
errors.Asက ဖြည့်ပေးဖို့ variable တစ်ခုပါ။ - Value မပါတဲ့
switchကcaseတစ်ခုစီကို boolean အဖြစ် စစ်ပါတယ်: သပ်ရပ်တဲ့if / else ifchain တစ်ခုပါပဲ။ Case တွေက fall through မဖြစ်တဲ့အတွက်breakမလိုပါဘူး။ errors.Isက wrap ထားတဲ့ layer တိုင်းကို ဖောက်ကြည့်ပြီး အဲဒီ value အတိအကျကို ရှာတဲ့အတွက် wrap ထားတဲ့post 7: not foundက match ဖြစ်နေဆဲပါ။errors.Asက အဲဒီ type ရဲ့ ပထမဆုံး error ကို ရှာပြီး သိမ်းပေးပါတယ်၊if (err instanceof ValidationError)နဲ့ cast တစ်ခု ပေါင်းထားသလိုပါပဲ။
try { const title = await findPost(id); console.log("found:", title);} catch (err) { if (err instanceof NotFoundError) console.log("404"); else throw err;}တစ်ကြောင်းချင်း ရှင်းလင်းချက်
- TS version ပါ: failure က တစ်ခုခုက catch မလုပ်မချင်း stack အပေါ်ကို မမြင်ရဘဲ တက်သွားပါတယ်။
findPost(id): Promise<string>signature က fail ဖြစ်နိုင်တယ်ဆိုတာ မပြောပြပါဘူး။ - TS catch ထဲမှာ
errကunknownဖြစ်တဲ့အတွက်instanceofနဲ့ narrow လုပ်ရပါတယ်။ Go ရဲ့errors.Asက အဲဒီအလုပ်ကိုပဲ လုပ်ပါတယ်။
Class အစား struct နဲ့ method
Go မှာ class မရှိသလို inheritance လည်း မရှိပါဘူး။ Data ကို struct နဲ့ သတ်မှတ်၊ behaviour ကို method တွေနဲ့ တွဲထည့်ပြီး struct တစ်ခုကို နောက်တစ်ခုထဲ embed လုပ်ပြီး code ကို ပြန်သုံးပါတယ်။ extends ကို လွမ်းမနေမိဘူးဆိုတာ သိလာပါလိမ့်မယ်။
Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်
တစ်ကြောင်းချင်း ရှင်းလင်းချက်
type Name struct { ... }က data အတွက် TSinterfaceလိုပါပဲ၊ ဒါပေမယ့် fixed memory layout ရှိတဲ့ တကယ့် type တစ်ခုပါ။Nameက စာလုံးကြီးနဲ့ စတဲ့အတွက် export ဖြစ်ပါတယ်။- Value receiver ပါတဲ့ method တစ်ခုပါ:
aက ခေါ်လိုက်တဲ့ Author ရဲ့ copy တစ်ခုပါ။ Method တွေက struct body အပြင်ဘက်မှာ ရှိပြီး package တစ်ခုတည်းထဲမှာ ဘယ်နေရာမဆို ရေးလို့ရပါတယ်။ - Field name မပါတဲ့
Authorက embed လုပ်ထားတာပါ:Postက Author ရဲ့ field တွေနဲ့ method တွေကို ရရှိတဲ့အတွက်post.Nameနဲ့post.Byline()အလုပ်လုပ်ပါတယ်။ ဒါက inheritance ရဲ့ အဆင်ပြေမှုပါတဲ့ composition ပါ။ - Pointer receiver (
*Post): method က copy ကို မဟုတ်ဘဲ မူရင်းကို ပြောင်းပါတယ်။ လက်တွေ့စည်းမျဉ်း: method က mutate လုပ်ရင် ဒါမှမဟုတ် struct က ကြီးရင် pointer receiver ကို သုံးပါ။ - Go မှာ constructor မရှိပါဘူး;
NewXလို့ နာမည်ပေးထားတဲ့ function က convention ပါ။title, author stringက parameter နှစ်ခုကြား type တစ်ခုကို မျှသုံးထားတာပါ။ - Composite literal က named field တွေနဲ့ struct ကို တည်ဆောက်ပါတယ် (မပါတဲ့ field တွေက zero value ရပါတယ်)။
&က သူ့ address ကို ပြန်ပေးတဲ့အတွက် ခေါ်တဲ့သူတွေ Post တစ်ခုတည်းကို မျှသုံးကြပါတယ်။ - Address ယူလို့ရတဲ့ value ပေါ်မှာ pointer method ကို ခေါ်တဲ့အခါ Go က address ကို အလိုအလျောက် ယူပေးတဲ့အတွက် call က JS လိုပဲ ဖတ်ရပါတယ်။
Bylineက embed လုပ်ထားတဲ့Authorကနေ promote ဖြစ်လာတာပါ။%+vက struct ကို field name တွေနဲ့တကွ print ထုတ်ပါတယ်: debug လုပ်ဖို့ အရမ်းကောင်းပါတယ်၊console.dirလိုပါပဲ။*postက pointer ညွှန်ပြနေတဲ့ value ကို ဖတ်ပါတယ်။
class Post extends Author { likes = 0; constructor(public title: string, name: string) { super(name); } like() { this.likes++; }}တစ်ကြောင်းချင်း ရှင်းလင်းချက်
- 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 မှာလည်း အဲဒီ အချက်အလက်ကို ဆက်သိမ်းထားတာပါ။
Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်
တစ်ကြောင်းချင်း ရှင်းလင်းချက်
- Interface ဆိုတာ method signature တွေရဲ့ အစုအဝေးပါ။ သေးသေးလေးပဲ ထားပါ: standard library မှာ အသုံးအများဆုံး interface တွေ (
io.Reader,error,fmt.Stringer) မှာ method တစ်ခုစီပဲ ရှိပါတယ်။ - တစ်ခုနဲ့တစ်ခု မဆက်စပ်တဲ့ struct နှစ်ခု။ type တူတဲ့ field တွေကို တစ်ကြောင်းတည်းမှာ ရေးလို့ရပါတယ်:
W, H float64။ - type တစ်ခုစီမှာ
Area() float64method ရှိလို့ နှစ်ခုလုံးShapeကို satisfy ဖြစ်သွားပါပြီ၊ နာမည်တောင် တစ်ခါမှ မဖော်ပြဘဲနဲ့ပါ။ သင့် package ထက် နောက်မှ ရေးထားတဲ့ package တွေက interface တွေကိုတောင် satisfy ဖြစ်အောင် လုပ်နိုင်ပါတယ်။ String()ထည့်လိုက်တော့Circleကfmt.Stringerဖြစ်သွားလို့fmt.Printlnက ဒီပုံစံအတိုင်း print ထုတ်ပါတယ်၊toString()ကို override လုပ်သလိုပါပဲ။- Interface တွေကို လက်ခံပြီး concrete type တွေကို return ပြန်ပါ: Go ရဲ့ classic guideline တစ်ခုပါ။
totalက အခုရှိတဲ့ shape ဖြစ်ဖြစ်၊ နောင်လာမယ့် shape ဖြစ်ဖြစ် ဘယ် shape နဲ့မဆို အလုပ်လုပ်ပါတယ်။ - Interface type ရဲ့ slice တစ်ခုထဲမှာ concrete type အမျိုးမျိုးကို ထည့်ထားနိုင်ပါတယ်။
Rect{3, 4}က field တွေကို အစဉ်လိုက် ဖြည့်ပါတယ် (struct သေးသေးလေးတွေအတွက် ရပါတယ်၊ field နာမည်တွေနဲ့ ရေးရင် ပိုရှင်းပါတယ်)။ - Type switch က interface ထဲမှာ ဘယ် concrete type ရှိနေလဲလို့ မေးပါတယ်။
caseတစ်ခုစီထဲမှာvက အဲဒီ type ဖြစ်နေပါတယ်: TS ပုံစံ narrowing ကို run time မှာ လုပ်တာပါ။ PrintlnကString()method ကို သတိထားမိလို့Circle(r=1)လို့ print ထုတ်ပါတယ်။
Slice နဲ့ map: Go ရဲ့ array နဲ့ object တွေ
JS မှာ array သုံးတဲ့ နေရာတိုင်းမှာ Go မှာတော့ slice ကို သုံးရမှာဖြစ်ပြီး map ကတော့ Go ရဲ့ Map/Record ပါ။ Slice တွေမှာ အရေးကြီးတဲ့ အသေးစိတ်တစ်ခု ကွယ်နေပါတယ်: slice အများအပြားက underlying array တစ်ခုတည်းကို မျှသုံးနေနိုင်ပါတယ်။
Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်
တစ်ကြောင်းချင်း ရှင်းလင်းချက်
slicespackage (Go 1.21+) က generic helper တွေကို ယူလာပေးပါတယ်:Sort,Contains,Index,Reverse,Max…[]int{...}က slice literal ပါ: bracket ထဲမှာ length မပါပါဘူး။ နောက်ကွယ်မှာတော့ array ကို ညွှန်တဲ့ pointer တစ်ခု၊ length တစ်ခုနဲ့ capacity တစ်ခု ပါပါတယ်။appendက (အသစ်ဖြစ်ကောင်းဖြစ်နိုင်တဲ့) slice ကို return ပြန်ပါတယ်: အမြဲ ပြန် assign လုပ်ပါ။ Capacity ကုန်သွားရင် ပိုကြီးတဲ့ array အသစ်ကို allocate လုပ်ပြီး copy ကူးပါတယ်၊ V8 က သင့် array တွေရဲ့ အတွင်းမှာ လုပ်သလိုပါပဲ။- နေရာမှာတင် (in place) ဂဏန်းတန်ဖိုးအလိုက် sort လုပ်ပါတယ်။ (ဂဏန်းတွေကို string အဖြစ် နှိုင်းယှဉ်တဲ့ JS
sort()ရဲ့ အံ့အားသင့်စရာ မရှိပါဘူး။) [1 2 5 8] 4 6လို့ print ထုတ်ပါတယ်: element လေးခု၊ နောက်တစ်ကြိမ် reallocate မလုပ်ခင် ခြောက်ခုစာ နေရာရှိပါတယ်။- Classic gotcha။
nums[:2]က array တစ်ခုတည်း ပေါ်ကို ကြည့်တဲ့ view အသစ်ပါ၊ ဒါကြောင့်firstTwo[0]ကို ရေးလိုက်ရင်numsပါ ပြောင်းသွားပါတယ် ([99 2 5 8])။ JS ရဲ့slice()က copy ကူးပါတယ်၊ copy လိုရင်slices.Cloneကို သုံးပါ။ - Slice တွေမှာ
.filter()ဒါမှမဟုတ်.map()method မရှိပါဘူး:appendနဲ့ ရိုးရိုး loop ရေးတာက idiom ဖြစ်ပြီး မြင်ရသလောက်ပဲ မြန်ပါတယ်။ (Generics နဲ့ ကိုယ်ပိုင်Mapရေးလို့ရပါတယ်၊ အောက်မှာ ကြည့်ပါ။) - Map literal တစ်ခု:
map[KeyType]ValueType။new Map<string, number>()နဲ့ တူပေမယ့် literal syntax နဲ့ ရေးလို့ရပါတယ်။ - မရှိတဲ့ key ကို ဖတ်ရင် zero value (
0) ရပါတယ်၊ ဒါကြောင့် key အသစ်ကို increment လုပ်တာ ဒီအတိုင်းပဲ အလုပ်ဖြစ်ပါတယ်:(m.get(k) ?? 0) + 1မလိုပါဘူး။ - Comma-ok ပုံစံက မရှိတာ နဲ့ zero ဖြစ်နေတာ ကို ခွဲခြားပေးပါတယ်။
if init; conditionကnနဲ့okကို ဒီifအတွင်းမှာပဲ scope ဖြစ်အောင် လုပ်ပေးပါတယ်။ 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 ကို သုံးရပါတယ်။
Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်
တစ်ကြောင်းချင်း ရှင်းလင်းချက်
- ဒီ function က struct ရဲ့ copy ကို လက်ခံပါတယ်။
c.Nကို ပြောင်းရင် copy ကိုပဲ ပြောင်းတာပါ၊ object argument ကို မျှသုံးတဲ့ JS နဲ့ တကယ်ကွာတဲ့ အချက်ပါ။ *Counterက "Counter ကို ညွှန်တဲ့ pointer" ပါ: function က address ကို ရပြီး ခေါ်တဲ့ဘက်က struct ကို ပြင်ပါတယ်။0ပဲ ရှိနေဆဲပါ: copy ကို တိုးပြီးမှ လွှင့်ပစ်လိုက်တာပါ။&cကcရဲ့ address ကို ယူပါတယ်။ အခု1လို့ print ထုတ်ပါပြီ။pက*Counterပါ။ Go မှာ (C နဲ့မတူဘဲ) pointer arithmetic မရှိလို့ pointer တွေက safe ဖြစ်ပါတယ်: valid value တစ်ခု ဒါမှမဟုတ်nilကိုပဲ အမြဲ ညွှန်ပါတယ်။- Field access အတွက် Go က အလိုအလျောက် dereference လုပ်ပေးပါတယ်:
p.Nဆိုတာ(*p).Nပါ။ *pက pointer နောက်ကွယ်က value တစ်ခုလုံးကို ဖတ်ပါတယ်။ နှစ်ခုလုံး 10 လို့ print ထုတ်ပါတယ်: struct တစ်ခုတည်းမို့ပါ။- 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 တွေအတွက် လုံလောက်ပါတယ်။
Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်
တစ်ကြောင်းချင်း ရှင်းလင်းချက်
- Constraint ဆိုတာ type တွေကို စာရင်းပြုထားတဲ့ interface ပါ။
|က TS union လိုပဲ ဖတ်ရပါတယ်၊~intဆိုတာ "int ဒါမှမဟုတ် underlying type က int ဖြစ်တဲ့ ဘယ် type မဆို" လို့ ဆိုလိုပါတယ်။ - Type parameter တွေကို square bracket ထဲမှာ ရေးပါတယ်:
Sum[T Number]≈sum<T extends number>။ ခေါ်တဲ့သူကTကို ရေးရခဲပါတယ်၊ infer လုပ်ပေးလို့ပါ။ - Zero value က ဘယ် type အတွက်မဆို အလုပ်လုပ်လို့
totalက int ဖြစ်ဖြစ် float ဖြစ်ဖြစ် 0 ကနေ စပါတယ်။ any(interface{}ရဲ့ alias) က TS ရဲ့unknownပါ: constraint လုံးဝ မရှိပါဘူး။ ဒါက typedArray.prototype.mapတစ်ခုပါ။makeက length 0 နဲ့ capacitylen(in)ရှိတဲ့ slice ကို ဖန်တီးပါတယ်: append လုပ်ရင်း အကြိမ်ကြိမ် allocate လုပ်မယ့်အစား တစ်ကြိမ်တည်း allocate လုပ်ပါတယ်။- Generic type တွေလည်း ရပါတယ်:
Stack[T]≈class Stack<T>။ T | undefinedအစား(value, ok)ကို return ပြန်ပါတယ်: ခေါ်တဲ့သူက empty case ကို မေ့လို့မရပါဘူး။- "Type T ရဲ့ ဘာမှမရှိတာကို return ပြန်ဖို့" ပုံမှန်သုံးတဲ့ နည်းလမ်း: သူ့ရဲ့ zero value ပါ။
float64ပေါ်မှာ တည်ဆောက်ထားတဲ့ named type တစ်ခုပါ။float64နဲ့ မတူတဲ့ type ဖြစ်ပေမယ့် constraint ထဲက~float64က လက်ခံပါသေးတယ်။Tကို argument တွေကနေ infer လုပ်ပါတယ်၊intပြီးတော့Celsius:6 41.5လို့ print ထုတ်ပါတယ်။- 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 ကို မျှသုံးပါ။
ဒါကို run ကြည့်ပြီး output ရောက်လာတာကို စောင့်ကြည့်ပါ: delay တွေက တကယ့် delay တွေပါ။
Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်
တစ်ကြောင်းချင်း ရှင်းလင်းချက်
- Network call အတု တစ်ခု။
time.Sleepက ဒီ goroutine ကိုပဲ block လုပ်ပါတယ်၊ ကျန်တာတွေက ဆက် run နေပါတယ်။asynccolouring မရှိပါဘူး: ဘယ် function ကိုမဆို concurrent run လို့ရပါတယ်။ time.Now()နဲ့time.Sinceက ကြာချိန်ကိုperformance.now()ပုံစံ တိုင်းပါတယ်။makeနဲ့ ဖန်တီးထားတဲ့ string channel တစ်ခုပါ။ ဒီ unbuffered channel ပေါ်မှာ ပို့တာ (ch <- v) နဲ့ လက်ခံတာ (<-ch) က တစ်ခုနဲ့တစ်ခု စောင့်ကြပါတယ်: built-in လက်လွှဲမှုတစ်ခုပါ။WaitGroupက run နေတဲ့ goroutine တွေကို ရေတွက်ပါတယ်: မစခင်Add၊ ပြီးရင်Done၊ သုည ဖြစ်တဲ့အထိWait။- Anonymous struct slice တစ်ခု: object literal array လိုမျိုး job စာရင်းကို inline မြန်မြန်ရေးတာပါ။
gokeyword က goroutine တစ်ခုကို စတင်ပါတယ်: ဒီ function က concurrent run ပြီး loop က ချက်ချင်း ဆက်သွားပါတယ်။ async function ကိုawaitမပါဘဲ ခေါ်သလိုပါပဲ။ Go 1.22 ကစပြီး iteration တစ်ခုစီမှာ ကိုယ်ပိုင်jobရှိလို့ closure က safe ဖြစ်ပါတယ်။- ဘာပဲဖြစ်ဖြစ် goroutine က return ပြန်တဲ့အခါ ဒီ job ပြီးပြီလို့ မှတ်ပါ။
- Result ကို channel ထဲ ပို့ပါ။ တစ်ယောက်ယောက်က လက်ခံတဲ့အထိ စောင့်ပါတယ်။
- Helper goroutine တစ်ခုက သုံးခုလုံးကို စောင့်ပြီးမှ "value တွေ ထပ်မလာတော့ဘူး" လို့ အချက်ပြဖို့ channel ကို close လုပ်ပါတယ်။
- Channel ပေါ်မှာ
rangeက close မဖြစ်မချင်း လက်ခံပါတယ်။ Result တွေက ပြီးတဲ့အစဉ်အတိုင်း (posts, likes, users) ရောက်လာပါတယ်၊ settled ဖြစ်ပြီးတဲ့ promise တွေပေါ်မှာfor awaitလုပ်သလိုပါပဲ။ - 600 မဟုတ်ဘဲ 300 ms လောက်ပဲ ကြာပါတယ်: sleep သုံးခုက တစ်ပြိုင်နက် ထပ်နေခဲ့လို့ပါ။ Promise မပါတဲ့
Promise.allပါပဲ။ - နှေးတဲ့ job (800 ms) တစ်ခုကို background မှာ စတင်ပါ။
selectက channel operation အများအပြားကို စောင့်ပြီး အရင်အဆင်သင့်ဖြစ်တဲ့ တစ်ခုကို run ပါတယ်:Promise.raceပါ။time.Afterက 500 ms ကြာရင် value ပို့ပေးမယ့် channel ကို return ပြန်လို့ ဒါက timeout ပါ။ (တကယ့် code မှာတော့context.Contextက deadline တွေနဲ့ cancellation ကို သယ်ဆောင်ပါတယ်။)
const results = await Promise.all([ fetchJob("users", 300), fetchJob("posts", 100), fetchJob("likes", 200),]);const report = await Promise.race([fetchJob("report", 800), timeout(500)]);တစ်ကြောင်းချင်း ရှင်းလင်းချက်
- TS နဲ့ ညီမျှတဲ့ code:
Promise.allက အားလုံးကို စောင့်ပေမယ့် အနှေးဆုံးတစ်ခု ပြီးမှ result တွေကို input order အတိုင်း return ပြန်ပါတယ်။ Go ရဲ့ channel version ကတော့ တစ်ခုချင်းစီကို ပြီးတာနဲ့ handle လုပ်ခဲ့ပါတယ်။ - 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 နဲ့ ညီမျှတာတောင် မလိုပါဘူး။
Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်
တစ်ကြောင်းချင်း ရှင်းလင်းချက်
- ဒီမှာ အားလုံးက standard library ပါ: JSON၊ HTTP client နဲ့ server၊ test server တစ်ခု။
- Struct tag တွေ (back-quote နဲ့ရေးထားတဲ့
json:"id") ကencoding/jsonကို ဘယ် key သုံးရမလဲ ပြောပါတယ်။ Export လုပ်ထားတဲ့ (စာလုံးကြီးနဲ့စတဲ့) field တွေကိုပဲ encode လုပ်ပါတယ်။ - Handler တစ်ခု: Express ရဲ့
(req, res) => {}လိုပါပဲ၊ ဒါပေမဲ့ order က(w, r)၊ writer အရင်လာပါတယ်။ - Slice ကို JSON အဖြစ် response ထဲ တိုက်ရိုက် stream လုပ်ပါတယ်:
res.json(posts)။ {id}path parameter ကို ဖတ်ပါတယ်:req.params.id။- Status code နဲ့ body ကို call တစ်ခုတည်းနဲ့ ရေးပါတယ်။
http.StatusNotFoundက constant 404 ပါ။ - Mux (router) တစ်ခု:
express()။ - Method နဲ့ wildcard ပါတဲ့ pattern တွေ (Go 1.22+):
app.get("/api/posts/:id", ...)။ - တကယ့် program မှာဆိုရင် ဒီတစ်ကြောင်းက server ကို စတင်ပါတယ်။ Request တစ်ခုစီက core အားလုံးပေါ်မှာ ကိုယ်ပိုင် goroutine နဲ့ အလိုအလျောက် run ပါတယ်။
- Playground က public port တွေ ဖွင့်လို့မရလို့
httptestက program ထဲမှာပဲ local address ပေါ်မှာ server ကို စတင်ပါတယ်: test တွေမှာ သုံးမယ့် tool အတူတူပါပဲ။ - Built-in HTTP client: promise မပါတဲ့
fetchပါ။errcheck ကို ထပ်တွေ့ရပါတယ်။ - Connection ကို free ဖြစ်အောင် response body တွေကို close လုပ်ရပါမယ်၊ ပိုရှည်တဲ့ code မှာဆိုရင်
defer res.Body.Close()လုပ်ပါလိမ့်မယ်။
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);တစ်ကြောင်းချင်း ရှင်းလင်းချက်
- Express version က dependency လိုပါတယ်၊ Go version က ဘာမှ မလိုပါဘူး။ နှစ်ခုလုံး အရှည် သိပ်မကွာပါဘူး။
- 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 (မှားနေတဲ့Printfverb တွေ၊ ဘယ်တော့မှ မရောက်နိုင်တဲ့ code…)go test ./...→ Jest / Vitest (built in ဖြစ်ပြီး benchmark နဲ့ fuzzing ပါ ပါဝင်ပါတယ်)GOOS=linux GOARCH=arm64 go build→ command တစ်ခုတည်းနဲ့ တခြား OS/CPU အတွက် cross-compile လုပ်ပါ
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တစ်ကြောင်းချင်း ရှင်းလင်းချက်
- Import path (များသောအားဖြင့် repo ရှိတဲ့နေရာ) နဲ့ နာမည်ပေးထားတဲ့ module တစ်ခုကို စတင်ပါတယ်။
- လက်ရှိ folder ထဲက package ကို build လုပ်ပြီး run ပါတယ်။
- Package တိုင်းထဲက test တိုင်းကို race detector နဲ့ coverage ပါ run ပါတယ်။
- Mac ကနေ Linux binary ကို build လုပ်ပါတယ်: file တစ်ခုတည်းကို server ပေါ် ဒါမှမဟုတ် Docker image သေးသေးလေးထဲ copy ကူးလိုက်ရင် run ပါပြီ။
Test တွေက _test.go file တွေထဲက ရိုးရိုး function တွေပါ။ Playground က test တွေပါပြီး main မပါတဲ့ program ကို go test အဖြစ် run ပေးလို့ ဒါကိုလည်း စမ်းကြည့်လို့ရပါတယ်: Slugify ကို တမင်မှားအောင် ပြင်ပြီး fail ဖြစ်တာကို ကြည့်ပါ။
Tab က indent · ⌘/Ctrl + Enter က run · Esc က editor ထဲက ထွက်
တစ်ကြောင်းချင်း ရှင်းလင်းချက်
- Standard
testingpackage: install လုပ်ရမယ့် test runner မလိုပါဘူး။ (တကယ့် project မှာဆိုရင် ဒါကslug_test.goထဲကpackage journalဖြစ်ပါလိမ့်မယ်။) - Test လုပ်မယ့် code: lowercase ပြောင်း၊ whitespace နဲ့ ခွဲ၊ dash တွေနဲ့ ပြန်ဆက်။
func TestXxx(t *testing.T)ပုံစံ ဘာမဆို test တစ်ခုပါ။tက failure တွေကို report လုပ်ပါတယ်:describe/it/expectDSL မရှိပါဘူး။- Table-driven test များ: input တွေနဲ့ မျှော်လင့်ထားတဲ့ output တွေရဲ့ slice တစ်ခု၊ Go မှာ အသုံးအများဆုံး testing pattern ပါ။
- ရိုးရိုး
!=နဲ့ နှိုင်းယှဉ်ပါ။ Go က assertion library တွေကို အားမပေးလို့ failure message ကို ကိုယ်တိုင် ရေးရပါတယ်။ 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 တစ်ခုတည်းဆီ
- Package တိုင်းကို parse လုပ်ပြီး type-check လုပ်ပါတယ်။ Go ရဲ့ grammar ကို မြန်မြန် parse လုပ်နိုင်အောင် ဒီဇိုင်းထုတ်ထားပြီး မသုံးတဲ့ import ဒါမှမဟုတ် variable ဆိုတာ error ဖြစ်တာကြောင့် build တွေ ပေါ့ပါးနေပါတယ်။
- SSA (static single assignment) အဖြစ် ပြောင်းပြီး optimise လုပ်ပါတယ်: inlining၊ escape analysis၊ dead-code ဖယ်ရှားခြင်း၊ bounds-check ဖယ်ရှားခြင်း။
- Target OS နဲ့ CPU (
GOOS/GOARCH) အတွက် machine code ထုတ်ပေးပြီး၊ Go runtime အပါအဝင် အားလုံးကို statically linked executable တစ်ခုတည်းအဖြစ် link လုပ်ပါတယ်။ - 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၊pprofprofiling အားလုံး 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
- ပထမ အပတ်: language။ A Tour of Go ကို လေ့လာပြီး အပေါ်က ဥပမာတိုင်းကို ကိုယ်ပိုင် အပြောင်းအလဲတွေနဲ့ ပြန် run ကြည့်ပါ။
- ဒုတိယ အပတ်: idiom တွေ။ Effective Go ကို ဖတ်ပြီး Go by Example ကို လှန်လှောကြည့်ပါ။ Error၊ interface နဲ့ table-driven test တွေကို လေ့ကျင့်ပါ။
- တတိယ အပတ်: တကယ့် service တစ်ခု။
net/http၊ database (database/sql+ pgx)၊ configuration နဲ့contextသုံးတဲ့ graceful shutdown တို့ပါတဲ့ JSON API တစ်ခု တည်ဆောက်ပါ။ - စတုတ္ထ အပတ်: concurrency ကို လက်တွေ့ အသုံးချ။ Worker pool၊
contextcancellation၊errgroup၊ rate limiting၊ ပြီးတော့ အဲဒါအားလုံးပေါ်မှာgo test -race။ - ပြီးရင် သင့် stack နဲ့ ချိတ်ပါ: သင့် Go API ပေါ်မှာ Next.js ဒါမှမဟုတ် React front end တစ်ခု တင်ပြီး contract ကို OpenAPI ဒါမှမဟုတ် Connect နဲ့ share ပါ။
pprofနဲ့ profile လုပ်၊ 10 MB container တစ်ခု ship လုပ်ပြီး ညဘက် pager မမြည်တဲ့ ငြိမ်သက်မှုကို ခံစားလိုက်ပါ။
Go က သိသာထင်ရှားတဲ့ အရာကို ရေးတာကို ဆုလာဘ်ပေးပါတယ်။ TypeScript ကနေ လာတဲ့သူအနေနဲ့ type လှည့်ကွက်အချို့ကို ခဏတော့ လွမ်းနေပါလိမ့်မယ်၊ ပြီးမှ အဲဒါတွေကို ဘယ်လောက် ရှားရှားပါးပါးပဲ လိုခဲ့လဲဆိုတာ သတိထားမိလာပါလိမ့်မယ်။

