Go Concurrency Patterns: Goroutines, Channels, and Best Practices

Go’s concurrency model is one of its strongest features. Here’s how to effectively use goroutines, channels, and concurrency patterns. Goroutines Basic Usage // Simple goroutine go func() { fmt.Println("Running in goroutine") }() // With function go processData(data) WaitGroup import "sync" var wg sync.WaitGroup func main() { for i := 0; i < 10; i++ { wg.Add(1) go func(id int) { defer wg.Done() processItem(id) }(i) } wg.Wait() // Wait for all goroutines } Channels Basic Channel Operations // Unbuffered channel ch := make(chan int) // Send ch <- 42 // Receive value := <-ch // Close close(ch) Buffered Channels // Buffered channel (capacity 10) ch := make(chan int, 10) // Non-blocking send if buffer not full ch <- 1 ch <- 2 Channel Directions // Send-only channel func sendOnly(ch chan<- int) { ch <- 42 } // Receive-only channel func receiveOnly(ch <-chan int) { value := <-ch } // Bidirectional (default) func bidirectional(ch chan int) { ch <- 42 value := <-ch } Common Patterns Worker Pool func workerPool(jobs <-chan int, results chan<- int, numWorkers int) { var wg sync.WaitGroup for i := 0; i < numWorkers; i++ { wg.Add(1) go func() { defer wg.Done() for job := range jobs { result := processJob(job) results <- result } }() } wg.Wait() close(results) } Fan-Out, Fan-In // Fan-out: Distribute work func fanOut(input <-chan int, outputs []chan int) { for value := range input { for _, output := range outputs { output <- value } } for _, output := range outputs { close(output) } } // Fan-in: Combine results func fanIn(inputs []<-chan int) <-chan int { output := make(chan int) var wg sync.WaitGroup for _, input := range inputs { wg.Add(1) go func(ch <-chan int) { defer wg.Done() for value := range ch { output <- value } }(input) } go func() { wg.Wait() close(output) }() return output } Pipeline Pattern func pipeline(input <-chan int) <-chan int { stage1 := make(chan int) stage2 := make(chan int) // Stage 1 go func() { defer close(stage1) for value := range input { stage1 <- value * 2 } }() // Stage 2 go func() { defer close(stage2) for value := range stage1 { stage2 <- value + 1 } }() return stage2 } Context for Cancellation Using Context import "context" func processWithContext(ctx context.Context, data []int) error { for _, item := range data { select { case <-ctx.Done(): return ctx.Err() default: processItem(item) } } return nil } // Usage ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() err := processWithContext(ctx, data) Context with Timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() result, err := fetchData(ctx) Select Statement Non-Blocking Operations select { case value := <-ch1: // Handle value from ch1 case value := <-ch2: // Handle value from ch2 case ch3 <- 42: // Successfully sent to ch3 default: // No channel ready } Timeout Pattern select { case result := <-ch: return result case <-time.After(5 * time.Second): return errors.New("timeout") } Mutex and RWMutex Protecting Shared State type SafeCounter struct { mu sync.Mutex value int } func (c *SafeCounter) Increment() { c.mu.Lock() defer c.mu.Unlock() c.value++ } func (c *SafeCounter) Value() int { c.mu.Lock() defer c.mu.Unlock() return c.value } Read-Write Mutex type SafeMap struct { mu sync.RWMutex data map[string]int } func (m *SafeMap) Get(key string) int { m.mu.RLock() defer m.mu.RUnlock() return m.data[key] } func (m *SafeMap) Set(key string, value int) { m.mu.Lock() defer m.mu.Unlock() m.data[key] = value } Best Practices 1. Always Use defer for Cleanup // Good func process() { mu.Lock() defer mu.Unlock() // Process } // Bad func process() { mu.Lock() // Process mu.Unlock() // Might not execute if panic } 2. Avoid Goroutine Leaks // Good: Use context for cancellation func worker(ctx context.Context) { for { select { case <-ctx.Done(): return default: // Work } } } // Bad: Goroutine runs forever func worker() { for { // Work forever } } 3. Close Channels Properly // Good: Close channel when done func producer(ch chan int) { defer close(ch) for i := 0; i < 10; i++ { ch <- i } } Common Pitfalls 1. Race Conditions // Bad: Race condition var counter int func increment() { counter++ // Not thread-safe } // Good: Use mutex var ( counter int mu sync.Mutex ) func increment() { mu.Lock() defer mu.Unlock() counter++ } 2. Closing Closed Channel // Bad: Panic if channel already closed close(ch) close(ch) // Panic! // Good: Check or use sync.Once var once sync.Once once.Do(func() { close(ch) }) Performance Tips 1. Use Buffered Channels // For known capacity ch := make(chan int, 100) 2. Limit Goroutine Count // Use worker pool instead of unlimited goroutines const maxWorkers = 10 semaphore := make(chan struct{}, maxWorkers) 3. Profile Your Code import _ "net/http/pprof" // Add to main go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) }() Conclusion Go’s concurrency model provides: ...

December 10, 2025 · 4770 views

Rust for JavaScript Developers: A Practical Guide

