CODING LUẬN BÀN – HAY NHỮNG ĐIỀU FRESHER CẦN BIẾT KHI CODE

15/04/2022 79
CODING LUAN BAN
CODEWELL

Bạn là một Coder? Chắc hẳn trong quá trình viết code, bạn đã hình thành một “phong cách” nhất định nào đó, hoặc dựa theo một số quy chuẩn của dự án để code?

Không khó để viết code cho máy hiểu, nhưng để người khác đọc code của bạn mà hiểu được thì không phải đơn giản. Một thống kê đã chỉ ra rằng, trong quá trình thi triển coding của một coder, tỷ lệ giữa thời gian đọc code và viết code là 10:1. Nhìn vào con số này, chắc hẳn bạn đã thấy được code sao cho “clean”, cho dễ đọc, dễ hiểu sẽ giúp ích cho bạn cũng như cả đồng đội của mình nhiều như thế nào.

Dưới đây là những điều đúc rút từ kinh nghiệm bản thân anh Phạm Minh Tuấn (CO-WELL Asia) cũng như những góp nhặt của anh từ các chia sẻ trên mạng nhằm giúp cộng đồng lập trình viên, nhất là các bạn Fresher có thể code nhanh và tốt hơn.

 

Lưạ chọn và tuân thủ theo một quy ước (convention) nhất định

Có khá nhiều chuẩn khác nhau cho mỗi ngôn ngữ, ví dụ Java thì có IBM/Google Convention; PHP thì có Zend/ PSR-0, PSR-1, PSR-2 Standard; JS thì có Eslint…

Bất kể bạn thực hiện dự án nào, bạn cũng nên nắm được các chuẩn này và lựa chọn cho mình một Coding Stardard phù hợp.

Khi tìm hiểu về convention, bạn cũng sẽ gặp một số các quy ước phổ biến như: Hungarian notation; Quy tắc đặt tên theo underscores (snake_case), camelCase, PascalCase; thụt đầu dòng (indent) theo tab hay theo space…

Các Coding Editor (IDE) hiện đại (như PHPStorm, Visual Studio Code, Eclipse…) đều có các plugins hỗ trợ việc check code theo các chuẩn. Các bạn chỉ cần lựa chọn, sử dụng và sẽ dần tạo thành thói quen cho mình.

 

Đặt tên class, hàm, biến có ý nghĩa

“Good code is self-documenting”

Nghĩa là không cần comment người khác nhìn vào cũng biết code mình làm gì.

Chắc chỉ cần nói thế là các bạn đã hiểu. Trước khi tung hổ trảo khai sinh ra một Class, Hàm hay Biến, Thuộc tính nào, bạn nên dành cho chúng một cái tên phù hợp, dễ hiểu và có ý nghĩa.

 

Comment vừa phải

de2bedecbd5b4c8f595e9f5ba68f0b9c869a199f

Chỉ comment ở những chỗ thực sự cần thiết (đầu file, class, đầu hàm, một số logic quan trọng/phức tạp…), không cần comment nhiều vì bản thân code bạn tự nó đã có ý nghĩa rồi.
Một trong những rule được khuyến nghị dành cho viết comment đó là: nội dung của comment cần phải trả lời được câu hỏi Why cho sự tồn tại của đoạn code thay vì trả lời cho câu hỏi What liên quan đến nội dung code đó xử lý gì. Tuy nhiên, theo cá nhân mình nghĩ thì với một số đoạn code dài, logic phức tạp thì vẫn có thể comment mô tả ngắn gọn đoạn code đó xử lý gì thì khi đọc code và điều tra sẽ nhanh hơn, không phải đi sâu đọc kỹ nội dung code.

Ví dụ:
Comment vô nghĩa:

   /** paging limit default is 10 **/

    pageLimit = 10;

Comment OK:

/**

    * Get user shopping history list

    **/

    private function getUserShoppingHistory() {

         // 1. Get user shopping data from DB

         ......

         // 2. Call API to get user data from XYZ system

         ........

         // Merge data of 1, 2

         .......

         return ...

    }

 

Trước đây, có những dự án còn đưa ra những quy định “khó nhằn” như sau:
//Ticket XXXYYY – Nội dung sửa -START

//Ticket XXXYYY – Nội dung sửa –END

