Tư duy lập trình phục vụ cuộc sống

5088
Bài viết được chia sẻ tại Spiderum
Cho đến thời điểm này, một trong những quyết định sáng suốt nhất cuộc đời mà tôi có thể khẳng định được với cá nhân mình, đó là đi học lập trình. Dĩ nhiên tôi không phải là lập trình viên, và cũng không có ý định trở thành lập trình viên (mặc dù ước vọng thầm kín của tôi vẫn là một ngày được làm bá chủ C++), nhưng những bài học về giải quyết vấn đề (problem-solving) trong cuộc sống mà việc học lập trình đã đem lại cho tôi thì giá trị phải cao gấp mấy lần việc đọc ba cái sách self-help nhảm nhí.
Mà self-help thì làm sao?
Và với bài viết này, tôi sẽ chỉ cho bạn thấy những bài học đó là gì, với hy vọng rằng chúng sẽ có ích cho bạn. Và biết đâu đấy, có thể nhờ chúng mà bạn sẽ quyết định xách đít lên và đi học lập trình thì sao?
Ngoài ra thì như đã nói, tôi không phải là lập trình viên. Thế nên trong nội dung bài viết có chỗ nào hiểu sai về concept lập trình, hy vọng được dựa trên tính hiệu quả thực tế mà chiếu cố.

Chia nhỏ vấn đề

Một trong những thứ đầu tiên mà bạn phải làm trong hầu hết mọi chương trình học lập trình, đó là flowchart. Điều này là bởi để viết được cả một chương trình lớn là không hề dễ dàng, và việc bạn cần phải làm là chia nhỏ các bài toán lớn thành các vấn đề nhỏ hơn đến hết mức có thể để bạn có thể quản lý chúng dễ dàng.
Sơ đồ hình dung các loại vòng lặp trong R
Thật vậy, đối với các lập trình viên, không giống với hình dung của nhiều người, khi được giao nhận việc phát triển một sản phẩm phần mềm, họ sẽ không bắt tay vào thực hiện ngay lập tức. Việc đầu tiên họ làm luôn luôn là vẽ ra một sơ đồ giải thuật với flowchart hoặc một số công cụ vẽ bản đồ tư duy khác.
Trên sơ đồ giải thuật đó, họ sẽ xác định các thuộc tính của bài toán, các phương án tiếp cận, các khả năng có thể xảy ra và thứ tự ưu tiên của các đầu việc, rồi sau đó mới xác định các đầu việc cụ thể ở từng giai đoạn, và cuối cùng mới đến thực hiện. Điều này là bởi, việc bắt tay vào thực hiện công việc, tác vụ cụ thể chỉ là những vấn đề nhỏ. Còn những câu hỏi trên diện rộng, liên quan đến cấu trúc của chương trình, khả năng mở rộng và duy trì trên đường dài của chương trình mới là những vấn đề lớn. Và chỉ cần chúng ta vội vàng hay coi nhẹ việc giải quyết những vấn đề này sớm, thì càng về đường dài, hậu quả của những sai lầm này sẽ ngày càng nặng nề. Khái niệm này thường được gọi là nợ kỹ thuật (technical debt).
Tương tự như trong cuộc sống hay trong công việc của bạn. Mỗi khi bạn phải đối mặt với một vấn đề phức tạp, ví dụ như một kế hoạch xây nhà, mua nội thất, trả nợ, đặt thiết kế và in áo đồng phục cho hội nhóm hay tổ chức của bạn, lên kế hoạch đi chơi cuối tuần cho gia đình, quản lý các tác vụ trong một dự án của công ty, v.v. thì việc đầu tiên mà bạn cần làm là ngồi xuống, làm một tách trà ăn một miếng bánh, và vẽ một sơ đồ từng bước các đầu việc cụ thể mà bạn cần phải làm, các vấn đề nhỏ mà bạn cần phải giải quyết, thứ tự giải quyết thế nào để tiết kiệm thời gian và chi phí, và các phương án dự phòng ra sao. Điều này sẽ giúp bạn dự đoán trước được phần lớn các khả năng có thể xảy ra khi giải quyết vấn đề ở mọi thời điểm, và đưa ra được phương án giải quyết cần thiết, để tránh phải gánh nợ.
Một ví dụ về nợ kỹ thuật trong cuộc sống:
Bạn vừa chuyển ra khỏi chỗ ở cũ, và đang cần tìm một căn nhà để thuê ở. Công ty bạn làm việc nằm ở Mai Dịch, còn người yêu của bạn thì nhà lại ở Hà Đông. Bạn quyết định tìm thuê một căn nhà ở gần khu vực Hà Đông, để tiện còn chạy qua chạy lại chỗ người yêu, cuối tuần còn đi chơi muộn không lo đưa đón ngược đường. Xác định là sẽ ở đây lâu dài, bạn quyết định thuê nhà với kỳ hạn hợp đồng là 1 năm, đồng thời đầu tư lắp đặt điều hòa tủ lạnh cho đầy đủ tiện nghi. Tuy nhiên chỉ hai tháng sau khi bạn chuyển về chỗ ở mới thì bạn và người yêu chia tay. Giờ thì lợi ích của việc ở gần nhà người yêu đã mất, đồng thời mỗi sáng bạn phải mất đến gần 50 phút đi đường để di chuyển từ Hà Đông đến Mai Dịch, vừa tắc đường vừa nóng vừa bụi bặm. Bạn vừa đi vừa nghĩ đến cảnh lúc chuyển nhà đi lại phải tháo dỡ điều hòa với tủ lạnh và thuê người chuyên chở mà thấy khổ. Đây chính là món nợ kỹ thuật mà bạn phải gánh vì đã đưa ra quyết định sai lầm trong việc chọn vị trí thuê nhà (và chọn yêu đối tượng kia.)
Và cũng giống như trong lập trình, bạn càng tỉnh táo và khéo léo khi đưa ra các quyết định ban đầu bao nhiêu, thì kết quả bạn nhận được về sau sẽ càng chuẩn xác và càng đúng tiến độ bấy nhiêu.