Coming from JavaScript to Rust? Here’s a practical guide to help you make the transition. Why Rust? Rust offers: Memory safety without garbage collection Performance comparable to C/C++ Modern tooling and package management WebAssembly support for web development Growing ecosystem with active community Key Differences 1. Ownership System Rust’s ownership system is unique: // Rust: Ownership let s1 = String::from("hello"); let s2 = s1; // s1 is moved to s2 // println!("{}", s1); // Error: s1 is no longer valid // JavaScript: Reference let s1 = "hello"; let s2 = s1; // s1 is still valid console.log(s1); // Works fine 2. Mutability Rust requires explicit mutability: ...

December 10, 2025 · 4374 views

TypeScript Best Practices for 2025: Write Better Type-Safe Code

TypeScript has become the de facto standard for large-scale JavaScript applications. Here are the best practices to write better type-safe code in 2025. 1. Use Strict Mode Always enable strict mode in tsconfig.json: { "compilerOptions": { "strict": true, "noImplicitAny": true, "strictNullChecks": true, "strictFunctionTypes": true, "strictBindCallApply": true, "strictPropertyInitialization": true, "noImplicitThis": true, "alwaysStrict": true } } 2. Prefer Interfaces for Object Shapes Use interfaces for object shapes, types for unions/intersections: // Good: Interface for object shape interface User { id: string; name: string; email: string; } // Good: Type for union type Status = 'pending' | 'approved' | 'rejected'; // Good: Type for intersection type AdminUser = User & { role: 'admin' }; 3. Use Discriminated Unions Make type narrowing easier with discriminated unions: ...

December 10, 2025 · 3689 views

Will WebAssembly Kill JavaScript? Let's Find Out

The question “Will WebAssembly kill JavaScript?” has been circulating in the developer community for years. Let’s explore this topic with a practical perspective. What is WebAssembly? WebAssembly (WASM) is a binary instruction format for a stack-based virtual machine. It’s designed as a portable compilation target for high-level languages like C, C++, Rust, and Go, enabling deployment on the web for client and server applications. JavaScript’s Strengths JavaScript has several advantages that make it unlikely to be completely replaced: ...

December 9, 2025 · 4533 views

Java Class Initialization Order: Understanding the Real Sequence