Giờ với việc quản lý source trên các công cụ như GIT thì bạn hoàn toàn ko cần những comment kiểu này mà có thể xem lịch sử source là biết rồi. Các IDE như Visual Studio Code có các plugins rất xịn để có thể xem được lịch sử liên quan đến từng dòng source tại vị trí con trỏ đang chỉ.

 

Tách hàm, file common. Tái sử dụng code nhiều nhất có thể

Những code đã có trong hệ thống thường là đã được test rồi, nên trước khi bắt tay vào sáng tác mới một xử lý nào đó, hãy ưu tiên tham khảo và sử dụng lại những xử lý có sẵn để đảm bảo đúng các spec đã có của hệ thống cũng như giảm số lượng bug phát sinh.

Cần hạn chế duplicate code xuống mức thấp nhất có thể . Có một rule rất nổi tiếng liên quan đến việc này là DRY (Don’t Repeat Yourself) hay còn được biết là rule DIE (Duplication Is Evil).

 

Ví dụ:

Có một case khá phổ biến với vấn đề này, là khi ta cần thay đổi logic của một hàm được sử dụng tại nhiều chỗ (common) để phục vụ cho một spec mới với hoặc một giao diện cụ thể.

Phản ứng của anh em Dev thông thường sẽ là clone hàm cũ ra một hàm mới để sử dụng cho phần của mình. Vừa nhanh lại không sợ ảnh hưởng đến các chỗ sử dụng khác. Tuy nhiên điều này sẽ dẫn đến hậu quả là sau này khi phát sinh một thay đổi mới đến tất cả các giao diện liên quan, thì thay vì sửa một chỗ ở hàm common, ta sẽ phải sửa ở hai chỗ khác nhau, hoặc có thể sửa lại bị sót.

Việc clone chỉ nên làm khi có một feedback của khách hàng cần xử lý gấp, không có nhiều thời gian test. Nếu không thì thay vì duplicate code, hãy cố gắng sửa hàm cũ bằng cách thêm một tham số mới hoặc sử dụng một biến/thuộc tính có sẵn để check riêng cho trường hợp spec phát sinh riêng của mình. Như vậy code sẽ gọn hơn và cũng tránh bị ảnh hưởng.

 

Độ dài hàm vừa phải

Về nguyên tắc, mỗi hàm là một đơn vị xử lý, thực hiện một mục đích chứ không phải để làm nhiều việc cùng lúc. Vì vậy, độ dài của hàm sẽ không thể quá dài. Khi nội dung hàm quá dài, việc đọc code để hiểu mục đích xử lý của hàm sẽ gặp khó khăn.

Có khá nhiều tiêu chuẩn khác nhau về độ dài hàm, tuy nhiên cá nhân thì mình nghĩ tối đa 1 hàm chỉ nên từ 30-40 dòng. Nếu dài quá, bạn hãy nghĩ cách để tách nội dung xử lý ra thành các hàm nhỏ để dễ code hơn.

Ngoài ra thì hàm cũng không nên quá nhiều tham số (tối đa là 3), nếu cần nhiều hơn hãy truyền theo kiểu object thay vì các parameter tách biệt.

 

Không viết khai báo, xử lý SQL hay gọi API trong các vòng lặp

Hãy tưởng tượng bạn có vòng lặp với 200 userID khác nhau. Và trong vòng lặp này bạn xử lý lấy hoặc update dữ liệu với mỗi user ID tương ứng, sau đó gọi API để liên kết dữ liệu với bên thứ ba. Kết quả khi chạy thực tế sẽ là 200 câu query cùng với 200 request API sẽ được thực hiện!

Đừng để người dùng phải mất thời gian, chờ đợi trong mỏi mòn và ức chế khi sử dụng sản phẩm của chúng ta.

 

Không dùng nhiều tầng If hoặc nhiều vòng lặp lồng nhau

Việc dùng nhiều tầng khi điều tra source rất khó và cũng ảnh hưởng đến Performance của code. Tối đa chỉ nên 2 tầng. Cần nhiều hơn thì bạn hãy nghĩ phương án khác để xử lý cho phù hợp, ví dụ như tách các xử lý bên trong thành hàm con thì source cũng sẽ dễ nhìn hơn.

 

Check biến trước khi sử dụng

Trước khi sử dụng, hay luôn thực hiện check biến đã được khởi tạo có rỗng hay không. Nhất là khi lấy thuộc tính để sử dụng thì cũng phải check tầng cha của thuộc tính.