Giảm cảm tính

Công cụ chia nhỏ vấn đề không chỉ dừng lại ở mô hình flowchart.
Có rất nhiều khi trong cuộc đời cũng như công việc của mình, bạn phải đối mặt với hai lựa chọn khác nhau, nhưng bạn cảm thấy chúng lại cân bằng nhau. Lựa chọn nào cũng có những cái lợi cũng như những cái bất lợi của nó, và bạn không biết làm thế nào để đặt được chúng lên bàn cân, bởi đơn giản vì chúng không có cùng một đơn vị đo và một hệ quy chiếu.
Nhưng có một tin mừng là chúng thực ra không cần thiết phải cảm tính đến thế. Việc chúng ta có thể làm, như đã nói, đó là chia nhỏ vấn đề cảm tính lớn ra thành những vấn đề cảm tính nhỏ hơn.
Tôi lấy một ví dụ cụ thể:
Bạn đang đứng trước hai sự lựa chọn khó khăn về sự nghiệp. Một là tiếp tục làm việc cho công ty A là công ty hiện tại của bạn, và hai là chuyển sang công ty B làm. Cả hai lựa chọn đều có những điểm mạnh cũng như điểm yếu của riêng mình. Vậy thì làm thế nào để bạn đưa ra được quyết định?
Tại đây, việc bạn cần làm là lên danh sách tất cả những điểm có lợi cũng như những điểm bất lợi của từng lựa chọn đối với cá nhân bạn. Và giả sử bạn có một số đầu mục như sau:

Công ty A

Có lợi
  • Sếp là chuyên gia với nhiều năm kinh nghiệm trong ngành.
  • Được sếp hứa hẹn sẽ training trực tiếp 1-1 nếu ở lại.
  • Môi trường làm việc startup trẻ trung, vui vẻ và thoải mái. Các đồng nghiệp đều rất tâm đầu ý hợp với mình.
  • Thời gian di chuyển đến công ty mỗi sáng là 15 phút.
Bất lợi
  • Lương thấp hơn khoảng 20% so với với nhu cầu sinh hoạt.
  • Có thông tin cho rằng lời hứa của sếp là không thực sự đáng tin cậy.
  • Áp lực công việc sẽ rất lớn nếu như sếp không thể thực hiện được lời hứa training, và mình sẽ mất thêm ít nhất là 3-6 tháng nữa mới có thể thấy được kết quả cho quyết định của mình nếu ở lại. Và cơ hội sang bên công ty B sẽ khép lại chỉ trong vòng khoảng 1 tháng nữa.

Công ty B

