A

Go (Golang)

go golang programming-languages backend systems-programming concurrency performance

Go (Golang)

Go (often called Golang) is a statically typed, compiled programming language designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson. Released in 2009, Go was created to address shortcomings in other languages used at Google for building large-scale, high-performance systems. For JavaScript and TypeScript developers, Go offers a compelling option for backend services with a focus on simplicity and performance.

What is Go?

Go is designed for simplicity, reliability, and efficiency. It combines:

  • Fast compilation like dynamic languages
  • Execution speed like C/C++
  • Easy concurrency with goroutines
  • Simple syntax with minimal features
  • Built-in tooling for formatting, testing, and dependencies
package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

Go for JavaScript/TypeScript Developers

What You'll Recognize

If you know JavaScript/TypeScript, some concepts will feel familiar:

  • Functions as first-class citizens
  • Closures and anonymous functions
  • Garbage collection (no manual memory management)
  • JSON handling is straightforward
  • Package-based modules
// Functions as values (like JavaScript)
add := func(a, b int) int {
    return a + b
}

result := add(5, 3)  // 8

What's Different

Statically Typed (Compile-time)

Unlike JavaScript, Go is statically typed:

var name string = "Alice"
var age int = 25

// Type inference (like TypeScript's let)
name := "Alice"  // Compiler infers 'string'
age := 25        // Compiler infers 'int'

No Classes, Use Structs

Go doesn't have classes. Instead, it uses structs with methods:

type User struct {
    Name  string
    Email string
}

func (u User) Greet() string {
    return "Hello, " + u.Name
}

user := User{Name: "Alice", Email: "[email protected]"}
fmt.Println(user.Greet())  // Hello, Alice

Error Handling: No Try/Catch

Go uses explicit error returns instead of exceptions:

// JavaScript/TypeScript
try {
  const data = await fetchUser(id);
} catch (error) {
  console.error(error);
}

// Go
data, err := fetchUser(id)
if err != nil {
    log.Printf("Error: %v", err)
    return
}
// Use data...

Pointers (But Simple)

Go has pointers, but they're simpler than C/C++:

func updateName(u *User, newName string) {
    u.Name = newName  // Automatically dereferenced
}

user := User{Name: "Alice"}
updateName(&user, "Bob")  // Pass pointer with &
fmt.Println(user.Name)    // Bob

Why Go?

1. Speed

Go compiles to native machine code and is significantly faster than Node.js:

  • Compilation: Seconds for large projects
  • Execution: 10-100x faster than JavaScript for CPU-intensive tasks
  • Startup time: Instant (no runtime to load)
// Processing millions of records
for _, record := range records {  // Much faster than JS for loops
    processRecord(record)
}

2. Concurrency

Go's killer feature is goroutines - lightweight threads that make concurrent programming easy:

// Start concurrent operations (like async/await but easier)
go fetchUser(id1)      // Runs concurrently
go fetchUser(id2)      // Runs concurrently
go fetchUser(id3)      // Runs concurrently

// With channels for communication
results := make(chan User)

go func() {
    user, _ := fetchUser(id)
    results <- user  // Send to channel
}()

user := <-results  // Receive from channel

Comparison to JavaScript:

// JavaScript - async/await
const [user1, user2, user3] = await Promise.all([
  fetchUser(id1),
  fetchUser(id2),
  fetchUser(id3),
]);

// Go - goroutines
ch := make(chan User, 3)
for _, id := range []int{id1, id2, id3} {
    go func(id int) {
        user, _ := fetchUser(id)
        ch <- user
    }(id)
}

users := []User{<-ch, <-ch, <-ch}

Goroutines are cheaper than JavaScript Promises for thousands of concurrent operations.

3. Simple Deployment

Go compiles to a single binary with no dependencies:

# Build
go build -o myapp

# Deploy (just copy the binary)
./myapp

No need for:

  • node_modules/ (no dependencies to ship)
  • Runtime installation (Node.js)
  • Version managers (nvm)

This makes Docker images tiny and deployment simple.

4. Built-in Tooling

Go comes with excellent tools out of the box:

go fmt        # Format code (like Prettier, but standard)
go test       # Run tests (built-in testing)
go mod init   # Initialize module (like npm init)
go mod tidy   # Clean up dependencies
go build      # Compile binary
go run        # Compile and run

5. Standard Library

Go's standard library is comprehensive. You can build HTTP servers without external dependencies:

