Hãy để tôi làm rõ một điều đầu tiên. Nếu bạn là một lập trình viên mới bắt đầu, bài viết này không nhằm mục đích khiến bạn cảm thấy tồi tệ về những sai lầm mà bạn có thể mắc phải mà là để giúp bạn nhận thức được chúng, dạy bạn phát hiện các dấu hiệu của chúng và nhắc nhở bạn tránh những sai lầm đó.
Tôi có đọc qua một bài viết chia sẻ thực tế từ một bạn lập trình viên nước ngoài và thấy hữu ích nên có dịch lại bài viết để cho những bạn bắt đầu lập trình có thể rút kinh nghiệm từ đây nhé.
Tôi đã phạm phải những sai lầm này trong quá khứ và đã học được từ mỗi người trong số họ. Tôi rất vui vì đã hình thành thói quen viết mã (coding habits) để giúp tôi tránh mắc phải sai lầm một lần nữa. Bạn cũng nên làm như vậy.
Những sai lầm này không được trình bày ở đây theo bất kỳ thứ tự cụ thể nào.
1) Viết mã mà không cần lập kế hoạch
Nhìn chung, nội dung bằng văn bản chất lượng cao không thể được tạo ra một cách dễ dàng. Nó đòi hỏi chúng ta phải suy nghĩ và nghiên cứu một cách cẩn thận. Các chương trình chất lượng cao cũng không ngoại lệ.
Viết chương trình chất lượng là một quá trình rèn luyện: ***Think (Nghĩ). Research (Nghiên cứu). Plan (Kế hoạch). Write (Viết). Validate (Xác thực). Modify (Biến đổi). ***
Thật tiếc là không có từ viết tắt tốt cho điều này. Bạn cần tạo thói quen luôn thực hiện đúng trình tự các hoạt động này.
Một trong những sai lầm lớn nhất mà tôi mắc phải khi là một lập trình viên mới bắt đầu là bắt đầu viết mã ngay lập tức mà không hề suy nghĩ và nghiên cứu nhiều. Mặc dù điều này có thể hoạt động tốt đối với một ứng dụng độc lập nhỏ, nhưng nó có tác động tiêu cực lớn đối với các ứng dụng lớn hơn.
Giống như bạn cần suy nghĩ trước khi nói bất cứ điều gì mà bạn có thể hối tiếc, bạn cần suy nghĩ trước khi viết mã bất cứ điều gì mà bạn có thể hối tiếc. Viết mã cũng là một cách để truyền đạt suy nghĩ của bạn.
Lập trình chủ yếu là đọc mã trước đó, nghiên cứu những gì cần thiết và cách nó phù hợp với hệ thống hiện tại, đồng thời lập kế hoạch viết các tính năng với các bước gia tăng nhỏ, có thể kiểm tra được. Việc viết các dòng mã thực sự có lẽ chỉ là 10% của toàn bộ quá trình.
Đừng nghĩ về lập trình như viết các dòng mã. Lập trình là một sự sáng tạo dựa trên logic cần được nuôi dưỡng.
2) Lên kế hoạch quá nhiều trước khi viết mã
Đúng. Lập kế hoạch trước khi bắt tay vào viết mã là một điều tốt, nhưng ngay cả những điều tốt cũng có thể gây hại cho bạn khi bạn làm quá nhiều. Quá nhiều nước có thể gây ngộ độc cho bạn.
Đừng tìm kiếm một kế hoạch hoàn hảo. Điều đó không tồn tại trong thế giới lập trình. Hãy tìm kiếm một kế hoạch đủ tốt, thứ mà bạn có thể sử dụng để bắt đầu. Sự thật là, kế hoạch của bạn sẽ thay đổi, nhưng điều tốt là buộc bạn phải tuân theo một số cấu trúc dẫn đến mã của bạn rõ ràng hơn. Quá nhiều kế hoạch chỉ đơn giản là lãng phí thời gian của bạn.
Tôi chỉ nói về việc lập kế hoạch cho các tính năng nhỏ. Lập kế hoạch cho tất cả các tính năng cùng một lúc đơn giản nên được đặt ngoài quy tắc! Đó là cái mà chúng tôi gọi là Phương pháp tiếp cận thác nước, là một kế hoạch tuyến tính của hệ thống với các bước riêng biệt sẽ được hoàn thành từng bước một. Bạn có thể tưởng tượng cách tiếp cận đó cần bao nhiêu kế hoạch. Đây không phải là loại kế hoạch mà tôi đang nói ở đây. Cách tiếp cận thác nước không hoạt động đối với hầu hết các dự án phần mềm. Bất cứ điều gì phức tạp chỉ có thể được thực hiện với sự thích ứng nhanh với thực tế.
Viết chương trình phải là một hoạt động đáp ứng. Bạn sẽ thêm các tính năng mà bạn sẽ không bao giờ nghĩ đến trong sơ đồ thác nước. Bạn sẽ loại bỏ các tính năng vì những lý do mà bạn sẽ không bao giờ xem xét trong kế hoạch thác nước. Bạn cần sửa lỗi và thích nghi với những thay đổi. Bạn cần phải linh hoạt.
Tuy nhiên, hãy luôn lập kế hoạch cho một số tính năng tiếp theo của bạn. Hãy làm điều đó thật cẩn thận vì lập kế hoạch quá ít và lập kế hoạch quá nhiều đều có thể ảnh hưởng đến chất lượng mã của bạn và chất lượng mã của bạn không phải là thứ bạn có thể mạo hiểm.
3) Đánh giá thấp tầm quan trọng của chất lượng mã
Nếu bạn chỉ có thể tập trung vào một khía cạnh của mã mà bạn viết, thì đó phải là khả năng đọc của nó. Mã không rõ ràng thì chỉ là rác. Nó thậm chí không thể tái chế được.
Đừng bao giờ đánh giá thấp tầm quan trọng của chất lượng mã. Hãy xem mã hóa như một cách để giao tiếp triển khai. Công việc chính của bạn với tư cách là một lập trình viên là truyền đạt rõ ràng việc triển khai bất kỳ giải pháp nào mà bạn đang thực hiện.
Ngay cả những điều nhỏ nhặt cũng quan trọng. Ví dụ: Nếu bạn không nhất quán với cách thụt đầu dòng và cách viết hoa của mình, bạn có lẽ đơn giản là đánh mất giấy phép viết mã.
Một điều đơn giản khác là việc sử dụng các dòng dài. Bất cứ điều gì vượt quá 80 ký tự đều khó đọc hơn nhiều. Bạn có thể muốn đặt một số điều kiện dài trên cùng một dòng để giữ cho khối câu lệnh if dễ nhìn hơn. Đừng làm thế. Đừng bao giờ vượt quá giới hạn 80 ký tự.
Nhiều vấn đề đơn giản như thế này có thể được khắc phục dễ dàng bằng các công cụ linting và formatting. Trong JavaScript, chúng ta có hai công cụ tuyệt vời kết hợp hoàn hảo với nhau: ESLint và Prettier. Làm điều mình ưu tiên và luôn luôn sử dụng chúng.
Dưới đây là một vài sai lầm khác liên quan đến chất lượng mã:
— Sử dụng nhiều dòng trong một hàm hoặc một tệp. Bạn phải luôn chia mã dài thành các phần nhỏ hơn để có thể kiểm tra và quản lý riêng. Cá nhân tôi nghĩ rằng bất kỳ chức năng nào có hơn 10 dòng là quá dài, nhưng đây chỉ là quy tắc chung.
— Sử dụng phủ định kép. Xin đừng làm điều đó.
— Sử dụng tên biến ngắn, chung chung hoặc dựa trên kiểu tên biến. Cung cấp các tên biến mô tả và không mơ hồ.
— Các chuỗi và số nguyên thủy mã hóa cứng không có mô tả. Nếu bạn muốn viết logic phụ thuộc vào một giá trị số hoặc chuỗi nguyên thủy cố định, hãy đặt giá trị đó trong một hằng số và đặt cho nó một cái tên hay.
— Sử dụng các lối tắt và cách giải quyết cẩu thả để tránh mất nhiều thời gian hơn cho các vấn đề đơn giản. Đừng nhảy xung quanh các vấn đề. Hãy đối mặt với thực tế của bạn.
— Nghĩ rằng mã dài hơn là tốt hơn. Mã ngắn hơn là tốt hơn trong hầu hết các trường hợp. Chỉ viết các phiên bản dài hơn nếu chúng làm cho mã dễ đọc hơn. Ví dụ: không sử dụng các biểu thức một lớp thông minh và các biểu thức bậc ba lồng nhau chỉ để giữ cho mã ngắn hơn, nhưng cũng đừng cố gắng làm cho mã dài hơn khi không cần thiết. Xóa mã không cần thiết là điều tốt nhất bạn có thể làm trong bất kỳ chương trình nào.
— Việc sử dụng quá nhiều logic có điều kiện. Hầu hết những gì bạn nghĩ cần logic có điều kiện đều có thể được thực hiện mà không cần nó. Xem xét tất cả các lựa chọn thay thế và chọn hoàn toàn dựa trên khả năng đọc. Không tối ưu hóa cho hiệu suất trừ khi bạn có thể đo lường. Tránh điều kiện Yoda (Yoda conditions) và bài tập trong điều kiện.
4) Chọn giải pháp đầu tiên
Khi tôi bắt đầu lập trình, tôi nhớ rằng khi tôi gặp một vấn đề, tôi sẽ tìm ra giải pháp và ngay lập tức chạy theo nó. Tôi sẽ gấp rút triển khai ngay trước khi nghĩ đến sự phức tạp và khả năng thất bại của giải pháp được xác định đầu tiên của mình.
Mặc dù giải pháp đầu tiên có thể hấp dẫn, nhưng các giải pháp tốt thường được phát hiện khi bạn bắt đầu đặt câu hỏi về tất cả các giải pháp mà bạn tìm thấy. Nếu bạn không thể nghĩ ra nhiều giải pháp cho một vấn đề, đó có thể là dấu hiệu cho thấy bạn không hoàn toàn hiểu vấn đề.
Công việc của bạn với tư cách là một lập trình viên chuyên nghiệp không phải là tìm giải pháp cho vấn đề. Đó là tìm giải pháp đơn giản nhất cho vấn đề. “Đơn giản” có nghĩa là giải pháp phải hoạt động chính xác và thực hiện đầy đủ nhưng vẫn đủ đơn giản để đọc, hiểu và bảo trì.
5) Không Bỏ Cuộc
Một sai lầm khác mà tôi đã mắc phải thường xuyên hơn là tôi muốn thừa nhận là gắn bó với giải pháp đầu tiên ngay cả sau khi tôi xác định rằng đó có thể không phải là cách tiếp cận đơn giản nhất. Điều này có lẽ liên quan đến tâm lý với tâm lý “không bỏ cuộc”. Đây là một tâm lý tốt cần có trong hầu hết các hoạt động, nhưng nó không nên áp dụng cho lập trình. Trên thực tế, khi viết chương trình, tâm lý đúng đắn là thất bại sớm và thất bại thường xuyên.
Ngay khi bạn bắt đầu nghi ngờ về một giải pháp, bạn nên cân nhắc vứt bỏ nó và suy nghĩ lại vấn đề. Điều này đúng cho dù bạn đã đầu tư bao nhiêu vào giải pháp đó. Các công cụ kiểm soát nguồn như GIT có thể giúp bạn phân nhánh và thử nghiệm nhiều giải pháp khác nhau. Hãy tận dụng điều đó.
6) Không tìm kiếm trên Google
Đã có nhiều trường hợp tôi lãng phí thời gian quý báu để cố gắng giải quyết một vấn đề khi lẽ ra tôi nên nghiên cứu nó trước.
Trừ khi bạn đang sử dụng công nghệ tiên tiến nhất, nếu không khi bạn gặp sự cố, rất có thể người khác cũng gặp sự cố tương tự và tìm ra giải pháp cho nó. Tiết kiệm cho mình một chút thời gian và Google It First.
Đôi khi, Google sẽ tiết lộ rằng những gì bạn nghĩ là vấn đề thực sự không phải, và điều bạn cần làm không phải là khắc phục nó mà là chấp nhận nó. Đừng cho rằng bạn biết mọi thứ cần thiết để chọn giải pháp cho một vấn đề. Google sẽ làm bạn ngạc nhiên.
Tuy nhiên, hãy cẩn thận với những gì bạn Google. Một dấu hiệu khác của người mới là sao chép và sử dụng mã của người khác mà không hiểu nó. Mặc dù mã đó có thể giải quyết vấn đề của bạn một cách chính xác, nhưng bạn không bao giờ được sử dụng bất kỳ dòng mã nào mà bạn không hiểu đầy đủ.
Nếu bạn muốn trở thành một lập trình viên sáng tạo, đừng bao giờ nghĩ rằng bạn biết mình đang làm gì.
7) Không sử dụng Encapsulation
Điểm này không phải là về việc sử dụng mô hình hướng đối tượng. Việc sử dụng khái niệm đóng gói luôn hữu ích. Không sử dụng đóng gói thường dẫn đến các hệ thống khó bảo trì hơn.
Trong một ứng dụng, một tính năng chỉ nên có một nơi xử lý nó. Đó thường là trách nhiệm của một đối tượng duy nhất. Đối tượng đó chỉ nên tiết lộ những gì thực sự cần thiết để các đối tượng khác của ứng dụng sử dụng nó. Đây không phải là về bí mật mà là về khái niệm giảm sự phụ thuộc giữa các phần khác nhau của ứng dụng. Việc tuân thủ các quy tắc này cho phép bạn thực hiện các thay đổi một cách an toàn trong phần bên trong của các lớp, đối tượng và chức năng của mình mà không phải lo lắng về việc phá vỡ mọi thứ ở quy mô lớn hơn.
Các đơn vị logic và trạng thái khái niệm nên có các lớp (classes) riêng của chúng. Theo lớp, ý tôi là một mẫu thiết kế. Đây có thể là một đối tượng Lớp thực tế hoặc một đối tượng Chức năng. Bạn cũng có thể xác định nó là Mô-đun (Module) hoặc Gói (Package).
Trong một lớp logic, các phần nhiệm vụ độc lập sẽ có các phương thức riêng. Các phương pháp nên làm một việc và làm tốt việc đó. Các lớp tương tự nên sử dụng cùng tên phương thức.
Là một lập trình viên mới bắt đầu, tôi không phải lúc nào cũng có bản năng bắt đầu một lớp mới cho một đơn vị khái niệm và tôi thường không xác định được những gì có thể được khép kín. Nếu bạn thấy một lớp “Util” đã được sử dụng làm bãi rác cho nhiều thứ không thuộc về nhau, thì đó là dấu hiệu của code newbie. Nếu bạn thực hiện một thay đổi đơn giản và sau đó phát hiện ra rằng thay đổi đó có hiệu ứng xếp tầng và bạn cần thực hiện nhiều thay đổi ở nơi khác, thì đó là một dấu hiệu khác của mã mới.
Trước khi thêm một phương thức vào một lớp hoặc thêm nhiều trách nhiệm hơn cho một phương thức, hãy suy nghĩ và đặt câu hỏi cho bản năng của bạn. Bạn cần thời gian ở đây. Đừng bỏ qua hoặc nghĩ rằng bạn sẽ cấu trúc lại điều đó sau. Chỉ cần làm điều đó ngay lần đầu tiên.
Ý tưởng lớn ở đây là bạn muốn mã của mình có Độ gắn kết cao (High Cohesion) và Khớp nối thấp (Low Coupling), đây chỉ là một thuật ngữ ưa thích có nghĩa là giữ các mã có liên quan với nhau (trong một lớp) và giảm sự phụ thuộc giữa các lớp khác nhau.
8) Lên kế hoạch cho điều chưa biết
Bạn thường muốn nghĩ xa hơn giải pháp mà bạn đang viết. Tất cả các loại giả định sẽ xuất hiện trong đầu bạn với mỗi dòng mã mà bạn viết. Đây là một điều tốt để thử nghiệm các trường hợp biên, nhưng thật sai lầm khi sử dụng làm trình điều khiển cho các nhu cầu tiềm năng (potential needs).
Bạn cần xác định what-if (tức là các trường hợp có thể xảy ra) thuộc về loại nào trong hai loại chính này. Đừng viết mã không cần thiết vào ngày hôm nay. Đừng lên kế hoạch cho tương lai không xác định.
Viết một tính năng bởi vì bạn nghĩ rằng bạn có thể cần nó trong tương lai hoàn toàn là sai. Đừng làm điều đó.
Luôn viết số lượng mã tối thiểu cần thiết cho giải pháp bạn đang triển khai vào hôm nay. Xử lý các trường hợp biên, chắc chắn, nhưng đừng thêm vào các tính năng không cần thiết..
9) Không sử dụng đúng cấu trúc dữ liệu
Khi chuẩn bị cho các buổi phỏng vấn, những lập trình viên mới thường tập trung quá nhiều vào việc học thuật toán. Tìm hiểu và sử dụng các thuật toán tốt khi cần thiết là điều tốt, nhưng việc ghi nhớ chúng có lẽ sẽ không góp phần nào vào khả năng lập trình xuất sắc của bạn.
Tuy nhiên, ghi nhớ các ưu điểm và nhược điểm của các cấu trúc dữ liệu khác nhau mà bạn có thể sử dụng trong ngôn ngữ lập trình của bạn chắc chắn sẽ làm cho bạn trở thành một nhà phát triển tốt hơn.
Việc sử dụng sai cấu trúc dữ liệu là một tín hiệu rõ ràng và sáng sủa cho thấy bạn là một người mới học lập trình.
Bài viết này không nhằm mục đích giảng dạy bạn về cấu trúc dữ liệu, nhưng để tôi đề cập một vài ví dụ nhanh:
— Sử dụng danh sách (mảng-arrays) thay vì bản đồ (đối tượng-objects) để quản lý các bản ghi-records
Lỗi cấu trúc dữ liệu phổ biến nhất có lẽ là sử dụng danh sách thay vì bản đồ để quản lý một danh sách bản ghi. Đúng, để quản lý một DANH SÁCH (LIST) bản ghi, bạn nên sử dụng một BẢN ĐỒ (MAP).
Lưu ý rằng tôi đang nói về danh sách bản ghi ở đây, trong đó mỗi bản ghi có một định danh được sử dụng để tìm kiếm bản ghi đó. Việc sử dụng danh sách cho các giá trị scalar (khi chỉ có một giá trị) là phù hợp và thường là lựa chọn tốt, đặc biệt nếu trọng tâm của việc sử dụng là "đẩy" các giá trị vào danh sách.
Trong JavaScript, cấu trúc danh sách phổ biến nhất là một mảng và cấu trúc bản đồ phổ biến nhất là một đối tượng (cũng có một cấu trúc bản đồ trong JavaScript hiện đại).
Việc sử dụng danh sách thay vì bản đồ để quản lý các bản ghi thường là sai. Mặc dù điểm này thực sự chỉ đúng đối với các bộ sưu tập lớn, tôi khuyên bạn nên tuân thủ nó mọi lúc. Lý do chính điều này quan trọng là khi tìm kiếm các bản ghi bằng cách sử dụng định danh của chúng, bản đồ nhanh hơn nhiều so với danh sách.
— Không Sử Dụng Ngăn Xếp (Stack)
Khi viết bất kỳ mã nào yêu cầu một dạng đệ quy nào đó, luôn có sự cám dỗ để sử dụng các hàm đệ quy đơn giản. Tuy nhiên, thường khó để tối ưu hóa mã đệ quy, đặc biệt là trong môi trường đơn luồng.
Tối ưu hóa mã đệ quy phụ thuộc vào việc hàm đệ quy trả về cái gì. Ví dụ, tối ưu hóa một hàm đệ quy trả về hai hoặc nhiều cuộc gọi tới chính nó khó khăn hơn tối ưu hóa một hàm đệ quy chỉ đơn giản trả về một cuộc gọi tới chính nó.
Những gì chúng ta thường bỏ qua khi còn là người mới học là có một phương án thay thế cho việc sử dụng hàm đệ quy. Bạn có thể sử dụng một cấu trúc Ngăn Xếp (Stack). Tự đẩy các cuộc gọi hàm vào Stack và bắt đầu lấy chúng ra khi bạn sẵn sàng duyệt lại các lệnh gọi.
10) Làm cho mã hiện tại trở nên tồi tệ hơn
Hãy tưởng tượng rằng bạn được đưa cho một căn phòng bừa bộn như thế này:
Sau đó, bạn được yêu cầu thêm một mục vào phòng đó. Vì đã là một mớ hỗn độn rồi, bạn có thể muốn đặt món đồ đó ở bất cứ đâu. Bạn có thể hoàn thành nhiệm vụ của mình trong vài giây.
Đừng làm điều đó với mã lộn xộn. Đừng làm cho tình hình trở nên tồi tệ hơn! Luôn để lại mã sạch hơn một chút so với khi bạn bắt đầu làm việc với nó.
Điều đúng đắn cần làm đối với căn phòng phía trên là dọn dẹp những thứ cần thiết để đặt món đồ mới vào đúng vị trí. Ví dụ: nếu mặt hàng là một bộ quần áo cần được đặt trong tủ quần áo, bạn cần dọn đường dẫn đến tủ quần áo đó. Đó là một phần của việc thực hiện nhiệm vụ của bạn một cách chính xác.
Dưới đây là một số thói quen sai thông thường thường làm cho mã trở nên lộn xộn hơn so với trước đây (đây không phải là một danh sách hoàn chỉnh):
Nhân bản mã. Nếu bạn sao chép/dán một phần mã để chỉ thay đổi một dòng sau đó, bạn đang đơn giản là nhân bản mã và làm cho tình hình lộn xộn hơn. Trong ngữ cảnh của ví dụ về căn phòng lộn xộn ở trên, điều này tương đương với việc thêm một cái ghế nữa có chân thấp thay vì đầu tư vào một cái ghế mới có thể điều chỉnh chiều cao. Luôn luôn giữ khái niệm trừu tượng trong tâm trí và sử dụng nó khi bạn có thể.
Không sử dụng tập tin cấu hình. Nếu bạn cần sử dụng một giá trị có thể thay đổi trong các môi trường khác nhau hoặc vào các thời điểm khác nhau, giá trị đó thuộc về một tập tin cấu hình. Nếu bạn cần sử dụng một giá trị ở nhiều nơi trong mã của bạn, giá trị đó cũng thuộc về một tập tin cấu hình. Chỉ cần tự hỏi câu hỏi này mỗi khi bạn giới thiệu một giá trị mới vào mã: giá trị này có thuộc về tập tin cấu hình không? Câu trả lời có lẽ sẽ là có.
Sử dụng câu lệnh điều kiện và biến tạm thời không cần thiết. Mỗi câu lệnh if là một nhánh logic cần được kiểm tra ít nhất hai lần. Khi bạn có thể tránh câu lệnh điều kiện mà không làm mất tính đọc hiểu, bạn nên làm như vậy. Vấn đề chính ở đây là mở rộng một hàm với logic điều kiện thay vì giới thiệu một hàm mới. Mỗi khi bạn nghĩ rằng bạn cần một câu lệnh if hoặc một biến hàm mới, bạn nên tự hỏi: liệu tôi đang thay đổi mã ở cấp độ phù hợp hay tôi nên nghĩ về vấn đề ở cấp độ cao hơn?
Và về chủ đề câu lệnh if không cần thiết, hãy xem xét đoạn mã này:
Hàm isOdd ở trên có một số vấn đề, nhưng bạn có thể thấy vấn đề rõ ràng nhất là gì không?
Nó sử dụng một câu lệnh if không cần thiết. Đây là một mã tương đương:
Mình sẽ tiếp tục chủ đề này ở bài tiếp theo nhé. Cảm ơn các bạn đã đọc tới đây.
Nếu bạn đang tìm kiếm cho mình một môi trường làm việc cởi mở với sự hỗ trợ tận tình từ các chuyên gia đi trước cũng như cơ hội tham gia vào các dự án thực tiễn thì có thể liên hệ ITBee Solutions để phát triển sự nghiệp lập trình của mình nhé.
_Nguồn: Medium