Khi viết một hàm, hãy luôn check các tham số truyền vào trước khi sử dụng. Làm sao để việc này thành tiềm thức mỗi khi tạo một hàm mới, bạn sẽ tránh được nhiều bug phát sinh sau này.

function doSomething (paramFirst, paramSecond…) {

if (paramFirst == null ){

return or doSomethingFun;

}

 

if (paramSecond == null ){

return or doSomethingSad;

}

 

//Main processing

}

 

Insert, update, delete dữ liệu nhiều bước thì dùng transaction

Nhớ lại những ngày đầu ở CO-WELL khi đi phỏng vấn các bạn DEV, có rất nhiều câu hỏi liên quan đến transaction này, mặc dù cơ bản nhưng không phải bạn DEV nào cũng biết, sử dụng thành thục và trả lời rành mạch được.

 

Hiểu rõ khi tái sử dụng source code của người khác

Đôi khi ta thường tìm một đoạn code cũ trong dự án, google, copy/paste cả đoạn source, thấy nó chạy thì sướng quá. Tuy nhiên, một số logic nhỏ khác trong xử lý của đoạn code này rất có thể đang confict với spec của phần bạn đang làm, từ đó có thể gây ra các lỗi degrade. Tham khảo và tái sử dụng là một việc rất cần thiết khi code, tuy nhiên cần tỉnh táo.

 

Xử lý LOG

Logging là một skill mà bất kể DEV nào đều phải biết khi tham gia dự án.

Khi bạn build một source code cho dự án lớn hay nhỏ nào thì cũng nên chú ý tới việc build cơ chế xuất Log chung của dự án. Đây sẽ là một cách hữu ích để bạn điều tra các bug phát sinh trong quá trình phát triển.

Xử lý Log còn liên quan đến chiến lược Error-Handling (xử lý lỗi) của mỗi hệ thống. Thông thường các hệ thống khi đẩy lên Product, thường có các tool để monitoring Log như Zabbix hay CloudWatch của AWS để thông báo cho người quản lý mỗi khi hệ thống phát sinh các Log lỗi Error.

Tuy nhiên chỗ nào cũng xuất Log thì sẽ làm cho code rất lộn xộn, khó đọc. Ta nên log ở những chỗ xử lý core quan trọng (ví dụ phần core gửi mail, gửi SMS, gọi API, sử dụng thư viện third party…), và log ở những chỗ try-catch Exceptions

Sau khi bạn thêm Log vì mục đích debug, điều tra một vấn đề nào đó, khi giải quyết xong hãy đừng quên xóa đi trước khi commit source.

 

Check ảnh hưởng

Đây là cả vấn đề mà ngay cả những DEV kinh nghiệm đôi khi làm vẫn bị sót. Hãy check ảnh hưởng mỗi khi sửa code đã có hoặc thêm code mới vào source code đã có.

Có nhiều loại ảnh hưởng khác nhau trong quá trình code:

  • Ảnh hưởng về hiển thị

Ví dụ: bạn thêm xử lý css mới, sửa một css cũ.

  • Ảnh hưởng về spec / nghiệp vụ

Ví dụ: sửa nghiệp vụ bên giao diện Update nhưng logic lại ảnh hưởng đến bên giao diện Create

  • Ảnh hưởng về môi trường

Ví dụ: sửa chỗ gửi SMS mà lại sử dụng config của bên môi trường thật.

  • Ảnh hưởng đến hệ thống khác

Ví dụ: bạn sửa logic update dữ liệu hệ thống của bạn, nhưng dữ liệu này lại đang được đồng bộ sang hệ thống khác để sử dụng.

  • Ảnh hưởng về mặt dữ liệu

Ví dụ khi bạn tạo một cột mới cho một bảng, thì bạn cũng sẽ phải chú ý vấn đề là tạo các dữ liệu default cho các dữ liệu có sẵn của bảng đó thế nào. Để đảm bảo khi release lên môi trường thật thì chức năng với các dữ liệu cũ vẫn hoạt động đúng.

 

Performance/ Security

Một số vấn đề nói đến ở bên trên cũng đã liên quan đến vấn đề Performance hoặc Security rồi, tuy nhiên ở đây mình vẫn muốn để thành một phần riêng vì vấn đề này quá rộng và quan trọng.

Khi làm outsource, nhất là với khách hàng Nhật, đôi khi dự án của bạn sẽ phải làm việc với các framework khá cũ, đôi khi là code thuần. Lúc này, việc xử lý security, performance càng cần chú ý.