Có lợi
  • Chắc chắn sẽ được training trực tiếp vì công ty B đã xây dựng xong quy trình training rất quy củ, mặc dù người train không có nhiều kinh nghiệm bằng sếp bên công ty A.
  • Mức lương cao hơn 20% so với nhu cầu sinh hoạt.
  • Sản phẩm của công ty này phù hợp với tiêu chuẩn đạo đức của bạn hơn so với sản phẩm của công ty A.
  • Địa điểm công ty rất gần với chỗ làm của người yêu bạn và bạn có thể dành thời gian đi ăn cùng nhau vào buổi trưa.
Bất lợi
  • Vì là công ty lớn nên môi trường làm việc rất khắt khe, yêu cầu nhân viên phải mặc đồng phục vào Thứ Hai và Thứ Sáu. Nếu đến muộn quá 15 phút vào buổi sáng, bạn sẽ mất luôn chấm công của sáng hôm đó. Ngoài ra còn có khả năng bạn sẽ phải quan tâm đến chính trị chốn văn phòng.
  • Thời gian di chuyển đến công ty mỗi sáng là 35 phút, lại đi qua một đoạn đường rất bụi và không có cây xanh.
Sau khi đã có được danh sách tất cả các điểm ưu và điểm nhược của từng lựa chọn mà bạn có thể nghĩ ra được như trên, bạn tiến hành tính điểm cho từng đầu mục dựa trên giá trị ưu tiên của cá nhân bạn (hay nói cách khác là cảm tính) trên thang điểm từ -5 đến 5 (hoặc cũng có thể là một thang điểm nào đó khác như -7 đến 7 hay -10 đến 10, miễn là thang điểm này có tâm tại 0; cá nhân tôi cho rằng thang điểm -5 đến 5 là hợp lý nhất). Các điểm có lợi sẽ là điểm số dương, và các điểm bất lợi sẽ là điểm số âm.
Cuối cùng tính tổng lại, bạn sẽ có một bảng như sau:
Bảng tính điểm các đầu mục của hai lựa chọn
Sau khi đã thu về kết quả tổng điểm, bạn có thể thấy lựa chọn công ty B có sức nặng hơn lựa chọn công ty A là 2 điểm, và do đó có lẽ bạn nên lựa chọn công ty B. Hai điểm là một con số không lớn, mà nếu bạn không chia nhỏ các đầu mục của mỗi lựa chọn ra và đưa chúng trở về cùng một hệ quy chiếu là thang điểm từ -5 đến 5, thì bạn sẽ khó lòng có thể nhận ra được sự khác biệt rất nhỏ này. Mặc dù về bản chất, các con số này cũng đều mang tính cảm tính và không thể chuẩn xác 100%, nhưng chí ít là chúng tỏ ra rõ ràng hơn là hai lựa chọn đơn sơ giữa công ty A và công ty B ban đầu.

Dán nhãn & phân bổ mọi thứ

Một trong số các công cụ cơ bản để xây dựng một chương trình là các biến (variable). Biến cũng có nhiều loại biến, được phân biệt bằng các kiểu dữ liệu (data type), ví dụ như kiểu nhị phân đúng-sai là boolean, kiểu ký tự là char, kiểu số nguyên là int. Thế rồi bạn cũng có các công cụ không phân biệt bằng kiểu dữ liệu như biến, mà lại phân biệt bằng cấu trúc như lệnh If-Else, vòng lặp, mảng, struct, hàm, v.v. Việc bạn lựa chọn đúng công cụ để xử lý đúng loại đối tượng là vô cùng quan trọng, bởi nó sẽ xác định tính hiệu quả trong khả năng xử lý vấn đề của bạn.
Tương tự như trong cuộc sống. Nếu “Kiểu dữ liệu” là chất lỏng thì phải đựng bằng chai, còn nếu là thủy tinh dễ vỡ thì phải để vào hộp xốp. Giấy tờ cá nhân quan trọng thì phải cất vào cùng một nơi, còn giấy tờ đã không còn giá trị gì thì có thể sử dụng để làm nháp. Nếu thời gian không phải là lựa chọn cơ cấu đúng đắn, thì phải chọn lấy nhân lực. Nếu crush thuộc tuýp người lãng mạn thích đọc sách, thì mình phải tiếp cận bằng con đường văn thơ chữ nghĩa, thay vì bằng con đường phú quý và thẻ VISA. Nếu nhân viên thuộc dạng người có tham vọng, thì phải lấy cái sản phẩm hay con đường sự nghiệp làm công cụ khuyến khích họ, thay vì chỉ chăm chăm lương thưởng.
Mỗi một quyết định “dán nhãn & phân bổ” đúng đắn mà bạn đưa ra, là bạn đã giúp cho cuộc đời được thêm phần gọn gàng và dễ quản lý hơn cho chính mình.