Understanding Java class initialization order is crucial for avoiding subtle bugs. Here’s the real initialization sequence and common pitfalls. Initialization Order Static Fields and Blocks public class Example { // 1. Static fields initialized first private static int staticField = initializeStatic(); // 2. Static blocks executed in order static { System.out.println("Static block 1"); } static { System.out.println("Static block 2"); } private static int initializeStatic() { System.out.println("Static field initialization"); return 1; } } Instance Fields and Constructors public class Example { // 3. Instance fields initialized private int instanceField = initializeInstance(); // 4. Instance initialization blocks { System.out.println("Instance block"); } // 5. Constructor executed last public Example() { System.out.println("Constructor"); } private int initializeInstance() { System.out.println("Instance field initialization"); return 1; } } Complete Initialization Sequence public class InitializationDemo { // Step 1: Static fields private static String staticField = "Static field"; // Step 2: Static blocks (in order) static { System.out.println("Static block 1"); } static { System.out.println("Static block 2"); } // Step 3: Instance fields private String instanceField = "Instance field"; // Step 4: Instance blocks { System.out.println("Instance block"); } // Step 5: Constructor public InitializationDemo() { System.out.println("Constructor"); } } Common Pitfalls 1. Forward References // Bad: Forward reference public class BadExample { private int value = getValue(); // Error: forward reference private int multiplier = 10; private int getValue() { return multiplier * 2; // multiplier might not be initialized } } // Good: Initialize in correct order public class GoodExample { private int multiplier = 10; private int value = getValue(); private int getValue() { return multiplier * 2; } } 2. Static vs Instance Initialization public class ConfusingExample { private static int staticCounter = 0; private int instanceCounter = 0; static { staticCounter++; // Cannot access instanceCounter here! } { instanceCounter++; staticCounter++; // Can access static } } 3. Inheritance Initialization class Parent { static { System.out.println("Parent static block"); } { System.out.println("Parent instance block"); } public Parent() { System.out.println("Parent constructor"); } } class Child extends Parent { static { System.out.println("Child static block"); } { System.out.println("Child instance block"); } public Child() { System.out.println("Child constructor"); } } // Output order: // Parent static block // Child static block // Parent instance block // Parent constructor // Child instance block // Child constructor Best Practices Initialize fields in logical order Avoid forward references Keep initialization blocks simple Use constructors for complex initialization Document initialization dependencies Conclusion Understanding initialization order helps you: ...

June 15, 2024 · 4708 views

Java Collections Framework: Complete Guide with Examples

The Java Collections Framework is essential for every Java developer. Here’s a comprehensive guide. Collection Hierarchy Collection ├── List │ ├── ArrayList │ ├── LinkedList │ └── Vector ├── Set │ ├── HashSet │ ├── LinkedHashSet │ └── TreeSet └── Queue ├── PriorityQueue └── Deque └── ArrayDeque Lists ArrayList List<String> list = new ArrayList<>(); list.add("Java"); list.add("Python"); list.get(0); // "Java" list.remove(0); LinkedList List<String> list = new LinkedList<>(); list.add("First"); list.add("Last"); // Better for frequent insertions/deletions Sets HashSet Set<String> set = new HashSet<>(); set.add("Java"); set.add("Java"); // Duplicate ignored set.contains("Java"); // true TreeSet Set<String> set = new TreeSet<>(); set.add("Zebra"); set.add("Apple"); // Automatically sorted: [Apple, Zebra] Maps Map<String, Integer> map = new HashMap<>(); map.put("Java", 1); map.put("Python", 2); map.get("Java"); // 1 map.containsKey("Java"); // true Best Practices Use ArrayList for random access Use LinkedList for frequent insertions Use HashSet for unique elements Use TreeSet for sorted unique elements Use HashMap for key-value pairs Conclusion Master Java Collections to write efficient code! 📚

December 15, 2023 · 4160 views

Java Evolution: From Java 8 to Java 25 - Complete Guide

Java has evolved significantly from Java 8 to Java 25. Here’s a comprehensive guide to the major changes. Java 8 (2014) Lambda Expressions List<String> names = Arrays.asList("Java", "Python"); names.forEach(name -> System.out.println(name)); Streams API List<String> filtered = names.stream() .filter(name -> name.startsWith("J")) .collect(Collectors.toList()); Optional Optional<String> name = Optional.of("Java"); name.ifPresent(System.out::println); Java 9 (2017) Modules module com.example { requires java.base; exports com.example.api; } Private Methods in Interfaces interface MyInterface { default void method() { helper(); } private void helper() { // Private method } } Java 11 (2018) String Methods String text = " Java "; text.strip(); // "Java" text.repeat(3); // " Java Java Java " HTTP Client HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.example.com")) .build(); Java 17 (2021) Sealed Classes public sealed class Shape permits Circle, Rectangle { } public final class Circle extends Shape { } Pattern Matching if (obj instanceof String s) { System.out.println(s.length()); } Java 21 (2023) Virtual Threads Thread.ofVirtual().start(() -> { // Lightweight thread }); Pattern Matching for Switch String result = switch (value) { case Integer i -> "Integer: " + i; case String s -> "String: " + s; default -> "Unknown"; }; Java 25 (2025) Latest Features Enhanced pattern matching Improved performance Better memory management New API additions Migration Guide Update dependencies Test thoroughly Use new features gradually Monitor performance Update tooling Conclusion Java continues to evolve with: ...

September 10, 2023 · 3596 views

Java Isn't Verbose - You Are: Writing Concise Java Code

Java is often criticized for verbosity, but modern Java can be concise. Here’s how to write cleaner code. Modern Java Features Records (Java 14+) // Old way public class User { private String name; private String email; public User(String name, String email) { this.name = name; this.email = email; } // Getters, equals, hashCode, toString... } // New way public record User(String name, String email) { } Pattern Matching (Java 17+) // Old way if (obj instanceof String) { String s = (String) obj; System.out.println(s.length()); } // New way if (obj instanceof String s) { System.out.println(s.length()); } Switch Expressions (Java 14+) // Old way String result; switch (day) { case MONDAY: case FRIDAY: result = "Weekday"; break; default: result = "Weekend"; } // New way String result = switch (day) { case MONDAY, FRIDAY -> "Weekday"; default -> "Weekend"; }; Best Practices Use Streams // Old way List<String> filtered = new ArrayList<>(); for (String name : names) { if (name.startsWith("J")) { filtered.add(name.toUpperCase()); } } // New way List<String> filtered = names.stream() .filter(name -> name.startsWith("J")) .map(String::toUpperCase) .toList(); Use Optional // Old way String result = null; if (user != null && user.getName() != null) { result = user.getName().toUpperCase(); } // New way String result = Optional.ofNullable(user) .map(User::getName) .map(String::toUpperCase) .orElse(null); Conclusion Modern Java is concise when you: ...

July 20, 2023 · 4015 views

JavaScript Closures Explained: Understanding Scope and Memory

Closures are fundamental to JavaScript. Here’s how they work and when to use them. What is a Closure? A closure gives you access to an outer function’s scope from an inner function. function outer() { const name = 'JavaScript'; function inner() { console.log(name); // Accesses outer scope } return inner; } const innerFunc = outer(); innerFunc(); // "JavaScript" Common Patterns Module Pattern const counter = (function() { let count = 0; return { increment: () => ++count, decrement: () => --count, getCount: () => count }; })(); Function Factories function createMultiplier(multiplier) { return function(number) { return number * multiplier; }; } const double = createMultiplier(2); double(5); // 10 Common Pitfalls Loop with Closures // Bad: All log same value for (var i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); // 3, 3, 3 } // Good: Use let or IIFE for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 100); // 0, 1, 2 } Best Practices Understand scope chain Avoid memory leaks Use closures for encapsulation Be careful in loops Use modern alternatives when possible Conclusion Master closures to write better JavaScript! 🔒

March 15, 2023 · 4617 views