Ví dụ: khi bạn viết các câu SQL để lấy dữ liệu, cần xử lý parameter hóa các giá trị điều kiện truyền vào câu SQL để tránh lỗi SQL Injection.

Thêm một điều rất đơn giản nhưng dễ bị bỏ qua khác là khi test code của mình, các bạn phải luôn có case nhiều dữ liệu. Bình thường các DEV khi code thường lười tạo dữ liệu để test hoặc chưa biết cách/ không đủ nhiều thời gian để tạo dữ liệu test. Nên với các dữ liệu thông thường thì chạy rất OK nhưng khi lượng dữ liệu tăng lên thì lại gây ra các lỗi khá nghiêm trọng.

Mình ví dụ một trường hợp đơn giản như sau:

Khi thực hiện Get request với tham số đẩy lên URL. Mỗi Browser đều có giới hạn về độ dài của URL khi truyền lên. Nhiều bạn truyền tham số lên (ví dụ như một mảng ID) mà ko lường các case nhiều dữ liệu, khi dữ liệu tăng lên thì URL vượt giới hạn độ dài, sẽ bị cắt phần đuôi nên sẽ ko thể chạy bình thường, gây ra Exception.

Hãy thay Post bằng Get nếu bạn thấy dữ liệu truyền lên có khả năng nhiều. Hoặc thay vì truyền tham số lên URL hãy nghĩ đến các cách truyền khác (như lưu session, storage, hoặc lưu dữ liệu tạm trong DB…).

 

Tiêu chuẩn 2 giây

Lúc mới vào công ty và tham gia dự án đầu tiên, khách hàng của mình có một nguyên tắc bất di bất dịch là tốc độ load trang không được quá 2 giây. Nếu quá 2s thì sẽ phải tìm mọi cách để giải quyết. Ví dụ với các trang search, dữ liệu rất lớn đến vài triệu người dùng, với kỹ thuật thông thường không thể tối ưu được thì họ đã phải tìm đến các phương án khác phức tạp hơn như dùng Search Engine của bên thứ ba. Với vai trò developer, bạn cũng nên có những tiêu chuẩn về performance như vậy để có hướng giải quyết vấn đề phù hợp từ ban đầu.

Người dùng sẽ nhanh chóng rời trang khi thấy tốc độ load chậm, dù trang của bạn có đẹp hay nhiều tính năng mấy chăng nữa. Code lởm chưa chắc là thảm họa nhưng sản phẩm code ra không có người dùng mới là hậu quả không ai mong muốn. Việc load chậm không chỉ làm người dùng khó chịu, mà bản thân chúng ta là những người phát triển cũng sẽ cảm thấy rất ức chế, mất thời gian, ảnh hưởng đến tiến độ công việc.

 

Write Test

Đây là một công việc không phải Developer nào cũng thích và đủ kiên trì để làm, và khá ít dự án khách hàng yêu cầu thực hiện điều này, vì sẽ làm đội công số (chi phí) phát triển lên gấp 1.5 đến 2 lần.

Tuy nhiên với kinh nghiệm cá nhân thì mình thấy trong cuộc đời developer đầy sóng gió của bạn, nếu đã một lần tìm hiểu và làm thực tế về Unit Test thì sau đó việc viết code của bạn sẽ chuẩn và chất lượng hơn rất nhiều

 

Tránh code rác

  • Các hàm/ các biến khai báo không sử dụng thì hãy xóa đi.
  • Xóa các khai báo import/include không sử dụng
  • Xóa các comment code thừa

Không biết các bạn thế nào chứ cá nhân mình rất ác cảm khi đang đọc code mà thấy có một đoạn code dài thòng lòng đang được comment. Những nội dung code này người ta gọi là zombie code.

Có thể người code là một người cẩn thận hoặc nghĩ rằng nội dung code này là vô cùng quý giá, ko thể xóa được, hoặc tránh rủi ro sau có thể dùng lại nên comment tạm thời.

Có một điều thú vị là theo thời gian các nội dung code xung quanh lại dần biến đổi, và các dòng source comment dần không còn ý nghĩa và giá trị sử dụng nữa.

  • Tránh có các đoạn code debug thừa ko cần thiết:

Log.debug(…) (Chỉ nên giữ lại các xử lý Log.Info hoặc Log.Error thực sự cần thiết)

windows.console.log(…)

 