package main

import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, World!")
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

Compare to Express.js:

const express = require('express');  // External dependency
const app = express();

app.get('/', (req, res) => {
  res.send('Hello, World!');
});

app.listen(8080);

Where Go Excels

Backend Services & APIs

Go is popular for building REST APIs and microservices:

// Using Gin (like Express for Go)
package main

import "github.com/gin-gonic/gin"

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

func main() {
    r := gin.Default()

    r.GET("/users/:id", func(c *gin.Context) {
        id := c.Param("id")
        user := User{ID: 1, Name: "Alice", Email: "[email protected]"}
        c.JSON(200, user)
    })

    r.Run(":8080")
}

DevOps Tools & CLI

Many popular tools are written in Go:

  • Docker: Container platform
  • Kubernetes: Container orchestration
  • Terraform: Infrastructure as code
  • GitHub CLI: gh command
  • Hugo: Static site generator

Go is excellent for CLI tools because of single binary distribution.

Cloud Services

Major cloud providers use Go extensively:

  • Google: Internal services, Cloud Platform
  • Uber: Microservices platform
  • Dropbox: Performance-critical services
  • Cloudflare: Edge services
  • Twitch: Chat servers (handles millions of connections)

Real-time Systems

Go excels at handling many concurrent connections:

// WebSocket server handling thousands of connections
func handleWebSocket(conn *websocket.Conn) {
    for {
        var msg Message
        err := conn.ReadJSON(&msg)
        if err != nil {
            break
        }
        // Process message
        broadcastToAll(msg)
    }
}

Go vs JavaScript/TypeScript

Aspect JavaScript/Node.js TypeScript Go
Type System Dynamic Static (compile-time) Static (compile-time)
Performance Slow Slow (same as JS) Fast (compiled)
Concurrency Event loop, async/await Same as JS Goroutines, channels
Compilation None (interpreted) To JavaScript To native binary
Binary Size N/A (needs runtime) N/A ~5-10 MB
Startup Time Slow Slow Instant
Memory High High Lower
Learning Curve Easy Moderate Moderate
Ecosystem Huge Huge Large (growing)
Frontend Yes (browsers) Yes No
Backend Yes (Node.js) Yes Yes (native)

Common Use Cases for JS vs Go

Use Node.js/TypeScript when:

  • Building full-stack apps (same language frontend/backend)
  • Rapid prototyping
  • Working with JSON/APIs heavily
  • Team already knows JavaScript
  • Using server-side rendering (React, Next.js)
  • Real-time apps (Socket.io)

Use Go when:

  • Performance is critical
  • Building microservices
  • Handling high concurrency (thousands of connections)
  • Creating CLI tools
  • Working with systems programming
  • Need efficient resource usage (cloud costs)
  • Simple deployment requirements

Learning Go from JavaScript

Syntax Comparison

Variables

// JavaScript
const name = "Alice";
let age = 25;

// Go
const name = "Alice"  // or: name := "Alice"
var age = 25          // or: age := 25

Functions

// JavaScript
function add(a, b) {
  return a + b;
}

// Go
func add(a int, b int) int {
    return a + b
}

Arrays/Slices

// JavaScript
const numbers = [1, 2, 3, 4, 5];
numbers.push(6);
const doubled = numbers.map(n => n * 2);

// Go
numbers := []int{1, 2, 3, 4, 5}
numbers = append(numbers, 6)

var doubled []int
for _, n := range numbers {
    doubled = append(doubled, n*2)
}

Objects/Structs

// JavaScript
const user = {
  name: "Alice",
  email: "[email protected]"
};

// Go
type User struct {
    Name  string
    Email string
}

user := User{
    Name:  "Alice",
    Email: "[email protected]",
}

Loops

// JavaScript
for (let i = 0; i < 10; i++) {
  console.log(i);
}

// Go
for i := 0; i < 10; i++ {
    fmt.Println(i)
}

Async/Concurrency

// JavaScript
async function fetchData() {
  const result = await fetch(url);
  const data = await result.json();
  return data;
}

// Go
func fetchData() (Data, error) {
    resp, err := http.Get(url)
    if err != nil {
        return Data{}, err
    }
    defer resp.Body.Close()

    var data Data
    err = json.NewDecoder(resp.Body).Decode(&data)
    return data, err
}

Go Frameworks & Libraries

