Hành trình từ .NET sang Go: Tại sao tôi chuyển stack?
🎯 Bối cảnh chuyển đổi
Sau 1 năm làm việc với C# và .NET Framework trong các dự án học tập, tôi quyết định học Golang vào tháng 9/2024. Không phải vì .NET không tốt, mà vì tôi muốn trải nghiệm một paradigm hoàn toàn khác biệt.
📊 So sánh đầu tiên: Hello World Performance
C# (.NET 8)
// Program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World");
app.Run();
Go (Gin Framework)
// main.go
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "Hello World"})
})
r.Run(":8080")
}
Benchmark với Apache Bench (10,000 requests, concurrency 100):
| Metric | .NET 8 | Go (Gin) | |--------|--------|----------| | Requests/sec | 8,943 | 12,456 | | Time per request | 11.2ms | 8.0ms | | Memory usage | 45MB | 12MB |
🤯 Những "culture shock" khi chuyển sang Go
1. Error Handling: Từ Try-Catch sang if err != nil
C# style:
try {
var data = await _repo.GetUserById(id);
return Ok(data);
} catch (Exception ex) {
_logger.LogError(ex, "Error fetching user");
return StatusCode(500, "Internal Server Error");
}
Go style:
data, err := repo.GetUserById(id)
if err != nil {
log.Printf("Error fetching user: %v", err)
c.JSON(500, gin.H{"error": "Internal Server Error"})
return
}
c.JSON(200, data)
Suy nghĩ ban đầu: "Sao phải check error mọi lúc thế này? Mệt quá!"
Sau 2 tháng: "Ồ, cách này giúp tôi handle edge cases tốt hơn nhiều!"
2. Không có Class, chỉ có Struct + Methods
C# OOP:
public class UserService {
private readonly IUserRepository _repo;
public UserService(IUserRepository repo) {
_repo = repo;
}
public async Task<User> GetUser(int id) {
return await _repo.FindById(id);
}
}
Go composition:
type UserService struct {
repo UserRepository
}
func NewUserService(repo UserRepository) *UserService {
return &UserService{repo: repo}
}
func (s *UserService) GetUser(id int) (*User, error) {
return s.repo.FindById(id)
}
3. Goroutines vs Tasks/Async-Await
C#:
var tasks = users.Select(async user => {
return await ProcessUserAsync(user);
});
var results = await Task.WhenAll(tasks);
Go:
var wg sync.WaitGroup
results := make(chan Result, len(users))
for _, user := range users {
wg.Add(1)
go func(u User) {
defer wg.Done()
results <- ProcessUser(u)
}(user)
}
wg.Wait()
close(results)
💡 Khi nào nên dùng Go vs .NET?
Chọn Go khi:
- Xây dựng microservices, API servers
- Cần performance cao, resource usage thấp
- Làm việc với concurrent programming nhiều
- DevOps tools, CLI applications
Chọn .NET khi:
- Enterprise applications với business logic phức tạp
- Team đã quen thuộc với C# ecosystem
- Cần Entity Framework, LINQ, và các abstractions cao cấp
- Windows-first environment
🎓 Bài học rút ra
- Simplicity is not easy: Go có ít features hơn, nhưng viết code đơn giản đúng cách lại khó
- Performance matters: Trong môi trường production, 40% memory savings thực sự quan trọng
- Learning curve: Go dễ học hơn C# rất nhiều (1 tuần vs 3 tháng để "fluent")
🚀 Kết luận
Tôi không hối hận khi học Go. Nhưng tôi cũng không từ bỏ .NET. Mỗi công cụ có use case riêng. Quan trọng là hiểu khi nào dùng cái gì.
Lời khuyên cho bạn đọc: Đừng theo trend mù quáng. Hãy thử cả hai và tự đánh giá phù hợp với mình nhất.