Tối giản ở đâu và bao nhiêu

Hay thực ra câu hỏi là chúng ta có nên hiểu biết về chính trị? Và biết bao nhiêu là đủ?
Đối với một người lập trình viên, thì một trong những câu hỏi đau đầu nhất và cũng yêu cầu kinh nghiệm trong nghề dạn dày nhất, đó chính là việc khi nào thì nên trừu tượng hóa (abstraction), và bao nhiêu lớp trừu tượng hóa là đủ.
Các cấp độ trừu tượng trong lập trình
Ở cấp độ cơ bản nhất trong lập trình, chúng ta biết rằng có một đoạn code thực hiện một tác vụ cụ thể và nhất định—nhận vào một tham số và trả về một kết quả—mà ta sẽ sử dụng lại nhiều lần, thì chúng ta có thể thiết lập để đoạn code đó để nó trở thành một “hàm.” Và hàm này sẽ hoạt động như một module riêng biệt: nó nhận đầu vào và trả về đầu ra. Đó được gọi là trừu tượng hóa.
Việc trừu tượng hóa này này đem lại một số lợi ích, và một trong số đó là việc bạn có thể yên tâm rằng mỗi khi bạn cần đến chức năng của đoạn code này, bạn chỉ việc gọi hàm đó ra, đưa cho nó những tham số nó cần, và nó sẽ trả về cho bạn kết quả mà bạn yêu cầu, và bạn sẽ không cần phải quan tâm xem nó hoạt động như thế nào nữa. Ta có thể tạm hình dung rằng, việc hàm đó hoạt động như thế nào là vấn đề bậc thấp (low-level), còn việc ta sử dụng công cụ đó để giải quyết công việc của mình thế nào là vấn đề bậc cao (high-level).
Trong cuộc sống cũng tương tự. Mỗi một tiên đề, mỗi một giả định, mỗi một niềm tin mà chúng ta có về bản chất của một sự vật hay một sự việc, cũng là một lớp trừu tượng hóa như vậy. Ví dụ:
Bạn biết rằng giá mỗi lít xăng rơi vào khoảng 20.000đ, do đó nếu hôm nay đổ xăng chúng ta có thấy giá xăng ở mức 20.520đ/lít, thì bạn có thể tạm tin rằng đó là giá xăng đã được nhà nước ban hành, chứ không phải là trạm xăng có ý định lừa bạn.
Bạn ngồi ở một tiệm cà phê, và có một người đàn ông đến hỏi bạn có muốn đánh giày hay không. Bạn bảo rằng có, thế là ông ta đưa cho bạn một đôi dép khá cũ nát để đi tạm, rồi cầm đôi giày của bạn đi mất. Ba tiếng sau không thấy ông ta quay lại, bạn xác định rằng ông ta đã lấy cắp đôi giày của bạn.
Định lý Pythagoras đã chứng minh rằng bình phương cạnh huyền bằng tổng bình phương hai cạnh góc vuông, do đó để tính ra cạnh huyền thì bạn chỉ cần tính được hai cạnh góc vuông là xong, khỏi phải chứng minh lại.
Bạn biết rằng kể từ năm 1986, Việt Nam đã chuyển từ cơ chế kinh tế kế hoạch hóa tập
trung bao cấp sang cơ chế kinh tế thị trường, cho phép các doanh nghiệp tư nhân tồn tại và cho phép bàn tay của thị trường thúc đẩy kinh tế. Từ đó kinh tế Việt Nam phát triển, đời sống nhân dân được cải thiện. Bạn thấy rằng như thế này là đủ, không cần phải quan tâm gì nữa làm gì cho phiền.
Bạn có thể thấy rằng, những giả định trên giúp cho bạn được dễ dàng hơn trong cuộc sống, thay vì phải suy nghĩ và kiểm chứng cho từng khả năng mà bạn có thể nghĩ ra một, bởi vì sức người và thời gian của bạn là có hạn. Bạn không thể cứ mỗi lần cần dùng là lại phải chứng minh lại định lý Pythagoras, hay bạn phải kiểm chứng khả năng rằng có lẽ người đàn ông kia trong lúc đánh giày của bạn đã gặp phải tai nạn bởi một người lái xe say rượu. Nếu như không có trừu tượng hóa, hay nói cách khác là tạm chấp nhận rằng một số điều là đúng mặc dù chưa có đủ cơ sở, thì bạn sẽ không thể mua nổi một mớ rau 5.000đ ngoài chợ.
Tuy vậy, bạn cũng có thể thấy, hoặc là không, rằng một trong số những giả định nêu trên là không hợp lý cho lắm.
Và đó cũng chính là vấn đề của việc trừu tượng hóa. Nó chỉ cho bạn nhìn thấy bề nổi để bạn có thể sống dễ dàng hơn và tập trung vào những gì quan trọng với mình. Thế nhưng điều đó cũng đồng nghĩa rằng nó đã trở thành một lớp vỏ bọc phức tạp để che giấu đi cái bề chìm. Và nhỡ như có một vấn đề nào đó có nguồn gốc xuất phát từ bề chìm, thì sẽ rất khó để bạn có thể truy ra được nguồn gốc của nó. Và vì thế, bạn cần phải biết đưa ra một lựa chọn hết sức thông minh trong việc lúc nào thì cần trừu tượng hóa, và lúc nào thì không. Tương tự trong cuộc sống cũng vậy: biết giả định rằng cái gì cái gì về cơ bản là đúng, không cần phải nghĩ nữa, và cũng hiểu rằng cái gì đòi hỏi chúng ta phải có cái nhìn sâu sắc hơn.