Code refactoring

Khi giải quyết vấn đề, bạn hãy ưu tiên tập trung vào coding để giải quyết vấn đề một cách hiệu quả và nhanh nhất, đừng vội nghĩ đến việc Refactoring. Chỉ khi đã giải quyết xong vấn đề, nhất là trước thời điểm commit/ tạo Merge Request cho task của mình, thì nên view lại code một lượt để thực hiện refactoring. Việc này sẽ làm giảm chi phí về maintain code về sau, cũng như tăng thêm skill của DEV.

Các bạn cũng sẽ hỏi Refactoring thì là làm những gì? Câu trả lời rất đơn giản, ngoài các đặc thù đặc trưng không nhiều của mỗi dự án, hãy dành thời gian thực hiện các điều mà mình đã đề cập ở các mục trên.

 

Học hỏi từ code của người khác

Trong quá trình làm project, thay vì chỉ sử dụng, hãy liên tục học từ các open source (github là cả một thế giới đầy giá trị), source người khác đã viết trong project, và học ngay khi review source của người khác.

Một số cuốn sách kinh điển về code như Code Complete Clean Code cũng là thứ các bạn nên lướt qua.

 

Thái độ

Để code sạch, code đẹp, điều cuối cùng mình nghĩ chính là Thái độ của bạn với coding. Các sếp hay gọi với từ mỹ miều là Mindset. Kiên quyết và luôn có cái nhìn khắt khe khi viết các dòng code, mong muốn chúng thực sự là Clean Code thì dần tự nhiên kỹ năng của bạn sẽ được nâng cao.

 

Tổng kết

Một bài viết dài là một bài viết kém chất lượng, nên mình cũng cố gắng tổng kết các điều trên thành một bài thơ nhỏ, hy vọng sẽ giúp các bạn dễ nhẩm, dễ nhớ hơn:

 

Better Code Melody / Bài ca Better Code

Sinh ra là để sống
Định Mệnh đời Co-der!
Thì há chẳng phải nhớ
Chuẩn bài sẽ Mas-ter?

Chọn con-ven-tion đúng
Để khẳng định s-tyle
Co-ding s-tan-dard
Luôn bỏ túi để xài

Đặt tên cần có nghĩa
Com-ment vừa đủ thôi
Để trả lời “Why” nhé,
Không bừa phứa, nhiều lời

Độ dài hàm vừa phải,
Tách hàm common đi
Tránh dup-li-cate code
Sẽ chẳng ích lợi gì

Vòng lặp hem phải chỗ
Khai báo rồi Que-ry,
Gọi A-P-I nữa
Không thì sẽ chậm rì

Dù là Gấu xinh đẹp,
Hay Biến, Thuộc tính gì
Check Trước Khi Sử Dụng!”
Không là Đứng thành Quỳ

In-sert và up-date
Nhiều bước với Da-ta
Tran-sac-tion là thứ
Yêu chẳng muốn đòi quà

Log dùng để thả thính
Điều tra nhiều bug khoai
Ngoại lệ dù có khủng
Try-catch sẽ bắt bài

Dù từ đâu đi nữa
Copy code người ta
Thì phải luôn hiểu rõ,
Phân biệt chính hay tà

Check và tránh ảnh hưởng
Là điều chẳng cao siêu
Nhưng mà bỏ qua nó
De-g-rade rất nhiều

Đừng để zom-bie code
Kinh dị lắm người ơi
Xóa Khi Không Sử Dụng!”
Đồng nghiệp hết cạn lời

Per-for-mance muôn thuở
Là thứ đắc nhân tâm
Nguyên tắc 2 giây đó
Luôn phải nắm nằm lòng

Và cái không thể thiếu
Là Se-cu-ri-ty
Không may mà dính phốt
Ra đường chứ còn gì

Còn Write Test là món
Ai nghe cũng thở dài
Nhưng một lần xơi nó
Lại ra ngô, ra khoai

Sau cùng ta sẽ Xúc
Code Re-fac-to-ring
Để đời sau hưởng phúc
Luôn phải nhớ về mình!

Học từ o-pen-source
Học từ các gu-ru
Học từ p-rac-tice
Ta rồi sẽ P-ro!

Sinh ra là để sống
Định Mệnh đời Co-der!
Chúng ta hãy cùng nhớ
Chuẩn bài sẽ Mas-ter !

 

Phạm Minh Tuấn – CO-WELL Asia