Tăng lương nhờ cải thiện coding

I. Lời mở đầu

Trên con đường sự nghiệp IT, chúng ta cần biết và thành thạo nhiều kĩ năng khác nhau. Hầu hết 99% chúng ta có chung đặc điểm này: học một ngôn ngữ lập trình và sử dụng nó để làm việc. Từ quan điểm của mình, tôi cho rằng coding là kĩ năng cốt lõi & quan trọng, đặc biệt ở giai đoạn bạn chưa lên level Senior hoặc Manager.

Công ty cần developers để code. Developers cần code để kiếm tiền

Năng lực của dev cơ bản thể hiện thông qua 2 yếu tố: Chất lượng & Tốc độ. Tôi gọi chung là bằng từ “performance”. Một developer càng có performance cao thì mức lương của người đó sẽ tương xứng theo. Điều này có lẽ đúng trong hầu hết mọi ngành nghề không chỉ IT, các công ty luôn cần những người: làm việc ít hoặc không có sai sót & thời gian hoàn thành công việc nhanh. Những người như này, tôi hay gọi là: được việc hay đáng đồng tiền.

Với 1 dự án phần mềm, chất lượng code của dev ảnh hưởng trực tiếp đến chất lượng product & deadline. Điều này càng dễ nhận ra với các dự án dài hạn từ 2 năm trở lên. Chất lượng code tốt hạn chế phát sinh nhiều bug & giảm thời gian test của QA, các task vụ điều tra và đánh giá phạm vi ảnh hưởng thực hiện dễ dàng hơn với độ chắc chắn cao. Ngược lại, chất lượng code càng thấp thì thời gian làm task càng dài & dễ phát sinh bug. Từ hồi sinh viên, tôi đã đi code thuê project cuối kỳ để kiếm tiền đóng học và cho đến bây giờ, tôi vẫn luôn cho rằng điều này là đúng. Tôi đã thấy các dự án outsource trễ estimate phải OT liên tục vì Dev & QA tốn nhiều thời gian cho việc fixbug và test lại. Chẳng ai vui vẻ về việc này.

Tôi đã dành nhiều thời gian để suy nghĩ và cải thiện việc coding của bản thân. Tôi đã có một mức tăng lương nhanh chóng mà hồi xưa tôi không dám nghĩ đến – nhờ việc này. Bài viết hôm nay, hi vọng giúp mọi người có thêm hiểu biết và cải thiện khả năng coding.

II. Mô hình hóa miền nghiệp vụ

1. Cấp độ của dev

Trước khi vào nội dung chính, tôi muốn nói 1 chút về điều này. Bởi hiểu mình là ai, trình độ của mình ở đâu, mình yếu ở điểm gì – từ đó mới tìm cách để cải thiện.

Với quan điểm của mình, tôi cho rằng cơ bản là 3 cấp độ:
Level 1:  Biết sử dụng ngôn ngữ lập trình để code
Level 2: Biết coding
– Level 3: Làm tốt việc coding

Sau đây là phần giải thích từng level:
– Level 1: Bạn có hiểu biết về một ngôn ngữ, được tham gia vào phát triển một dự án. Nhưng code của bạn còn lộn xộn, rườm ra, sai naming convention. Code của bạn chạy chậm và có nhiều bug. Bạn thực hiện task chậm chạp & chưa tạo được độ tin cậy trong mắt đồng nghiệp. Bạn chưa đảm bảo cam kết về deadline.
– Level 2: Code của bạn đã cải thiện tốt hơn, nắm & áp dụng được naming convention. Bạn có khả năng review code cho đồng nghiệp. Bạn tạo ra ít bug hơn và thời gian làm task nhanh hơn trước. Có kĩ năng đánh giá và estimate task với ít khi có sai lệch. Có khả năng điều tra source code khi dự án phát sinh lỗi hoặc cần đánh giá phạm vi ảnh hưởng.
– Level 3: Code của bạn rất ít có bug, thời gian hoàn thành nhanh chóng. Bạn có khả năng điều tra, đánh giá được phạm vi nhanh chóng với độ tin cậy rất cao. Bạn hiểu & áp dụng các nguyên tắc code như SOLID, Design Patterns, v.v… Bạn có thể review & hướng dẫn member khác code. Có hiểu biết sâu sắc thành phần và cấu trúc dự án. Code của bạn có hiệu suất rất cao và đáp ứng tốt các vấn đề thay đổi trong tương lai.
Trên đây chỉ là sơ lược vài nét chính. Tôi đã thấy 1 số người dù đã code 7-8-10 năm vẫn mắc kẹt đâu đó giữa Level 1 và 2. Tôi thấy số năm làm việc có vẻ rất Senior nhưng trình độ thì thật sự thì không tương xứng. Tôi gọi vui là Senior Rơm
Tôi cũng thấy nhiều bạn trẻ chỉ 1-2 năm kinh nghiệm đã đạt đến tiệm cận level 3. Tất nhiên, sau đó họ “bay đi” đến các phương trời khác với mức lương rất tốt.