Nếu những vấn đề thường ngày của bạn là những vấn đề bậc cao, thì chính trị là những vấn đề bậc thấp. Bởi chỉ khi chính trị ổn, thì cuộc sống của bạn mới êm đẹp để bạn tập trung lo sống.

Nhiều bạn trẻ cũng hỏi tôi, giống như tôi cũng thường tự hỏi chính mình, rằng em có nên quan tâm đến chính trị hay không, và quan tâm bao nhiêu là đủ. Và mặc dù tôi ghét phải trả lời thế này, nhưng sự thực là bao nhiêu cũng không đủ. Cũng giống như code bậc thấp trong lập trình, bạn càng biết sâu thì bạn càng có lợi. Tuy nhiên, chi phí thời gian và công sức là không hề nhỏ, và do đó vấn đề chính bạn cần lưu ý là cân bằng nó với bản thân, với cuộc sống và với công việc hàng ngày của bạn như thế nào.

Thói quen ghi chép

Trong lập trình, nhất là khi phải làm việc trong một dự án dài hơi, khi phải làm việc với một ai đó khác, hay có người sẽ phải đến làm việc tiếp trên code của bạn, thì một trong những quy tắc sống còn trong quá trình code của bạn là comment, về cơ bản nghĩa là ghi chép.
Đừng quên comment code của bạn
Việc ghi chép này là vô cùng quan trọng. Khi bạn đang thực hiện một tác vụ nào đó với một logic rất cụ thể, nhưng bạn lại chưa có yếu tố nào khác tương tác với logic của tác vụ này, thì bạn sẽ phải tìm cách ghi chép lại logic của nó để sau này khi bạn cần phải tương tác với nó, bạn còn có thể làm được mà không phải mò mẫm xem lại xem hôm đó mình ăn cái gì mà có thể code như thế này.
Tương tự trong cuộc sống cũng có rất nhiều những trường hợp như vậy. Bạn được nhận một voucher giảm giá cho một cửa hàng mua sắm mới khai trương, và trên voucher có mã giảm giá và số điện thoại của cửa hàng. Tuy nhiên, hiện tại bạn lại đang ở chỗ làm và chưa thể thực hiện ngay được việc đến cửa hàng đó hoặc là gọi điện cho họ để đặt mua, nên bạn quyết định sẽ ghi lại mã giảm giá và số điện thoại của họ để đến tối về nhà bạn có thể thực hiện được việc bạn muốn làm. Và đó cũng tương tự như những ngày sinh nhật của bạn bè, số điện thoại của đồng nghiệp, giấy nhắc nhở công việc cá nhân, kế hoạch cho giai đoạn tiếp theo của dự án, tài liệu mô tả của bộ phận thiết kế gửi cho bộ phận kỹ thuật, v.v. Luôn ghi nhớ trong đầu rằng, khi bạn phải làm việc với một ai khác, hay là phải làm việc với chính bản thân mình trong tương lai cũng vậy, hãy dành cho họ một sự cảm thông sâu sắc và ghi chép lại để trang bị cho họ những thông tin cần thiết để họ có thể làm việc hiệu quả được với bạn.
Mặt khác, đôi khi bạn cũng cần phải lưu ý cách bạn ghi chép hay là khối lượng bạn ghi chép. Bởi trong lập trình, dù ít hay nhiều thì khi bạn comment dài dòng quá lố, compiler cũng phải mất thời gian đi qua comment của bạn (mặc dù nó sẽ bỏ qua), do đó phần nào làm chậm thời gian xuất chương trình. Cũng giống như việc bạn ghi chép lại vào điện thoại hay vào sổ tay vậy. Dung lượng của chúng có hạn, và thời gian ghi chép hay đọc lại của bạn cũng có hạn, vì thế hãy ghi chép sao cho thật thông minh, rõ ràng, không thiếu cũng không thừa.