Web Frameworks

  • Gin: Fast, Express-like framework
  • Echo: High performance, minimalist
  • Fiber: Inspired by Express.js
  • Chi: Lightweight router

ORMs & Databases

  • GORM: Full-featured ORM (like TypeORM)
  • sqlx: Extensions for database/sql
  • pgx: PostgreSQL driver and toolkit

Testing

  • Built-in testing package
  • Testify: Assertions and mocks
  • GoMock: Mocking framework

Utilities

  • Viper: Configuration management
  • Cobra: CLI applications
  • Zap/Logrus: Structured logging

Gotchas for JS Developers

1. Error Handling

You'll write if err != nil a lot:

data, err := fetchUser(id)
if err != nil {
    return nil, err  // Handle error
}
// Use data...

2. No undefined or null (mostly)

Go has zero values instead:

var name string  // "" (empty string)
var age int      // 0
var isActive bool  // false
var ptr *User    // nil (for pointers)

3. Exported vs Unexported

Capitalization matters:

type User struct {
    Name  string  // Exported (public)
    email string  // Unexported (private)
}

4. No Inheritance

Go uses composition over inheritance:

type Writer struct {
    Name string
}

type Blogger struct {
    Writer  // Embedded struct (composition)
    Blog string
}

b := Blogger{
    Writer: Writer{Name: "Alice"},
    Blog:   "myblog.com",
}
fmt.Println(b.Name)  // Can access embedded fields

5. Multiple Return Values

Functions can return multiple values (commonly value + error):

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

result, err := divide(10, 2)
if err != nil {
    log.Fatal(err)
}
fmt.Println(result)  // 5

When to Learn Go

Learn Go if:

  • You want to build high-performance backend services
  • You're interested in DevOps/cloud infrastructure
  • You want to understand systems programming
  • You need better performance than Node.js
  • You're building CLI tools or microservices

Stick with TypeScript if:

  • You're primarily a frontend developer
  • You need full-stack with same language
  • Your team is JavaScript-focused
  • You're building React applications
  • Rapid iteration is more important than performance

Learning Resources

  1. A Tour of Go: go.dev/tour - Interactive browser-based tutorial
  2. Go by Example: gobyexample.com - Annotated example programs
  3. Effective Go: Official guide to idiomatic Go
  4. Learn Go with Tests: TDD approach to learning Go
  5. Go Web Examples: gowebexamples.com - Web development patterns

Real-World Examples

REST API Example

package main

import (
    "encoding/json"
    "net/http"
    "github.com/gorilla/mux"
)

type Article struct {
    ID      string `json:"id"`
    Title   string `json:"title"`
    Content string `json:"content"`
}

var articles []Article

func getArticles(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(articles)
}

func getArticle(w http.ResponseWriter, r *http.Request) {
    params := mux.Vars(r)
    for _, article := range articles {
        if article.ID == params["id"] {
            json.NewEncoder(w).Encode(article)
            return
        }
    }
    http.NotFound(w, r)
}

func main() {
    router := mux.NewRouter()
    router.HandleFunc("/articles", getArticles).Methods("GET")
    router.HandleFunc("/articles/{id}", getArticle).Methods("GET")

    http.ListenAndServe(":8000", router)
}

Concurrent Worker Pool

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Printf("Worker %d processing job %d\n", id, j)
        time.Sleep(time.Second)
        results <- j * 2
    }
}

func main() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)

    // Start 3 workers
    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    // Send jobs
    for j := 1; j <= 9; j++ {
        jobs <- j
    }
    close(jobs)

    // Collect results
    for a := 1; a <= 9; a++ {
        <-results
    }
}

Key Takeaways

  • Go is simple, fast, and great for backend services
  • Excellent built-in concurrency with goroutines
  • Compiles to single binary (easy deployment)
  • Different from JavaScript but not hard to learn
  • Great standard library, less need for external packages
  • Used by major companies for performance-critical services
  • Perfect for microservices, CLI tools, and cloud services
  • TypeScript - Type-safe JavaScript alternative
  • Rust - Systems programming with memory safety
  • React - Go can serve as a backend for React apps
  • JavaScript Frameworks - Frontend frameworks that pair with Go backends

For JavaScript/TypeScript developers, Go is an excellent second language that opens doors to backend development, DevOps, and systems programming. The learning curve is moderate, and the performance gains and simple deployment make it worth the investment.

Last updated: October 16, 2025