2. Mô hình hóa đối tượng nghiệp vụ

Có nhiều chủ đề để nói về việc cải thiện coding. Hôm nay, tôi chọn chủ đề này vì đơn giản rằng: Đối tượng nghiệp vụ là thành phần luôn luôn có của một dự án. Sẽ không bao giờ tồn tại 1 dự án mà không có đối tượng nghiệp vụ.

Mô hình hóa tốt đối tượng nghiệp vụ giúp cải thiện tư duy và làm cuộc đời dev của bạn tươi sáng hơn

Trong phần này, tôi sẽ đưa ra ví dụ thực chiến để chúng ta cùng trải nghiệm. Tôi có thể sử dụng Java, Scala, Python, Typescript. Với phần code trong ví dụ, tôi chọn Scala vì đây là ngôn ngữ rất tốt để biểu diễn.

Giai Đoạn 1: Khách hàng mong muốn có hệ thống quản lý Creative. Creative gồm các thông tin: tên – name (required), id chiến dịch – promotionId (required), trạng thái – status (required), lưu ý – note (optional), thời điểm tạo – createdAt (required).
Danh sách Status: Checking, Approved.

Coding: Ta nhanh chóng có 1 đối tượng Creative như sau:

case class CreativeId(value: String)

sealed abstract class CrStatus(val code: Int)
object CrStatus {
  case object Checking extends CrStatus(0)
  case object Approved extends CrStatus(1)
}

class Creative(
  id: CreativeId,
  name: String,
  status: CrStatus,
  promotionId: Int,
  createdAt: DateTime,
  note: Option[String]
)

Nhìn có vẻ rất ổn, đúng không. Thật ra, tốt hơn nữa thì ta nên để Creative là 1 Interface/Trait.
——————————————-
Giai đoạn 2: Sau 1 thời gian, khách hàng muốn thêm một loại Creative dành cho Youtube và có thông tin URL.

Coding: Ở đây tôi sẽ liệt kê cách làm của từng level.
Dev level 1 ~ 2: Thêm field CreativeType (Normal, YTB), thêm field URL vào trong class Creative. Sử dụng CreativeType để xác định loại creative cần xử lý trong logic code. Trông code sẽ như này:

sealed abstract class CrType(val code: Int)
object CrType {
  case object Normal extends CrType(0)
  case object YTB extends CrType(1)
}

class Creative(
  id: CreativeId,
  name: String,
  status: CrStatus,
  typed: CrType, // <- new field
  url: Option[URL], // <- new field
  promotionId: Int,
  createdAt: DateTime,
  note: Option[String]
)

Có vẻ ổn đó chứ :D. Nhưng không, nó không hề ổn. Việc pha trộn mã kiểu này tôi thấy thường xuyên, nó làm cho đối tượng ban đầu có xu hướng ngày càng bị mở rộng một cách không cần thiết. 1 số còn tệ hơn là sử dụng luôn field url để làm khóa xác định kiểu Creative. Thật bất hợp lý khi trộn 2 loại NormalYTB trong cùng mô hình, làm mọi thứ trở nên phức tạp. Nếu sau này, Khách hàng có nhiều yêu cầu cập nhật logic của đối tượng YTB thì chúng ta tốn công sức đánh giá phạm vi ảnh hưởng rồi còn phải test lại. Việc chung đụng này, thông thường hay đưa đến incidents: đánh giá thiếu phạm vi ảnh hưởng.

Dev level 2~3: Tạo 1 instance mới tách biệt, sau có thêm nhiều logic vào mỗi loại mà không lo bị ảnh hưởng. Ngoài ra, nên hạn chế tối đa việc tạo ra các field dạng Option. Vì khi nhìn vào ta không thể nhanh chóng nắm bắt được, khi nào là: Some(V) và None. Ta cần phải nhớ nhiều thứ như Some(V) thì sao & None thì sao. Nhức cái đầu.