Giải phóng bộ nhớ khi không dùng

Khi xây dựng một chương trình, bạn luôn luôn cần phải theo dõi khối lượng đối tượng, dữ liệu, cũng như các vùng bộ nhớ trên hệ thống máy tính hay các loại tài nguyên hệ thống khác, và nhanh chóng giải phóng những gì mà chương trình không còn cần sử dụng nữa ra khỏi bộ nhớ, để dành nó cho các tác vụ tiếp theo.
Hãy tự dọn bàn của mình trước khi bạn crash
Bạn có thể hình dung ổ cứng máy tính của bạn là cái nhà kho, còn bộ nhớ của bạn là cái mặt bàn. Hoặc cũng có thể ổ cứng máy tính là nhà bạn, còn bộ nhớ là chỗ làm của bạn. Hay như chính trên máy tính của bạn, thì theo một cách nào đó, màn hình desktop cũng là một dạng bộ nhớ của bạn. Bộ nhớ là nơi bạn tạm để những tác vụ, những công cụ, cũng như các thành phần của công việc hiện tại đang dang dở và yêu cầu phải có quyền truy cập hay khả năng tiếp cận cao với các đối tượng này. Thế nhưng bạn cần phải nhớ rằng bộ nhớ của bạn chỉ là tạm thời, và vì thế nó có giới hạn. Khi bạn đã hoàn thành một công việc nào đó, hãy nhớ đến việc dọn dẹp rác trên bộ nhớ. Thứ gì không cần nữa thì hãy trả lại về đúng nơi lưu trữ. Chỉ có thể bằng cách đó, tâm trí của bạn mới có thể được giải phóng để các tác vụ sau được giải quyết hiệu quả hơn. Vả lại, bản thân việc giải phóng bộ nhớ và nhìn nó được gọn gàng, sạch sẽ, và quan trọng nhất là tối ưu, cũng là một cách để đem lại cảm giác rằng bạn đã hoàn thành công việc xuất sắc vậy.
Dẫu biết rằng các ngôn ngữ lập trình bậc cao hiện nay như C# đã có khả năng tự động quản lý bộ nhớ (garbage collection), nhưng bài học thực tế của việc giải phóng bộ nhớ vẫn luôn mang giá trị cao.

Lời kết

Tôi sẽ không ngần ngại khi nói rằng lập trình quả thực xứng đáng là một môn cần được đưa vào giảng dạy mở rộng ở cấp phổ thông, bởi những công cụ mà nó đem lại cho chúng ta trong việc tư duy về giải quyết vấn đề là vô cùng hữu dụng, dù cho bạn có là ai đi chăng nữa. Bởi khi và chỉ khi bạn bắt buộc phải làm việc với những giới hạn, thì từ đó mới có thể sinh ra được những cách giải quyết tối ưu.
  50 keywords mà mọi JAVA developer nên biết
  Một vài lỗi mà những lập trình viên mới có thể mắc phải
  13 kênh Youtube lập trình tiếng Việt giúp bạn trở thành Fullstack developer
Có thể bạn quan tâm: