[Code sao cho chuẩn] Phần 6 – Đôi khi code tốt nhất là không code gì cả.

Lúc còn trẻ, khi chúng ta mới bắt đầu làm một việc gì đó, chúng ta thường có tâm trạng rất háo hức, bản thân tràn đầy nhiệt huyết. Chúng ta muốn tự mình làm hết tất cả mọi thứ, từ những việc nhỏ nhất cho tới việc lớn nhất, chúng ta muốn làm trọn vẹn quy trình từ phân tích thiết kế đến triển khai sản phẩm, chúng ta muốn tự tay mình code hết tất cả các chức năng, như thể sợ ai đó code thay là sẽ lấy hết phần thưởng của ta vậy. Có thể không phải vì sợ mất phần, nhưng vì niềm đam mê học hỏi thôi thúc chúng ta làm như thế. Có lẽ chúng ta chỉ muốn cải thiện khả năng của mình mà thôi, vậy điều đó có nên khuyến khích hay không?

Lúc còn đi học, một số thầy cô cũng thường khuyên sinh viên không nên sử dụng các thư viện có sẵn mà nên tự mình code chức năng cho quen. Sinh viên cũng hay được khuyên rằng nên tham gia học hỏi và làm thử ở tất cả các quá trình phát triển sản phẩm để nắm rõ các nguyên tắc đã học. Thật ra thì điều này cũng đúng thôi, chúng ta nên phát triển kĩ năng của mình ở nhiều khía cạnh hơn là việc bó hẹp vào chỉ một vài khía cạnh, tuy nhiên là điều này chỉ đúng nếu thời gian của bạn cho phép và bạn đang cần học hỏi một công đoạn cụ thể nào đó. Nếu không, tốt nhất là bạn nên làm quen với việc sử dụng sự trợ giúp từ một thư viện hoặc framework nào đó, hay tốt hơn phối hợp hoạt động với những người khác, bởi vì việc phát triển phần mềm không phải là công việc của cá nhân, mà nó mang tính kế thừa và đòi hỏi cả tập thể cùng phải hợp sức. Mình sẽ nói rõ 2 vấn đề này ngay sau đây

Hãy tận dụng những sự trợ giúp xung quanh

Trước đây khi còn đang học đại học, ngôn ngữ chủ yếu mà mình code là C++, mặc dù cũng là một ngôn ngữ lập trình bậc cao nhưng sự hỗ trợ của nó về mặt tiện dụng kém hơn nhiều so với những ngôn ngữ hiện đại hơn như C#, Python hay PHP. Khi mình tự học thêm về PHP, mình có gặp phải một bài toán sau đây:

Từ một mảng gồm n phần tử là n tên sinh viên, xuất ra file log chuỗi có định dạng sau: “Students in class: name[0], name[1], name[3], …, name[n-1]”

Do đã quá quen với ngôn ngữ C++, mà C++ lại không có hỗ trợ các hàm thao tác với mảng, nên mình ngay lập tức tự viết đoạn code để xử lí nó như sau:

$names = array("name1", "name2", "name3", "name4", "name5");
$str_log = "Names in class: ";

for($i = 0; $i < count($names); $i++)
{
    $str_log = $str_log . $names[$i];
    if( $i < count($names) - 1)
    {
        $str_log .= ", ";
    }
}

writeLog($str_log);

Mình nghĩ rằng đoạn code trên là đã quá chuẩn rồi, cho tới khi có một anh bạn nhìn vào nó và nói với mình rằng “code gì mà lạ vậy!!!”. Sau đó gợi ý cho mình sửa lại như sau:

$names = array("name1", "name2", "name3", "name4", "name5");
$str_log = "Names in class: " . implode($names, ", ");

writeLog($str_log);

Wow! Mình như bị thông não. Một công việc đơn giản mà mình lại code ra quá phức tạp, trông có vẻ cồng kềnh và lại tốn nhiều thời gian cho người khác (nếu phải maintain sau này), PHP đã hỗ trợ sẵn hàm implode() để hỗ trợ chúng ta việc này rồi. Sẽ là hợp lí nếu bạn đang tìm hiểu C++ và học cách thao tác với mảng sao cho thuần thục, nhưng mục đích chính ở đây là chuyển data từ dạng mảng sang dạng chuỗi nhanh nhất và chính xác nhất có thể để ghi log, vậy nên không có lí do gì để phải tự viết các thao tác chi tiết cả.