class Creative(
  id: CreativeId,
  name: String,
  status: CrStatus,
  promotionId: Int,
  createdAt: DateTime,
  note: Option[String]
) {
  // Logic for normal creative
}

class YTBCreative(
  id: CreativeId,
  name: String,
  status: CrStatus,
  url: URL, // <-- new field
  promotionId: Int,
  createdAt: DateTime,
  note: Option[String]
) extends Creative(id, name, status, promotionId, createdAt, note) {
  // Logic for YTB creative
}

———————————
Giai đoạn 3: Khách hàng mong muốn có thêm thêm status: NotApproved đi kèm lý do và status: Approved thì đi kèm với thời điểm.

Coding:
Dev level 1~2: Thêm loại status vào CrStatus, thêm field “notApprovedReason”, “approvedAt” vào trong model. Trông code sẽ như này:

case object NotApproved extends CrStatus(2)
class Creative(
  // other fields ...
  status: CrStatus,
  notApprovedReason: Option[String],
  approvedAt: Option[DateTime]
)

Trông chúng thật tối nghĩa và lười biếng. Nhìn vào đoạn code này, chẳng có gì đảm bảo rằng “approvedAt” hay “notApprovedReason” chỉ đi kèm với status sở hữu nó. Chúng có thể bị khởi tạo với bất kì status nào. Đây là một mô hình hóa yếu ớt và rời rạc, tạo ra nguy cơ lỗi rất cao. Tôi đã thấy có trường hợp status bị đổi sang dạng khác nhưng dev quên set/clear field “notApprovedReason”.

Dev level 2~3: Họ sẽ tách bạch về mặt ngữ nghĩa trong mô hình code & không cho phép xảy ra việc đính “notApprovedReason”, “approvedAt” sai với status. Khi thay đổi status cũng chẳng cần phải nhớ việc set/clear field “notApprovedReason”.

object CrStatus {
  case object Checking extends CrStatus(0)
  case class Approved(approvalAt: DateTime = DateTime.now) extends CrStatus(1)
  case class NotApproved(reason: String) extends CrStatus(2)
}

Update lại CrStatus 1 chút, ta đã có một mô tả rõ ràng về ngữ nghĩa hơn. Khi status bị đổi thành “Approved” thì mặc định sẽ có thông tin “approvalAt”. Khi status bị đổi thành “NotApproved” thì yêu cầu phải có “reason”. Khi đổi từ “NotApproved” sang 1 loại status khác thì tự động xóa “reason” đi. Model Creative xây dựng chặt chẽ đảm bảo không thể phát sinh bug từ việc “change status”. Nhờ có quá trình compile-time, bất kì lỗi nào do việc “change status sai” đều sẽ được phát hiện. Not bug, Not test

———————————
Giai đoạn 4: Khách hàng mở rộng tập khách hàng với: Anh, Nhật, Trung. Muốn phần status hiển thị được ở 3 dạng ngôn ngữ trên. Theo các bạn nên làm như thế nào ???
Phần này để lại cho các bạn tự tìm câu trả lời nhé

Trên đây chỉ là những ví dụ rất giản đơn, minh họa cho việc business thường xuyên thay đổi.

III. Lời kết

– Trong quá trình mô hình hóa xin đừng tạo ra các đối tượng không có trong business. Trừ những trường hợp đặc biệt.

– Việc tạo ra một mô hình hóa đúng và chặt chẽ giúp bạn tránh xa BUG và tăng tốc thời gian làm task.

– Nhìn vào cách triển khai mô hình hóa trong code, tôi có thể đánh giá được level dev ở đâu. Middle, Senior hay không ? nằm ở trình độ chứ đừng chỉ nhìn vào số năm làm việc.

– Một mô hình hóa tốt cho thấy bạn có tư duy logic tốt, rành mạch & mạch lạc. Thật tuyệt vời khi được làm việc với những đồng nghiệp như này.

– Bạn tin tôi đi, dù làm ở công ty nào thì dev vẫn phải code thôi. Hãy cải thiện nó để cải thiện mức lương của chính mình.

– Nếu bài viết này được đón nhận tích cực thì tôi sẽ viết nhiều bài chia sẻ các bí kíp coding tốt hơn.

Sau cùng, cảm ơn mọi người đã đọc

Add a Comment

Scroll Up