5 Điều Mà Senior Go Luôn Nghĩ Tới
Có bao giờ bạn dừng lại và nghĩ về các lập trình viên Golang đầy kinh nghiệm nghĩ gì khi họ đã master hết các kỹ thuật? Họ xem xét những gì khi viết code? Những bài học mà họ rút ra được khi trải nghiệm với đủ thể loại code writing trong team?
Trong bài viết này, mình sẽ "peek" vào những vấn đề mà các Senior Golang phải suy nghĩ.
Claims
Bài viết được dịch từ: 5 Things Senior Go Programmers are Always Thinking About — Are You?
1. Convention (Quy ước)
Việc một team tạo và cùng nhau follow conventions của cả team như phong cách code (code style), cách đặt tên, format,... rất quan trọng bởi nó quyết định tới tính nhất quán cũng như sự chuyên nghiệp của một team. Đồng thời convention cũng là một trong những nhân tố chính trong nghiệp maintain codebase.
Một vài conventions mà Go team thường xem xét bao gồm:
- Quy ước về phong cách code (coding style).
- Cách đặt tên trong Go.
- Quy ước về format.
- Quy ước cách viết tài liệu kỹ thuật (technical document).
- Quy vệc về việc viết test và debug.
Mình thường sử dụng Uber Go Style Guide như là codebase ban đầu cho team, nhưng ban đầu bạn có thể bị ngợp bởi số lượng best practice ở đây.
Tuy nhiên lưu ý rằng, convention không phải là tất cả, đừng quá vì convention mà bỏ qua tính linh hoạt, đôi khi phá vỡ convention lại là một cách đặt được kết quả nhanh và tốt nhất so với việc cứng nhắc follow một rule nào đó.
2. Code sạch, hiệu quả và dễ bảo trì
Ngoài việc phải có một convention, viết code sạch sẽ, hiệu quả và mang tính dễ bảo trì cũng là một trong trong những ưu tiên top đầu của một team lập trình.
Khi codebase ngày càng lớn, mọi thứ trở nên phức tạp và phức tạp hơn, điều quan trọng ở đây là bạn và team phải chắc chắn rằng code được viết một cách có cấu trúc và dễ maintain, đừng làm gì khác thường, bất ngờ cho dev khác bởi nó sẽ gây khó hiểu và giảm quality của cả một codebase.
Dưới đây là một vài best practices để làm cho codebase của chúng ta gọn gàng, cấu trúc tốt hơn, dễ dàng scale:
- Follow coding style của team (và convention).
- Giữ interfaces nhỏ gọn và súc tích, đừng thêm quá nhiều hàm vào interface mà không liên quan tới mục đích của nó.
- Xử lý lỗi một cách hợp lý, không ignore lỗi, ít nhất bạn nên thêm một cái logs nếu không muốn xử lý.
- Tránh naked returns, thay vì return (bool, int, string, error), hãy return một (struct, err) và chỉ rõ từng biến trả về mang ý nghĩa gì.
- Viết comments cho các vùng code phức tạp, tránh comments những gì mà chúng ta nhìn vào có thể hiểu ngay, chỉ comment những thứ mà code không nói.
- Review code thường xuyên, xoá các code không sử dụng, tránh gây nhầm lẫn cho các thành viên khác trong team.
- Thiết kế càng cơ bản, càng đơn giản càng tốt, đừng quá chú trọng vào tính trừu tượng mà làm feature phức tạp gấp nhiều lần so với thực tế.
- Benchmark bằng inputs thật sự, các inputs mang tính lộn xộn, hỗn tạp càng tốt để phát hiện các cases chưa cover.
- Log các events quan trọng.
Bằng cách nào đó, việc xoá code không sử dụng đi là một trong những cách quan trọng mà chúng ta có thể bắt đầu làm ngay bây giờ để làm sạch codebase.
3. Các kỹ thuật để tối ưu hiệu suất
Về tổng thể, tối ưu Go về hiệu suất cực kỳ quan trọng bởi chúng ta phải đảm bảo nó chạy mượt, hiệu quả và có khả năng scale ngay cả khi cao điểm.
Dưới đây là một vài kỹ thuật optimize code cơ bản mà mình đã giới thiệu trước đó, bạn có thể xem xét một số options sau:
- Luôn profile code để tìm ra điểm nghẽn, đừng bao giờ optimize trước khi profile, nếu không thì hãy cẩn thận vì có tểh bạn đang lãng phí thời gian vào 1% performance đấy.
- Sử dụng memory một cách hiệu quả, reuse là một trong những cách để tránh cấp phát lại.
- Benchmark các điểm nghẽn bằng data thật từ users.
- Tập trung tìm các hàm được gọi nhiều nhất, phức tạp nhất, xử lý hết nợ technical, //TODO, //...
- Cẩn thận với goroutine leaks, khi khởi tạo một goroutine, bạn nên biết khi nào nó sẽ kết thúc.
- Sử dụng xử lý song song hiệu quả, không phải khi nào sử dụng nhiều goroutine cũng tốt hơn.
4. Sử dụng xử lý song song (goroutine)
Go là ngôn ngữ được thiết kế mạnh về xử lý song song, hỗ trợ đầy đủ cũng như dễ dàng sử dụng cho developers. Nhưng bạn bắt đầu từ đâu?
- Xác định các phần có thể chạy song song mà không cần chờ nhau, xử lý song song chúng.
- Sử dụng goroutine pattern như worker, fan-in, fan-out, pipeline,...
- Sử dụng sync package để cải thiện readable của phần code xử lý song song.
- Sử dụng các hàm atomic để đảm bảo xử lý đúng các resources dùng chung giữa nhiều goroutines.
- Mutex lock là một trong những cách được sử dụng nhiều nhất, nhưng chúng chậm hơn atomics.
- Profile, profile và profile.
5. Làm việc hiệu quả với hệ thống phân tán
Làm việc với hệ thống phân tán (distributed system) là một trong những chìa khoá để tăng tính bảo trì, scale và độ tin cậy của hệ thống. Đặc biệt là các hệ thống cần hoạt động liên tục 24/7.
Distributed System có thể mở rộng theo chiều ngang (horizontally scale) bằng cách tăng thêm nốt, pods thay vì chỉ giới hạn vào CPU/ Memory của một máy tính cấu hình mạnh.
Dưới đây cũng là một vài tips để làm vệc với distributed system:
- Thiết kế để failed: nghe thì có vẻ kì lạ nhưng không có hệ thống nào có thể hoàn toàn tránh được việc sự cố xảy ra, điều quan trọng ở đây là khi có sự cố, hệ thống chúng ta phản ứng như thế nào.
- Sử dụng đúng data storage: các distributed system thường chỉ dựa vào data storage để hoạt động, bởi chúng không hề lưu bất kỳ trạng thái nào (stateless), vì vậy data storage là một trong những điểm cơ bản và quan trọng nhất trong hệ thống.
- Liên tục theo dõi và log hệ thống: bạn có thể phát hiện và fix bất cứ issues gì trước khi nó trở thành thảm hoạ.
Mình recommend các bạn đọc thêm về The Twelve-Factor app methodology để học những nguyên tắc đã được test và hoạt động hiệu quả với nhiều developers khác.
All rights reserved