Một ví dụ khác, khi mình xử lí chức năng hiển thị những người nào đã mua hàng trong ngày hôm nay, mình phải xử lí những công đoạn sau: lấy danh sách các hoá đơn trong ngày, đọc trong đó ra thông tin người mua hàng và lưu vào mảng (có thể lưu biến user_id), xử lí mảng để không có các giá trị user_id trùng nhau và xuất ra màn hình. Phải xử lí mảng để chắc chắn không có giá trị nào trong đó bị trùng lặp, mình đã code như sau:

function removeDuplicatedValue( $input_arr )
{
    $result_arr = array();
    foreach ( $input_arr as $item )
    {
        $result_arr[$item] = true;
    }

    return array_key( $result_arr );
}

// Sau đó sử dụng hàm trên để loại bỏ các giá trị dư thừa
$user_ids = getUsersToday();
$user_ids = removeDuplicatedValue($user_ids);

Đoạn code trên xử lí được vấn đề của mình, nhưng dường như cách xử lí này quá rườm rà và mất thời gian người đọc code. PHP đã hỗ trợ sẵn hàm xử lí các giá trị trùng lặp của mảng bằng hàm array_unique như sau:

//Sử dụng hàm trên để loại bỏ các giá trị dư thừa
$user_ids = getUsersToday();
$user_ids = array_unique($user_ids);

Code của ta trở nên ngắn và tường minh hơn nhiều phải không nào! Có thể nguyên nhân sâu xa của vấn đề là do mình chưa quen và chưa nắm rõ được ngôn ngữ PHP, nhưng không thể vì việc đó mà phủ nhận mình vừa viết ra một đoạn code tồi.

Bài học rút ra là: Trước khi bắt đầu làm một việc gì hay code chức năng nào đó, chúng ta nên nhìn quanh một vòng liệu xung quanh mình đã có sẵn công cụ nào hay chưa, nếu có hãy tận dụng chúng. Chúng ta đều biết code càng dài thì lại càng tiềm ẩn nguy cơ xảy ra lỗi, vậy nên hãy code sao cho đơn giản, dễ hiểu và ngắn nhất có thể. Một developer chuyên nghiệp phải có trách nhiệm với những dòng code mình viết ra, và để đảm bảo nó chạy đúng thì chúng ta phải chắc chắn rằng code viết ra phải được test một cách đầy đủ. Các hàm built-in hoặc các thư viện hỗ trợ chắc chắn là đã được test rất kĩ càng rồi, hãy tận dụng chúng nếu có thể, chẳng có gì là sai trái hay kém chuyên nghiệp khi sử dụng sự trợ giúp từ thư viện cả.

quote-if-i-have-seen-further-than-others-it-is-by-standing-upon-the-shoulders-of-giants-isaac-newton-135288.jpg

Thời gian không có nhiều, hãy nghĩ đơn giản

Một trong các công đoạn có thời gian xử lí lâu nhất chính là công đoạn đọc dữ liệu từ ổ đĩa lên, để nâng cao năng suất chương trình bạn sẽ nghĩ ngay đến kĩ thuật lưu cache. Ý tưởng thì đơn giản, nhưng cache sao cho tốt lại là vấn đề không hề đơn giản một chút nào. Xét một bài toán cụ thể sau đây: bạn cần xử lí để nâng cao hiệu suất của chương trình có thao tác như sau

// First: process data from fileA
readFromDisk(fileA);
// ...
readFromDisk(fileA);
// ...
readFromDisk(fileA);

// Then: process data from fileB
readFromDisk(fileB);
// ...
readFromDisk(fileB);

// Then: process data from fileC
readFromDisk(fileC);
// ...
readFromDisk(fileC);

Một cách rất tự nhiên, bạn nghĩ ngay tới kĩ thuật cache hoành tráng, gồm các kĩ thuật design pattern như Singleton, tự động lưu cache các dữ liệu đã load vào SESSION để dành sử dụng sau này, thiết kế cấu trúc dữ liệu để lưu cache, ..v.v. và hàng tá các thứ khác để chắc chắn rằng bạn xây dựng được một kĩ thuật cache hoàn chỉnh và hiệu quả. Bạn nghĩ rằng sẽ phải viết hàng trăm dòng code và sẽ phải mất vài giờ thậm chí cả ngày để có thể đảm bảo mọi thứ vận hành ổn định.

Nhưng nếu thế nào khi bạn chỉ có vỏn vẹn 30 phút để triển khai ý tưởng của mình? Bạn có nghĩ điều này là khả thi không? Nếu bạn còn đang nghĩ tới các kĩ thuật hoành tráng khác thì hãy thử xem xét một cách xử lí vô cùng đơn giản như sau:

