Nâng lương nhờ cải thiện coding (phần 2)
Đây phần 2 của chuỗi các bài blog về chủ đề: cải thiện tư duy coding (Bạn xem phần 1 tại đây nhé)
I. Lời mở đầu
Công việc hàng ngày của mỗi developer đều xoay quanh code. Những dòng code là xương sống cốt lõi của một dự án phần mềm.
Trong một công ty IT, sẽ chẳng cần đến Manager nếu thiếu các member (như PO, Dev, QA); sẽ chẳng cần PO & QA nếu thiếu developer; sẽ chẳng cần developer nếu thiếu những dòng code. Nói một cách chung nhất, các vị trí khác như Manager, PO, QA vận hành xung quanh nhân tố trung tâm là developer, nhằm đảm bảo rằng:
Dev sẽ tạo ra các dòng code chính xác đáp ứng được yêu cầu của Khách Hàng
Cải thiện chất lượng coding là việc nên làm và cần làm; giống như xây một cái nhà muốn vững chãi, bền lâu thì phải có một cái móng tốt. Tôi không biết dự án bạn áp dụng công nghệ gì cao siêu; source code dự án của bạn tệ thì việc toang là tất yếu: team càng đông càng nhiều bug, càng nhiều khách hàng sử dụng càng phát sinh nhiều issue. Đến sau cùng chẳng phải là code mà chính là TƯ DUY
(hay gọi bằng từ MINDSET
). Tư duy mới là thứ quan trọng nhất; sự khác biệt giữa người giàu và người nghèo chính là ở tư duy, sự khác biệt giữa dev giỏi và yếu cũng chính là ở tư duy.
Luyện tập cách coding bản chất chính là rèn luyện tư duy
II. Tư duy trong các loại dự án phần mềm
Tôi trải qua 3 loại dự án: dự án cá nhân, dự án outsource, dự án product. Tư duy về lập trình của tôi thay đổi và tiến bộ qua từng loại theo thứ tự là:
[D.A cá nhân] mindset level 1 —> [D.A outsource] mindset level 2 —> [D.A product] mindset level 3
Dự án cá nhân: Đây là những dự án tôi tự nghĩ ra và làm cho vui. Business đã được tôi hình dung từ trước. Vì business logic đơn giản nên tôi chỉ cần “code theo kiểu đơn giản” là đã đủ, chẳng cần theo tiêu chuẩn hay quy tắc nào. Mọi thứ hoàn thành êm ả, tôi cho rằng có vẻ mình đã biết code. Hồi đầu đi phỏng vấn xin việc, khi được hỏi về OOP, tôi đã học tủ các khái niệm và trả lời như SGK, đến khi được hỏi việc áp dụng thì tôi trả lời thẳng thắn: Cần gì OOP, code của em vẫn chạy đúng đó thôi. Họ nhìn tôi và cười nhẹ, tất nhiên sau đó tôi tạch phỏng vấn.
Dự án outsource: Đây là những dự án mà business logic đã được chuẩn bị đầy đủ từ trước, chỉ cần đọc hiểu tài liệu mô tả và làm theo. Đặc điểm nổi bật là bạn cần phải “code thật trâu bò” để hoàn thành kịp deadline. OT là một chuyện bình thường như cơm bữa. Tôi, với sức trẻ khoẻ – lao vào code ngày đêm. Chẳng ai bảo tôi nên code như nào, cơ bản là tự bơi, trong team thân ai người đấy lo. Vì việc của họ còn chưa xong hơi đâu ra chỉ cho tôi. Tôi với tư duy “if-else” vật lộn với “array, loop”. Càng làm càng thấy phức tạp, rối rắm giữa code của mình và người khác. Quan trọng là code vẫn đang chạy đúng nên thôi kệ. Code một lần cuốn chiếu là xong. Sau bao nỗ lực OT thì dự án cũng kết thúc. Những kết quả đọng lại là 1 cảm giác mệt mỏi chán nán với nghề code. Tôi đúng là 1 CODER chính hiệu.
Dự án product: Đây là những dự án giúp tôi nhận ra mình cần thay đổi tư duy code từ kiểu “trâu bò” sang kiểu “thông minh”. Điểm khác biệt to lớn so với các dự án outsource, business logic cập nhật liên tục qua từng sprint chứ không định hình từ trước. Nếu cứ giữ suy nghĩ và cách code chỉ ở mức biết dùng syntax language trước kia thật không ổn, càng làm càng chậm, càng làm càng tăng nguy cơ mắc lỗi, luôn lo sợ rằng sẽ xảy ra bug ở 1 vùng ảnh hưởng nào đó mà mình không ngờ đến.
Với kiểu code “thông minh”, tôi thấy cuộc đời tươi sáng hơn nhiều, làm việc một cách đầy tự tin và chắc chắn. Tốc độ làm task được cải thiện rất nhiều.
Tôi tổng kết lại thành 1 bảng:
D.A cá nhân | D.A outsource | D.A product | |
Business logic | Tự định nghĩa | Có tài liệu liệt kê sẵn | Nhận yêu cầu từ KH qua PO |
Tần suất thay đổi logic | Không | Hiếm khi | Thường xuyên |
Thời gian dự án | Rất ngắn | Ngắn | Dài |
Kiểu làm việc | Cuốn chiếu | Cuốn chiếu | Một vòng lặp cập nhật |
Kiểu coding | Hoang dã | Trâu bò | Thông minh có cấu trúc |
Yếu tố cần có | – Ngôn ngữ LT – Thời gian rảnh – Ý tưởng | – Ngôn ngữ LT – Mô hình lập trình cơ bản – Sức khoẻ dẻo dai | – Ngôn ngữ LT – Mô hình lập trình cao – Principle – Best practice – Tư duy phân tích & thiết kế |
Việt Nam có thế mạnh về gia công phần mềm với lợi thế cạnh tranh là giá thành. Việc code bất chấp để hoàn thành deadline đã tạo ra nhiều lập trình viên kém và chất lượng dự án thường thấp. Giống như kiểu muối dưa mà ăn xổi vậy. Ăn thì vẫn được mà thật không tốt.
Vài năm gần đây, lợi thế về giá không còn như trước nữa khi mức lương trung bình tăng cao. Nhiều nước trong khu vực nổi lên với chi phí rẻ hơn và chất lượng dev tốt hơn. Tất yếu dẫn đến VN muốn cạnh tranh được thì cần nâng cao chất lượng sản phẩm. Kéo theo bản thân mỗi người dev cần nâng cao năng lực nếu muốn đi đường dài trong ngành nghề IT này. Chỉ dừng ở mức biết dùng syntax để code thì bạn sớm sẽ bị loại khỏi cuộc chơi, chưa kể bây giờ còn là thời đại của AI.
Lấy chất lượng sản phẩm làm yếu tố cạnh tranh không phải là mới nhưng chưa bao giờ lỗi thời. Đặt mình với tư cách là một khách hàng, chúng ta ai cũng muốn có được những sản phẩm chất lượng.
III. Thiết kế cấu trúc domain model
Trong phần 1, tôi đã chia sẻ qua về tầm quan trọng của một mô hình hoá chặt chẽ các đối tượng nghiệp vụ.
Cơ bản, ta cần ngăn chặn lỗi phát sinh ngay trong nội tại của sản phẩm. Đừng chỉ trông cậy vào tester vì họ không phải là vị thần toàn năng phát hiện được tất cả bug, nhất là ở những vùng ảnh hưởng không ngờ đến được. Hãy làm sao cho dev chức năng A thì chỉ quan tâm đến vùng A thôi, không phải lo nghĩ đến vùng B, C, D. Như vậy, phạm vi test giảm xuống và mọi người có thể tập trung nhiều trí lực vào đó. Khi vùng quan tâm rộng và nhiều thì khả năng tư duy sẽ giảm xuống do bộ não cần xử lý nhiều thứ.
Đối mặt với vấn đề: sửa chức năng cho media X lại bất ngờ ảnh hưởng đến cả chức năng của media Y. Dễ dàng quy ra rằng: team đánh giá thiếu phạm vi ảnh hưởng. Làm thế nào để team đánh giá đúng & đủ phạm vi ảnh hưởng là câu hỏi theo tôi không có lời đáp chính xác. Một câu hỏi tốt hơn nên là: làm thế nào để không cần đánh giá phạm vi ảnh hưởng.
Hình vẽ minh hoạ cụ thể cho kiểu code ngày xưa của tôi. Càng ngày càng có nhiều hàm được sử dụng như vậy nên việc xác định phạm vi ảnh hưởng càng phức tạp hơn. Dù rằng, cách làm như trên không phải là sai, cơ mà thật sự không tốt khiến việc xác định phạm vi bị phụ thuộc mạnh vào năng lực thực hiện của member. Chỉ cần đầu óc tôi không tỉnh táo, minh mẫn là rất dễ bị thiếu.
Đã đến lúc cần thay đổi cấu trúc code: tuân thủ nguyên tắc chữ “O”: Open & close. Open với mở rộng và Close với chỉnh sửa. Một cấu trúc tốt sẽ giúp bạn dễ dàng hình dung và tránh được nhiều rắc rối không cần thiết. Tôi sẽ chia sẻ cấu trúc code từ dự án ACA, 1 dự án thực tế trong công ty tôi đã làm để minh hoạ cho điều trên.
Về dự án ACA ra đời với mục đích: chuyển đổi response từ các media thành 1 dạng chung cho phía client.
Phần xử lý media response chính là core domain business của hệ thống này. Ý tưởng ban đầu trông giống như này.
Tôi sẽ có 1 lớp cơ sở là: Converter, nơi định nghĩa các thuộc tính và hành vi xử lý JsonResponse. Mỗi media lại có cách xử lý json khác nhau nên tôi cần định nghĩa mỗi lớp converter riêng cho từng media. Dựa vào quy tắc đặt tên như vậy, tôi biết khi cần cập nhật cho Facebook thì sửa code cụ thể ở file nào. Cả LineConverter & FBConverter đều sử dụng 1 số hàm chung của lớp Base. Khi hàm chung này không đáp ứng được nhu cầu của media, đơn giản là tôi ghi đè (override) lại hàm đó tại lớp con. Điều này đảm bảo việc sửa code cho media FB sẽ không bao giờ ảnh hưởng đến media Line. Tôi chỉ cần tập trung viết code và test riêng mỗi FBConverter thôi.
Một thời gian sau khách hàng thêm yêu cầu mới: muốn chuyển giá trị currency trong response về hết yên Nhật với tỉ lệ cho trước. VD: 1 USD = 50 JPY chẳng hạn. Có thể có nhiều kiểu chuyển đổi như thế trong tương lai. Tôi cần cập nhật lại sơ đồ cấu trúc đại loại trông như này:
Ở đây tôi dùng Bridge Pattern, tạo thêm 1 interface tên: Transformable với hàm: transfrom
public interface Transformable { ObjectNode transform(EntityType entityType, ObjectNode objectNode); }
Tôi thêm instance của Transformable vào lớp cơ sở Converter, thế là tất cả các lớp con bên dưới đều có thêm tính năng chuyển đổi currency. Còn việc chuyển đổi cụ thể như nào thì được định nghĩa ở từng lớp riêng biệt tuỳ theo nhu cầu. Cấu trúc code mềm dẻo, linh hoạt mà vẫn hoạt động chính xác, đúng tiêu chí loose-coupling
Khách hàng lại có thêm yêu cầu: lấy data từ một danh sách các field động, ngoài những field cố định. Field động là gì thì tuỳ thuộc vào request mà client gửi. Lần này, tôi đã nhờ đến Decorator Pattern để giải quyết. Sơ đồ lại được cập nhật 1 lần nữa.
Lớp decorator sẽ bọc bên ngoài instance của LineConverter, cho phép tôi bổ sung thêm tính năng lấy field động mà không làm ảnh hưởng đến hành vi ban đầu. Code cũ của tôi trong lớp LineConverter là không đổi nên không cần test, tôi chỉ cần tập trung vào kiểm thử với các hàm trong lớp Decorator thôi. Tôi mất 10 phút để làm xong bài kiểm thử này.
Nguyên lý đơn giản là: càng ít sửa code thì càng ít bug
Tôi chẳng cần quan tâm chuyện gì đã xảy ra với các hàm trong lớp LineConverter vì đã được kiểm thử trước đó rồi. Việc cập nhật tính năng mới thực sự rất nhẹ nhàng.
Khi thêm yêu cầu xử lý cho một media mới là Google, chỉ cần copy nguyên mẫu và sửa lại 1 vài chỗ là đã chạy ngon lành rồi. Việc code có cấu trúc giúp tôi tự tin copy-paste mà vẫn đảm bảo hệ thống hoạt động đúng. Thời gian coding cho 1 media diễn ra nhanh chóng.
Tổng kết sau cùng, tôi có 1 cấu trúc thực tế như này:
Tuy chưa phải là cấu trúc tốt nhất nhưng đủ ổn để sử dụng. Các media được khoanh vùng qua naming-package và naming-class, giúp việc đọc hiểu thuận tiện.
IV. Tổng kết
– Khi code của bạn rõ ràng thì tư duy hiểu & trình bày & phân tích vấn đề của bạn sẽ được cải thiện theo. Chúng ta là developer, chúng ta không chỉ biết code theo yêu cầu mà còn hiểu và tư vấn lại cho khách hàng.
– Việc coding không nên diễn ra theo kiểu “hoang dại” mà nên tuân theo những quy tắc. Với một người trình độ cao, họ nhìn bố cục code của bạn thì cũng phần nào hiểu trình độ của bạn đến đâu. Một source code có cấu trúc gọn gàng vẫn hơn 1 đống bùi nhùi, hỗn độn của các function chứ nhỉ ?
– Bạn đừng mong việc cải thiện tư duy coding sẽ thay đổi ngay lập tức. Mọi thứ cần có thời gian. Đức Phật đã nói: có 2 điều đáng tiếc nhất trên đời. Một là chưa từng bắt đầu. Hai là bỏ cuộc giữa chừng.
– Trong công việc, gặp người tư duy tốt là khó, gặp người tư duy tốt mà sẵn sàng chỉ cho bạn lại càng khó, bạn có thể hiểu và áp dụng được lời chỉ dẫn của họ lại càng khó hơn. Cuối cùng, vẫn là ở nỗ lực của bạn thôi. Ý thức quyết định vật chất nên trong ý thức của bạn phải muốn trước đã, mọi thứ khác sẽ tự đến theo sau.
Cảm ơn mọi người đã đọc bài blog dài này. Mọi người đón chờ phần tiếp theo nhé.