Có rất nhiều tin tuyển dụng kỹ sư phần mềm, nếu không nói là hầu hết, thường yêu cầu các ứng viên có kỹ năng giải quyết vấn đề (problem solving). Điều đó cho thấy kỹ năng giải quyết vấn đề là một trong những kỹ năng quan trọng của các kỹ sư phần mềm và cần được phát triển trong sự nghiệp của bạn để có thể thành công trong lĩnh vực này.
Việc tiên quyết đầu tiên trong kỹ thuật phần mềm là giải quyết vấn đề, sau đó mới đến viết code. Tại sao lại như vậy? Máy tính cần được con người cho biết chính xác những gì phải làm; chúng không thể đưa ra những giả định giống như con người khi được đưa ra những chỉ dẫn mơ hồ. Bên cạnh đó, các kỹ sư phần mềm được giao nhiệm vụ thiết kế các tính năng và ứng dụng thậm chí có thể chưa tồn tại, và đó chính là điểm bắt đầu.
Vì những lý do đó, phần khó nhất của việc trở thành một kỹ sư phần mềm không phải là hiểu các ngôn ngữ lập trình và framework hoặc thậm chí là các thuật toán. Thay vào đó, nó là sự kết nối nhiều giải pháp lại với nhau để tạo ra những phần mềm hữu ích.
Ví dụ về giải quyết vấn đề của software engineer
Nhiệm vụ của các developer rất đa dạng, từ lập trình thuần túy đến thiết kế hệ thống và khắc phục sự cố. Phần lớn thời gian của kỹ sư được dành để gỡ lỗi, tức là phát hiện và sửa các lỗi và lỗi trong code khiến chương trình bị hỏng hoặc hoạt động không như mong muốn. Sử dụng một ngôn ngữ máy tính cũng giống như khi bạn viết bằng ngôn ngữ thông thường trong đó việc hiểu cách sử dụng ngữ pháp vững chắc và xây dựng câu quan trọng hơn việc ghi nhớ toàn bộ từ vựng.
Hầu hết các tính năng trên web và di động mà chúng ta đang dùng và coi các tính năng đó là đương nhiên đều là kết quả của việc giải quyết vấn đề một cách khéo léo đáng kinh ngạc. Chúng ta hãy cùng xem qua ba ví dụ sau:
Ví dụ 1: Công cụ tự động hoàn thành (auto-complete) của Google
Đây là tính năng dự đoán trong thanh tìm kiếm của Google đề xuất các cụm từ tìm kiếm liên quan đến những gì bạn nhập vào. Theo Google, tính năng này giúp giảm khoảng 25% việc nhập liệu. Các bước như sau:
- Đầu tiên, các nhà phát triển phải thiết kế giao diện trải nghiệm người dùng và javascript để cho phép chương trình tự động hoàn thành các đề xuất trong thời gian thực.
- Thứ hai, họ cần một danh sách hợp lý các đề xuất tự động hoàn thành dựa trên những gì người dùng nhập vào. Người dùng có thể nhập theo nghĩa đen bất cứ thứ gì vào thanh tìm kiếm và như vậy có vô số biến thể có thể xảy ra.
- Thứ ba, Google đã phải phát triển một hệ thống để duy trì chức năng này ở backend. Rõ ràng, chức năng tự động hoàn thành đã tạo ra một tải trọng lớn lên hệ thống đến mức Google phải tăng cơ sở hạ tầng của mình lên nhiều lần để hỗ trợ các yêu cầu HTTP và truy vấn dữ liệu được thêm vào.
- Cuối cùng, các kỹ sư đã phải tinh chỉnh giao diện người dùng để tạo ra trải nghiệm được tất cả người sử dụng chấp nhận. Chức năng tự động hoàn thành được cung cấp bởi Google Trend, đưa ra các đề xuất dựa trên các tìm kiếm hàng đầu mà mọi người đã thực hiện. Tuy nhiên, các thuật toán của Google xóa các dự đoán bị coi là xúc phạm, vu khống hoặc khiêu dâm theo chính sách tự động hoàn thành của nó.
Rõ ràng các kỹ sư tại Google đã giải quyết vấn đề rất tốt. Chúng ta chưa nói đến những đoạn code được họ viết bên dưới nhưng rõ ràng họ đã đưa ra một loạt các giải pháp hiệu quả.
Ví dụ 2: Công nghệ nhận dạng hình ảnh của Intuit
Khi công ty phần mềm Intuit tung ra TurboTax, khách hàng có thể nộp thuế đơn giản bằng cách chụp ảnh W-2 (một mẫu đơn khai thuế) trên điện thoại thông minh của họ. Sau đó, phần mềm hỗ trợ AI sẽ đọc thông tin từ ảnh và tự động điền thông tin vào các trường chính xác. Tuy nhiên, các nhà phát triển đã không thể ngờ rằng việc chụp ảnh biểu mẫu thuế trong điều kiện thích hợp để máy có thể hiểu được khó khăn như thế nào.
Trong thực tế, mọi người đã gửi ảnh những mẫu đơn W-2 bị gấp lại hoặc nhàu nát được chụp trên nệm, mặt bàn bếp lộn xộn, trên thảm và thậm chí trong cả nhà nhà vệ sinh, trong điều kiện ánh sáng kém và phàn nàn với công ty rằng phần mềm không hoạt động.
Intuit đã xác định ba điểm cần giải quyết:
- Xác định cách để có được hình ảnh đẹp
- Hướng dẫn máy tính nhận dạng các loại biểu mẫu khác nhau và bố cục tương ứng của chúng
- Hiểu dữ liệu và áp dụng nó vào đúng ngữ cảnh
Các nhà thiết kế đã thêm tính năng phát hiện cạnh, độ tương phản, độ sáng và tiêu điểm vào tính năng chụp ảnh để người dùng có thể chỉnh sửa ảnh của họ trước khi tải chúng lên. Vấn đề đã được giải quyết.
Ví dụ 3: Dự án nhỏ
Bạn làm việc tại một công ty phần mềm nhỏ xây dựng hệ thống tạo báo cáo dựa trên web. Một ngày nọ, một đồng nghiệp mới chưa có nhiều kinh nghiệm của bạn đến xin lời khuyên khi không tìm ra giải pháp để vẽ một cái cây trên trình duyệt lấy thông tin từ cơ sở dữ liệu. Bạn nhanh chóng viết một hàm đệ quy truy cập vào cơ sở dữ liệu mỗi khi một nút được truy cập và nó hoạt động. Tuy nhiên khi bạn áp dụng vào thực tế và với lượng dữ liệu lớn, bạn có thể gặp vấn đề với hiệu suất và nhiều vấn đề khác.
Vậy giải pháp của bạn có phải là một ví dụ về giải quyết vấn đề tốt không? Không, hoàn toàn không. Đó chỉ là những gì nhất thời có trong đầu bạn mà không có được sự phân tích thấu đáo để đừa ra một giải pháp hoản chỉnh ngay từ đầu
Cả ba ví dụ trên cho thấy cách giải quyết vấn đề trong kỹ thuật phần mềm không chỉ là tìm ra cách phù hợp để yêu cầu máy tính làm việc gì đó, mà còn xác định các thông số phù hợp cho những người sử dụng phần mềm để họ có thể hoàn thành mục tiêu của mình.
Trong thực tế, các kỹ sư phần mềm làm việc với nhiều khách hàng, nhiều người trong số họ không biết họ muốn gì hoặc yêu cầu như thế nào. Một kỹ sư phần mềm giỏi biết cách ngoại suy các nhu cầu chưa được đáp ứng và truyền đạt ý tưởng của họ một cách hiệu quả.
Hãy cùng xem cách giải quyết vấn đề hiệu quả là như thế nào trong lĩnh vực phát triển phần mềm và bạn học được gì để cải thiện kỹ năng này cho chính bạn.
Quy trình giải quyết vấn đề trong công nghệ phần mềm
Quy trình giải quyết vấn đề được chia thành bốn bước:
- Hiểu vấn đề
- Lên kế hoạch
- Thực hiện giải pháp
- Nhìn lại những gì bạn đã làm
Phương pháp này là của George Pólya viết trong cuốn sách How to Solve It. Phương pháp giải quyết vấn đề của ông đã được nhiều lập trình viên sử dụng và giảng dạy, từ các giáo sư khoa học máy tính đến các giáo viên phát triển web.
1/ Hiểu vấn đề
Rõ ràng là để có thể giải quyết một vấn đề, trước tiên chúng ta phải hiểu nó. Toàn bộ ứng dụng và kiến trúc có thể bị sai sót và bị tê liệt bởi những hiểu lầm ban đầu về một vấn đề hoặc yêu cầu. Chúng ta bắt đầu tìm hiểu vấn đề khi phân tích các yêu cầu về phần mềm trong đó giải thích cách mọi thứ sẽ hoạt động theo quan điểm của người dùng.
Hiểu nội dung của vấn đề
Những câu hỏi dưới đây được đặt ra để đảm bảo rằng các thông số kỹ thuật của phần mềm được nêu chính xác:
- Các thông số kỹ thuật có đủ chính xác để có thể được lập trình và kiểm tra không?
- Có mối quan hệ rõ ràng giữa đầu vào và đầu ra không?
- Tất cả các trường hợp sử dụng có được xem xét hết không?
- Chúng ta có thể thấy những mâu thuẫn hay va chạm tiềm ẩn của các ràng buộc hoặc mục tiêu không?
- Có những khái niệm hoặc câu tùy tiện cần giải thích không? Các điều khoản của các yêu cầu có thể đo lường được không? Chúng ta có thể nhìn thấy những từ không định lượng được như ‘có lẽ’, ‘thường’, ‘nhanh’, ‘nhiều’, ‘hầu như’, v.v.?
Hiểu các mục tiêu
Đặt câu hỏi về các mục tiêu và kết quả đầu ra của phần mềm để đảm bảo rằng chúng phù hợp, nhưng cũng để làm rõ tất cả các vấn đề mà chúng ta có thể phải đối mặt trong khi thực hiện dự án.
- Mục tiêu nào là bắt buộc và mục tiêu nào là mong muốn?
- Nhiệm vụ quan trọng và nhiệm vụ nào là phụ trợ từ quan điểm kinh doanh?
- Mục tiêu thách thức nhất là gì và điều gì khiến chúng khó đạt được?
- Làm thế nào chúng ta có thể đo lường nếu các mục tiêu đã đạt được thành công?
- Những rủi ro có thể ảnh hưởng đến mục tiêu là gì? Tác động của lỗi hoặc thời gian ngừng hoạt động sẽ như thế nào?
- Làm thế nào các mục tiêu của phần mềm dự kiến sẽ mở rộng hoặc thay đổi?
- Những yếu tố bên ngoài hoặc sự phụ thuộc nào có thể ngăn cản việc đạt được các mục tiêu?
Hiểu dữ liệu
Đặt câu hỏi về các yếu tố đầu vào của phần mềm và tất cả dữ liệu được cung cấp trước hoặc dự kiến sẽ có sẵn để phần mềm hoạt động bình thường.
- Có bất kỳ đầu vào hay dữ liệu chưa sử dụng nào được cung cấp không? Nếu vậy, tại sao nó được cung cấp? Một số dữ liệu có thể được rút ra, tính toán hoặc suy ra không?
- Những giả định nào có thể được thực hiện một cách an toàn về tính đúng đắn và tính toàn vẹn của đầu vào?
- Mức độ đồng thời trong việc truy cập hoặc thay đổi dữ liệu là gì? Ranh giới và khối lượng của các giao dịch là gì?
- Mối quan hệ giữa các phần tử dữ liệu có dễ sử dụng không? Định dạng của dữ liệu có thuận tiện cho việc xử lý không?
- Giá trị của dữ liệu là gì? Các mối đe dọa bảo mật liên quan sẽ được mong đợi là gì?
- Dữ liệu sẽ có sẵn trong quá trình phát triển? Nó có thể được mô phỏng một cách thực tế?
- Kích thước của dữ liệu dự kiến sẽ tăng lên theo thời gian như thế nào? Dữ liệu cũ có thể được xóa?
- Điều gì xảy ra nếu dữ liệu không khả dụng trong một thời gian, do bảo trì hoặc các tình huống khẩn cấp?
- Dữ liệu có thể được phân phối hoặc tập trung không? Nếu không, tai sao không?
Hiểu các điều kiện
Ở đây chúng ta đặt câu hỏi về tất cả các giả định, ràng buộc và điều kiện được chỉ định cho phần mềm, chẳng hạn như xác nhận, quy tắc kinh doanh, chất lượng, khả năng sử dụng, yêu cầu bảo mật và hiệu suất, v.v.
- Các ràng buộc có thực tế không? Tại sao tất cả chúng đều cần thiết? Họ đang che giấu những vấn đề khác?
- Các ràng buộc bổ sung có thể bắt nguồn từ những ràng buộc hiện có (phụ thuộc giữa các chức năng, phụ thuộc bên ngoài, trình tự không thể tránh khỏi của các bước, v.v.) không?
- Có các điều kiện dựa trên các giả định sai hoặc chưa được xác minh (ví dụ: khách hàng có thể nghĩ rằng một tính năng nhất định sẽ dễ dàng hơn hoặc rẻ hơn bằng cách thêm một số hạn chế)?
- Có thể thêm nhiều ràng buộc hơn, ngay cả khi không cần thiết, để đơn giản hóa một số tình huống không?
- Có thể loại bỏ một số ràng buộc bằng cách sửa đổi các quy trình hoặc quy trình làm việc không?
- Có thể có những điều kiện không cần thiết, tự áp đặt được sử dụng ngầm hoặc rõ ràng và có lẽ dựa trên một mô hình tinh thần ngụy biện về một quy trình hoặc chức năng không?
Xây dựng mô hình
Các vấn đề hình học sẽ dễ hiểu và dễ suy luận hơn nếu chúng ta có thể hình dung chúng trong một bản vẽ hoặc mô hình. Các chức năng của phần mềm cũng dễ hiểu và dễ lý giải nếu chúng ta xây dựng các mô hình, wire-frames hay prototypes đơn giản hóa có thể giúp chúng ta hình dung các khía cạnh có liên quan. Điều này có thể mang lại sự tự tin cho những nhiệm vụ khó khăn hoặc quan trọng nhất và sự gần gũi có lợi đối với các vấn đề mà chúng ta sẽ phải giải quyết bằng công việc khó khăn hơn nhiều trong giải pháp thực tế. Mô hình là những người trợ giúp đắc lực để lập luận về các vấn đề và giải pháp trong nhóm kỹ thuật hoặc với tất cả các bên liên quan.
2/ Lập kế hoạch
Khi vấn đề đã được hiểu đúng, chúng ta bước vào giai đoạn cốt lõi của việc giải quyết vấn đề: lập kế hoạch. Đây là giai đoạn mà chúng ta đánh giá và đưa ra các chiến lược giải pháp khác nhau. Đây là lúc để động não và nảy sinh những ý tưởng cho phép chúng ta tạo ra phần mềm chất lượng và đạt được các mục tiêu của dự án. Dưới đây là một số chiến lược có thể sử dụng trong giai đoạn này. Các chiến lược này có thể được sử dụng riêng lẽ hoặc kết hợp
Vấn đề tương tự
Vấn đề tương tự là một chiến lược sử dụng kiến thức từ các vấn đề đã giải trước đó có liên quan chặt chẽ đến vấn đề đang được giải quyết hoặc ít nhất có chung một số điểm chung. Để có thể sử dụng giải pháp đã biết cho một vấn đề liên quan, thường xuyên phải đưa ra các yếu tố phụ trợ có thể điều chỉnh giải pháp đó phù hợp với mục tiêu và nhu cầu của chúng ta.
Ứng dụng trong Kỹ thuật phần mềm: trước khi giải quyết một vấn đề phức tạp, một kỹ sư phần mềm giỏi nên dành thời gian nghiên cứu các giải pháp cho các vấn đề cùng loại. Chúng ta có thể sẽ tìm thấy sách, blog và bài báo thảo luận về các ý tưởng và cách tiếp cận khác nhau, đoạn code, các dự án nguồn mở, các thành phần thương mại, v.v. Ngay cả khi vấn đề của chúng ta mới đến mức không thể sử dụng bất kỳ giải pháp nào mà chúng ta tìm thấy, chúng ta vẫn có thể có thể điều chỉnh một số thuật toán hoặc đoạn code để phục vụ tốt nhu cầu của mình. Trong trường hợp không thể sử dụng bất kỳ thứ gì tương tự, ít nhất chúng ta sẽ có thêm kiến thức về vấn đề và có một thuật ngữ so sánh cho các lựa chọn thiết kế của mình.
Chia nhỏ và kết hợp lại
Để giải quyết một vấn đề phức tạp, chúng ta có thể cố gắng phân tách nó thành những vấn đề khác vừa dễ giải quyết hơn vừa có thể được sử dụng như một bước đệm để đạt được mục tiêu ban đầu.
Ứng dụng trong Kỹ thuật phần mềm: các kỹ sư phần mềm có thể chia các ứng dụng phức tạp thành các thành phần nhỏ, tập trung, sau đó tổng hợp và kết nối để tạo thành một giải pháp phức hợp. Phân tích hướng đối tượng, phân tích chức năng và các design patterns đều là những ví dụ về phân tách các vấn đề và kết hợp lại các giải pháp. Chia nhỏ và kết hợp lại không chỉ hữu ích để chế giải quyết sự phức tạp mà còn tạo điều kiện tái sử dụng bằng cách lấy các thành phần được sử dụng trong một giải pháp trước đó và tổng hợp chúng theo cách khác nhau để giải quyết các vấn đề khác nhau, giống như các mảnh ghép lego.
Không chỉ trong các dự án thực tế mà trong phỏng kỹ sư phần mềm các công ty cũng sẽ tìm hiểu xem liệu bạn có kỹ năng giải quyết vấn đề bằng cách chia nhỏ một vấn đề phức tạp hay không. Chẳng hạn, một số câu hỏi hóc búa nhất mà Google hỏi trong cuộc phỏng vấn tuyển dụng software engineer:
- Hãy ước tính số lượng quả bóng tennis có thể nhét vào một chiếc Boeing 747: Câu hỏi này nghe có vẻ khó tin và không thể thực hiện được. Tuy nhiên người quản lý tuyển dụng muốn hiểu về cách bạn sử dụng những gì bạn đã biết về kích thước của quả bóng tennis và những gì bạn có thể suy luận thông qua phỏng đoán. Họ muốn thấy rằng bạn có thể xâu chuỗi các phần thông tin khác nhau lại với nhau và tạo ra một “thuật toán” để suy ra thêm thông tin.
- Mô tả AdWords cho đứa trẻ 7 tuổi: Bên cạnh việc kiểm tra kiến thức về sản phẩm của bạn, nhà tuyển dụng lắng nghe khả năng của bạn trong việc chia nhỏ các ý tưởng phức tạp hoặc trừu tượng thành các khái niệm đơn giản. Họ cũng đang kiểm tra kỹ năng giao tiếp của bạn qua câu hỏi này
Thay đổi vấn đề
Khi một vấn đề có vẻ quá phức tạp không thể giải quyết được, thay vào tìm cách giải quyết vấn đề đó chúng ta giải quyết một vấn đề bổ sung có nguồn gốc từ nguyên bản thông qua các kiểu thay đổi khác nhau. Mục đích của các thay đổi là để có được một vấn đề khác, đơn giản hơn hoặc quen thuộc hơn, với hy vọng rằng giải pháp của nó có thể giúp chúng ta giải quyết vấn đề ban đầu, hoặc ít nhất là cung cấp một số hiểu biết hữu ích.
Dưới đây là một số dạng biến đổi vấn đề điển hình:
1/ Các yếu tố phụ trợ
Khi chúng ta không biết cách giải quyết một vấn đề nào đó, chúng ta luôn cố gắng giải quyết một vấn đề có liên quan. Thêm các yếu tố phụ trợ có thể giúp bắc cầu giải pháp của vấn đề liên quan với giải pháp của vấn đề khó hơn.
Ứng dụng trong Kỹ thuật phần mềm: kế thừa (Inheritance) là một ví dụ phổ biến về cách chúng ta có thể mở rộng một chức năng cơ bản để thực hiện nhiều tác vụ cụ thể hoặc phức tạp hơn. Áp dụng các design patterns như proxy hoặc decorator cũng là những ví dụ để đạt được một giải pháp phức tạp bằng cách thêm các yếu tố lên trên một giải pháp đơn giản hơn. Ví dụ, chúng ta có thể có được một chức năng an toàn bằng cách triển khai chức năng đơn giản (vấn đề đơn giản hơn) và sau đó gói nó bằng data encryption decorator (phần tử phụ trợ).
2/ Loại bỏ các phần tử
Khi bị mắc kẹt với một chướng ngại vật làm chậm tiến độ, chúng ta có thể giả vờ làm cho chướng ngại vật biến mất bằng một cây đũa thần tưởng tượng. Những người giải quyết vấn đề giỏi sẽ sử dụng hoạt động tinh thần này để ném tâm trí của họ qua chướng ngại vật, để khám phá điều gì sẽ xảy ra tiếp theo nếu những trở ngại đột nhiên được giải quyết. Tinh thần loại bỏ chướng ngại vật có thể buộc bộ não của chúng ta phải lùi lại và xem xét những trở ngại dưới ánh sáng của một bối cảnh rộng lớn hơn.
Ứng dụng trong Kỹ thuật phần mềm: các developer dễ bị ám ảnh bởi những trở ngại chẳng hạn như mức độ tối ưu hóa cao, mà không xem xét những gì họ thực sự đạt được (hoặc mất) là kết quả của nỗ lực của họ. Nếu chúng ta được yêu cầu xây dựng một loại vali dễ mang theo, chúng ta có thể phải vật lộn và dành tất cả thời gian và sức lực để cố gắng có được những vật liệu xây dựng nhẹ và bền. Nhưng nếu chúng ta giả vờ một lúc rằng chúng ta đã có vật liệu nhẹ nhất và bền nhất, thì chúng ta sẽ ngừng bị ám ảnh bởi khía cạnh này và có lẽ chúng ta có thể cho rằng trọng lượng của vali ít nhất sẽ bằng trọng lượng của đồ đạc chứa trong nó. Cuối cùng chúng ta có thể tiếp tục và xem xét các lựa chọn thay thế, chẳng hạn như đặt bánh xe bên dưới hành lý.
3/ Khái quát hóa
Một kế hoạch tham vọng hơn có thể có nhiều cơ hội thành công hơn. Ý tưởng này còn được gọi là Inventor’s Paradox. Với khái quát hóa, thay vì giải quyết một vấn đề cụ thể, chúng ta giải quyết một vấn đề rộng hơn và chung chung hơn mà giải pháp của nó bao gồm lời giải của vấn đề cụ thể mà chúng ta cần giải quyết. Quy nạp toán học là một ví dụ phổ biến của việc giải quyết một vấn đề bằng cách tổng quát hóa nó.
Ứng dụng trong Kỹ thuật phần mềm: trong một số trường hợp, các giải pháp chung có thể đơn giản hơn nhiều. Đó là trường hợp khi tính cụ thể dẫn đến logic có điều kiện lồng nhau, phức tạp và code không ổn định cần được thay đổi thường xuyên và đáng kể để phù hợp với các quy tắc kinh doanh mới, với nguy cơ cao vi phạm điều gì đó. Ví dụ: chúng ta hãy xem xét một công cụ xác thực được triển khai dựa trên hàng trăm tham số cấu hình. Mỗi tham số có thể có các giá trị khả thi khác nhau thể hiện loại quy tắc xác thực nào nên được áp dụng hoặc bỏ qua. Hơn nữa, một số phụ thuộc giữa các quy tắc được xác định: nếu quy tắc xác thực A bị bỏ qua, thì quy tắc B khác cũng nên được bỏ qua, v.v. Nếu chúng ta cố gắng xây dựng một prototype của một giải pháp cụ thể, chỉ thực hiện một tập hợp con nhỏ các quy tắc cấu hình, chúng ta có thể sẽ gặp rất nhiều khó khăn bởi một rừng switches
và if
statement mà chúng ta sẽ phải viết. Để giải quyết, chúng ta có thể sử dụng một phép loại suy (analogy) với firewall mềm có thể xác thực nhiều điều kiện phức tạp bằng cách phân tách (decomposing) vấn đề thành các quy tắc xếp tầng ưu tiên có thể được xác định động (thay vì tất cả các biến thể có thể được hardcode bằng các giá trị cấu hình tĩnh). Sự phức tạp được giảm bớt bằng cách triển khai một công cụ xác thực chung.
4/ Chuyên môn hóa
Trái ngược với khái quát, hoạt động này có thể rất hữu ích khi một vấn đề phức tạp gây ra bởi tính tổng quát của nó và số lượng biến cao, chúng ta có thể cố gắng giải quyết các trường hợp đặc biệt, cực đoan. Điều này là để khám phá ranh giới của vấn đề với hy vọng có thêm kiến thức hoặc một số gợi ý về giải pháp. Nếu may mắn, chúng ta có thể sử dụng lại lời giải của bài toán đặc biệt, hoặc ít nhất là tìm ra liệu chúng ta có đang đi sai hướng hay không.
Ứng dụng trong Kỹ thuật phần mềm: các kỹ sư kiểm thử và QA thường xem xét các đầu vào đặc biệt có thể phá vỡ code hoặc gây ra kết quả không chính xác. Stress testing cũng là một ví dụ về việc xây dựng các trường hợp cực đoan có thể tiết lộ các điểm yếu và tắc nghẽn hiệu suất của một ứng dụng. Khi thiết kế phần mềm, luôn hữu ích khi nghĩ đến các trường hợp đặc biệt tiềm ẩn rủi ro như lỗ hổng bảo mật hoặc các vấn đề tiềm ẩn gây ra trong khi truy cập tài nguyên được chia sẻ. Nói chung, mọi kỹ sư phần mềm giỏi nên xem xét tất cả những thứ có thể xảy ra sai sót, kiểm tra các tác dụng phụ không mong muốn, nguyên nhân gây ra trạng thái không hợp lệ, v.v.
Làm việc ngược lại
Khi chúng ta không có manh mối về cách đạt được giải pháp từ dữ liệu, điều kiện đã cho, chúng ta có thể thử kiểm tra điểm cuối cùng mà chúng ta đã đạt được trong phân tích và truy ngược lại các bước của cho đến khi phát hiện ra một đường dẫn giữa dữ liệu và mục tiêu. Ngược lại, việc tìm ra giải pháp không trực quan và gây ra một số khó khăn về tâm lý, vì chúng ta đặt ra các bước khiến chúng ta rời xa mục tiêu của mình (điểm xuất phát), thay vì hướng chúng ta tới chúng.
Ứng dụng trong Kỹ thuật phần mềm: có rất nhiều bài toán phức tạp có đầu vào rõ ràng và mục tiêu rõ ràng nhưng không có bất kỳ giải pháp xác định rõ ràng nào. Các hệ thống chuyên gia giải quyết vấn đề bằng cách mô phỏng phán đoán của một chuyên gia chủ thể (con người) trong các tình huống khác nhau. Tương tự, việc thiết kế các thuật toán di truyền bắt đầu từ kết quả cuối cùng để xác định hàm mục tiêu nào cần sử dụng. Đối với loại vấn đề kỹ thuật rộng lớn này, việc suy nghĩ ngược lại là một việc làm thường xuyên và đôi khi là lựa chọn duy nhất. Hãy xem xét vấn đề này: đưa ra bất kỳ trang chủ (tập tin HTML) nào của trang web công ty, hãy tìm hình ảnh logo của công ty. Nếu không có giả định về đầu vào, chúng ta có thể không có bất kỳ giải pháp đơn giản nào, vì vậy chúng ta bắt đầu suy nghĩ từ cuối. Kiểm tra nhiều logo công ty được xác định theo cách thủ công (bởi một người trực tiếp), chúng ta có thể thu thập nhiều thuộc tính hữu ích có thể đo lường: thuộc tính hình học (vị trí, kích thước và tỷ lệ), thuộc tính đánh dấu (markup) (tên và thuộc tính của hình ảnh), thuộc tính đồ họa (định dạng, kích thước tập tin, số màu), v.v … Sử dụng phân tích định lượng, chúng ta có thể xây dựng các bước lùi lại để xác định bộ quy tắc có hiệu quả để thu được kết quả đã biết. Cuối cùng, chúng ta lập kế hoạch các bước đầu tiên để trích xuất các thuộc tính cần thiết từ html.
3/Thực hiện giải pháp
Lập một kế hoạch đòi hỏi kỹ năng phân tích, ý tưởng tốt và lập luận kinh nghiệm. Kế hoạch được xây dựng để giải quyết các vấn đề phức tạp. Tất cả các trực giác, giả định và lập luận xác đáng mà chúng ta đã sử dụng trong kế hoạch giờ đây cần được chuyển thể thành phần mềm hoạt động được theo yêu cầu đặt ra. Thực hiện kế hoạch là một công việc tổng hợp, thực hiện nghiêm túc và kỹ lưỡng. Chúng ta cần phải cẩn thận xác minh và chứng minh từng bước mà không để mất mối liên hệ giữa tất cả các bước.
Thực thi từ trên xuống
Thứ tự từ trên xuống rất phù hợp khi đi sâu vào chi tiết của giải pháp. Trước khi giải quyết những khía cạnh nhỏ, trước tiên chúng ta cần tìm ra những khía cạnh chính để đảm bảo rằng chúng hợp lý. Bắt đầu viết code ngay lập tức rất hấp dẫn nhưng cũng đầy rủi ro khi bức tranh lớn mờ nhạt. Do đó, trước tiên chúng ta nên giải quyết tất cả các nghi ngờ quan trọng và xác minh các giả định chính có thể ảnh hưởng đáng kể đến kết quả công việc của chúng ta. Việc lập trình các chi tiết có thể rất tốn thời gian. Sẽ rất tốn kém nếu sau này phát hiện ra rằng đoạn code của chúng ta triển khai một chức năng sai hoặc không mong muốn.
Xác định tầm quan trọng và các thách thức
Rủi ro là một yếu tố quan trọng khác trong việc xác định thứ tự thực hiện. Một số nhiệm vụ quan trọng hơn những nhiệm vụ khác trong bức tranh lớn. Một số nhiệm vụ cũng có thách thức cao hơn so với những nhiệm vụ khác. Nếu một bước vừa quan trọng vừa là thách thức đồng thời, chúng ta nên cố gắng ưu tiên nó do tác động lớn của nó đối với kế hoạch tổng thể.
Phá vỡ sự phụ thuộc
Các bước có thể phụ thuộc vào nhau một cách tự nhiên. Mỗi thành phần phần mềm thường dựa vào những thành phần khác để đạt được mục tiêu của nó. Trong kỹ thuật phần mềm, đôi khi có thể bỏ qua các chi tiết ít liên quan hơn (có thể được giải quyết sau) và tập trung vào các nhiệm vụ ưu tiên cao hơn.
Tổng hợp các nỗ lực
Các giải pháp phần mềm phức tạp có thể được thực hiện bởi nhiều developers và nhiều nhóm khác nhau. Tất cả các nổ lực của các nhà phát triển sẽ được tổng hợp lại để có được một chương trình hoàn chỉnh.
Kiểm tra từ dưới lên
Các kỹ sư chứng minh rằng một giải pháp phần mềm hoạt động thông qua các bước kiểm thử. Kiểm thử thường là một quá trình từ dưới lên bắt đầu bằng cách viết các bài kiểm thử unit test và sau đó chuyển sang các bài kiểm thử chức năng cho các mô-đun, kiểm tra tích hợp, cho đến toàn bộ giải pháp. Ưu điểm của kiểm tra từ dưới lên là nếu thử nghiệm cấp thấp không đạt, chúng ta có thể xác định ngay lỗi đó. Mặt khác, nếu thử nghiệm cấp cao không thành công, chúng ta có thể tập trung vào việc tìm ra các khiếm khuyết trong hệ thống và sự tương tác giữa các thành phần chính. Trên thực tế, các bài kiểm tra cấp thấp nên được tạo càng sớm càng tốt (bởi các nhà phát triển) để tránh việc sửa lỗi tốn kém thời gian và tốn kém, thường liên quan đến các lỗi logic. Các bài kiểm tra viết thường được tự động hóa để đảm bảo tính đúng đắn trước những thay đổi trong tương lai (kiểm thử hồi quy).
Mô phạm và thông thái
Có hai thái độ trái ngược nhau của các kỹ sư phần mềm và đều được áp dụng khá tốt trong bối cảnh của các phương pháp luận phát triển và thiết kế phần mềm hiện đại, theo Pólya.
- Kỹ sư phần mềm phụ thuộc vào bộ công cụ, patterns và các phương pháp thực hành hạn chế. Kiểu kỹ sư như vậy áp dụng nghiêm ngặt các tiêu chuẩn và tuân theo nguyên văn một phương pháp luận.
- Các kỹ sư bậc thầy tập trung vào mục đích của các patterns và phương pháp luận, nắm bắt cơ hội và đánh giá từng trường hợp cụ thể và dùng những công cụ phù hợp nhất với từng tình huống.
4/Nhìn lại những gì bạn đã làm
Giai đoạn cuối cùng của quá trình giải quyết vấn đề là nhìn lại giải pháp đã hoàn thành để mở rộng tiềm năng và củng cố kiến thức của chúng ta. Trong kỹ thuật phần mềm, quá trình này thường bắt đầu với code review và các cuộc họp đúc kết kinh nghiệm. Sau đây là một số mục tiêu chính có thể đạt được khi đánh giá lại toàn bộ quá trình:
Dọn dẹp
Để cải thiện giải pháp đã được thực hiện, cần loại bỏ sự trùng lặp, dư thừa và độ dài của code. Đơn giản và rõ ràng cũng là những mục tiêu quan trọng của giai đoạn dọn dẹp: loại bỏ mã các dead code và các bước không cần thiết, thay thế các thuật toán phức tạp bằng các thuật toán tương đương nhưng đơn giản hơn, chọn tên có ý nghĩa hơn cho các lớp, mô-đun, v.v. Điều này giúp giải pháp trực quan hơn và dễ dàng nhìn thấy nhanh chóng.
Bảo trì và khả năng mở rộng
Các giải pháp phần mềm tốt cần có khả năng đáp ứng với những thay đổi trong tương lai. Do đó, chúng ta cần xem xét liệu việc bảo trì theo dự kiến có phù hợp với kỳ vọng ban đầu hay không. Xem xét lại cũng là giai đoạn thích hợp để khai thác các nút thắt hiệu suất có thể ảnh hưởng đến kế hoạch mở rộng về sau.
Tái sử dụng
Mục tiêu chính của phần mềm mô-đun là có thể tái sử dụng càng nhiều càng tốt các thành phần của nó trong các ngữ cảnh khác nhau. Việc xem xét lại có thể cho thấy các cơ hội để tổng quát hóa hoặc điều chỉnh các phần của giải pháp để sử dụng trong các dự án khác, thực hiện các nhiệm vụ tương tự.
Những điều khác bạn có thể làm để cải thiện kỹ năng giải quyết vấn đề
Bên trên là quy trình giải quyết vấn đề mà các kỹ sư phần mềm nên biết và học hỏi để áp dụng trong công việc của mình. Để tạo những thói quen, nâng cao kỹ năng giải quyết vấn đề bạn bạn nên tìm thêm cơ hội để áp dụng, chẵng hạn:
Tham gia các dự án bên ngoài
Bạn không cần phải giới hạn bản thân trong việc giải quyết các vấn đề trong công việc hàng ngày. Cân nhắc tham gia một dự án mã nguồn mở và xem bạn có thể trợ giúp như thế nào. Điều này cũng sẽ cho phép bạn học hỏi từ các kỹ sư khác bên ngoài và tiếp xúc với nhiều vấn đề hơn.
Thách thức bản thân
Bạn có thể thử thách bản thân thông qua các bài tập viết mã bằng cách sử dụng các trang web như Coderbyte và HackerRank
Xây dựng một cái gì đó mới
Bạn có thể xây dựng một ứng dụng mới và chia sẻ code của mình. Bạn có thể làm điều này một mình hoặc trong một nhóm với các kỹ sư khác. Bằng cách chia sẻ mã nguồn bạn có thể mời các kỹ sư khác nhận xét về giải pháp của bạn, cải thiện hơn nữa kỹ năng giải quyết vấn đề.
Nếu bạn không có ý tưởng về các ứng dụng để xây dựng, hãy xem danh sách này từ Codementor để tìm nguồn cảm hứng.
Nguồn tham khảo cho bài viết
1/https://www.freecodecamp.org/news/how-to-solve-coding-problems/
2/https://www.codeproject.com/Articles/858726/Problem-Solving-for-Software-Engineers
3/https://www.springboard.com/blog/software-engineering/problem-solving-in-software-engineering-an-inside-look/
4/https://levelup.gitconnected.com/how-to-develop-your-problem-solving-skills-7b06fa230e5a
5/ Cover photo: https://betterprogramming.pub/the-problem-solving-survival-kit-ed9c4fb33746