Nhận xét rằng việc đọc dữ liệu từ đĩa chỉ thực hiện tuần tự với các filename, cụ thể là nó sẽ gọi fileA nhiều lần liên tiếp nhau, và nếu có chuyển sang fileB thì nó sẽ không còn cần tới fileA nữa, và cứ liên tục như thế. Với nhận định này, ta sẽ sử dụng một kĩ thuật cache cực kì đơn giản là: cache lại duy nhất file vừa sử dụng. Ta triển khai nó liền như sau:

clas readFromFile
{
    $lastUsed;          // cache data

    function lookUp( $key )
    {
        if ($this->lastUsed == null || $this->lastUsed['key'] != $key)
        {
            $this->lastUsed = loadDiskObject($key);
        }
  
        return $this->lastUsed;
    }

    // other functions
    // ...
}

Có thể đoạn cách giải quyết trên không phải là kĩ thuật cache hoàn hảo nhất, không chứa một cấu trúc dữ liệu chuẩn mực nhất, … nhưng nó giải quyết được vấn đề là đã tăng được đáng kể hiệu suất chương trình, đây chính là thứ chúng ta cần. Đối với một hệ thống lớn hơn, hay với một nhu cầu phức tạp hơn, thì kĩ thuật cache ở trên sẽ trở nên vô dụng, và bạn sẽ cần xây dựng một hệ thống cache hiệu quả hơn, nhưng đó là với những hệ thống lớn, nơi bạn cũng sẽ cần nhiều thời gian hơn để có thể thiết kế tốt.

Như đã nói từ đầu, một developer chuyên nghiệp phải có trách nhiệm đối với code mình viết ra, và nếu bạn không thể test hoặc không có thời gian để test nó một cách cẩn thận, hãy nghĩ đến những cách đơn giản hơn nhưng không kém hiệu quả hơn quá nhiều.

Suy nghĩ rộng hơn: Đừng tham lam, hãy tập trung vào vấn đề cốt lõi

Giải sử, khi bắt đầu ý định sẽ phát triển blog cá nhân, mình cũng có những suy nghĩ kiểu tham lam như rất nhiều bạn khác: mình muốn tự code nên giao diện và hệ thống xử lí backend để học thêm kĩ năng phát triển web, rồi mình cũng muốn tự thuê một Host hay một VPN để host trang blog của mình nhằm học thêm kĩ thuật triển khai hệ thống, … và còn rất nhiều mong muốn khác nữa. Tuy nhiên, cuối cùng lựa chọn của mình là nền tảng blog được cung cấp sẵn: WordPress.

Nếu mình chọn cách xây dựng hết từ đầu đến cuối thì có lẽ đến giờ này cũng chưa có bài viết nào được ra đời, lí do là … hệ thống chưa được xây dựng xong. Khi xây dựng một hệ thống mới từ đầu thì bạn phải có thiết kế đủ tốt để có thể mở rộng sau này, một hệ thống với đầy đủ chức năng để có thể post bài và quản lí bài viết, một hệ thống để thống kê số lượng truy cập, … một hệ thống đầy đủ như vậy cần rất nhiều thời gian để phát triển và chạy thử nghiệm. Nếu bạn đang học hỏi và muốn luyện tập coding, bạn có thể tự code nên trang web, nhưng vậy là không phù hợp nếu mình chỉ muốn viết blog và chia sẻ chúng. Lựa chọn WordPress, một công cụ hỗ trợ sẵn cho việc viết blog, vấn đề cốt lõi của bạn sẽ được giải quyết dễ dàng và hiệu quả hơn nhiều.

imageedit_2_3290832787
Mọi thứ vốn dĩ đơn giản, đừng làm nó trở nên phức tạp.

Công nghệ và kiến thức là vô hạn, nhưng thời gian của bạn và các developer khác là có hạn, vậy nên hãy biết bạn đang cần tập trung vào điều gì, code bạn cần phải xử lí điều gì, và bạn chỉ cần tập trung và xử lí việc đó mà thôi. Bạn không cần phải làm điều gì đó khó khăn hay code một thứ gì đó rất hoành tráng và phức tạp để có thể trở thành một pro-developer, bạn chỉ cần giải quyết mọi vấn đề theo cách đơn giản nhất, code một chức năng với ít code nhất để người khác dễ hiểu nhất là cũng đủ để trở thành một pro-developer rồi. Đời vốn dĩ đơn giản mà, và code cũng như thế, đừng làm nó trở nên phức tạp, phải không nào!

2 thoughts on “[Code sao cho chuẩn] Phần 6 – Đôi khi code tốt nhất là không code gì cả.

  1. Pingback: [Giới thiệu sách] The art of readable code – Cái tên đã nói lên tất cả – Webbynat

  2. Pingback: [ Giới thiệu sách ] The pragmatic programmer – Lập trình viên … tiêu biểu. (P1) – Những dòng code vui

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s