Các lập trình viên thời nay có thể văn minh hơn một chút so với các hiệp sĩ thời trung cổ, song thế giới kỹ thuật đương đại cũng có những “quái vật kỹ thuật” chực chờ ở những nơi không lường trước: những vấn đề khó khăn sát nút hạn chót; những điều phức tạp không có hướng dẫn giải thích rõ ràng; các con quái vật biết cách lẻn vào các lỗi còn phôi thai và trục trặc không đúng lúc, thường ngay sau khi chương trình được chuyển giao.
Sẽ có một số người ngây thơ tự nhủ rằng máy tính hoàn toàn có thể dự liệu, nghiêm chỉnh đưa ra các đáp án đúng. Chẳng khác nào ếch ngồi đáy giếng. Tuy các nhà thiết kế chip, phát triển ngôn ngữ lập trình và hàng triệu lập trình viên khắp nơi hết sức nỗ lực, nhưng vẫn còn có những vấn đề gai góc có thể làm nản chí ngay cả những lập trình viên giỏi nhất.
Dưới đây là 7 trong số các vấn đề xương xẩu nhất có thể gọi là “quái vật” của thế giới lập trình.
Đa luồng (multithreading)
Đây có vẻ là một ý tưởng tốt: Tách chương trình thành các phần độc lập để hệ điều hành chạy chúng như các chương trình nhỏ riêng biệt. Nếu bộ vi xử lý có 4, 6, 8 nhân (lõi) hay nhiều hơn, tại sao không viết chương trình có 4, 6, 8 luồng hay nhiều hơn để huy động đồng thời tất cả các nhân?
Ý tưởng này thực hiện được khi các phần chương trình thực sự tách biệt, không dính dáng gì với nhau. Nhưng khi chúng cần truy xuất cùng biến hoặc ghi dữ liệu vào cùng file, mọi thứ “đi tong”. Một trong các luồng sẽ lấy được dữ liệu trước nhưng bạn không thể đoán được đó là luồng nào.
Do vậy người ta tạo ra nào là “monitor” (đối tượng giám sát), “semaphore” (bảng mã) và các công cụ khác để tổ chức sắp xếp đống lộn xộn đa luồng. Nếu chúng làm việc được thì vấn đề được giải quyết, chỉ có điều làm tăng thêm độ phức tạp và đòi hỏi phải suy tính khi lưu dữ liệu vào một biến. Còn không thì chỉ làm cho mọi thứ càng thêm rối. Dữ liệu không có nghĩa. Tiền biến mất khỏi tài khoản, chỉ còn là những bit trong bộ nhớ. Nhà phát triển phải mất nhiều thời gian để khoá chặn các cụm dữ liệu để chỉ một luồng thao tác trên đó. Điều đó có thể ngăn chặn sự hỗn loạn nhưng lại làm triệt tiêu ưu điểm của việc xử lý đa luồng. Thế thì thà viết chương trình "đơn luồng" còn hơn.
Bao đóng (biểu thức “closure”)
Vào một lúc nào đó, một ai đó cho rằng tham chiếu dữ liệu ngoài hàm là có ích. Cơ chế này làm việc tốt trong các trường hợp đơn giản, nhưng các lập trình viên bắt đầu nhận thấy vấn đề nảy sinh khi hàm truy xuất dữ liệu khác bên ngoài nó, thường được gọi là "biến tự do". Phiên bản nào đúng? Đó là dữ liệu khi hàm được khởi tạo? Hay khi hàm thực sự chạy? Điều này đặc biệt quan trọng đối với JavaScript do có thể có “khoảng trống” ở giữa.
Phương thức “bao đóng” (closure) là một trong những nguồn cơn gây đau đầu nhất cho các lập trình viên JavaScript (và giờ là Java và Swift). Không chỉ những người tay mơ, ngay cả các chuyên gia lão làng cũng không thể phân biệt cái gì đóng và đâu là ranh giới của cái gọi là “bao đóng”.
Cái tên không có nghĩa, việc truy xuất dữ liệu không bị đóng (ngưng) hẳn như đến giờ đóng cửa một quán bar. Ngược lại, dữ liệu vẫn được truy xuất qua một ngõ hẹp trong môi trường dữ liệu-thời gian, một cơ chế dịch chuyển thời gian kỳ lạ như trong các bộ phim viễn tưởng. Vì các tên gọi "Cơ chế truy xuất ngăn xếp phức hợp” (Complex Stack Access Mechanism) hay "Hệ thống xử lý kiểm soát dữ liệu" (Data Control Juggling System) dài quá, nên người dùng luôn cái tên "bao đóng”.
Dữ liệu quá lớn
Khi RAM bắt đầu đầy, mọi thứ bắt đầu chệch choạc. Bất kể bạn đang thực hiện phân tích thống kê mới mẻ hay làm việc với bảng tính Excel cũ rích. Khi dùng hết RAM, máy tính sẽ chuyển sang cái gọi là bộ nhớ ảo trên ổ đĩa cứng chậm rì. Máy sẽ làm mọi thứ chậm lại, vậy còn tốt hơn là “đứng hình” hay ngưng hẳn công việc.
Vấn đề là ổ đĩa cứng làm việc chậm hơn so với bộ nhớ RAM ít nhất 20 hoặc 30 lần, có khi còn chậm hơn. Nếu có tiến trình nào đó khác tìm cách ghi hoặc đọc ổ đĩa, thì mọi thứ trở nên thật tồi tệ vì ổ đĩa cứng chỉ có thể làm mỗi lúc một việc.
Việc kích hoạt bộ nhớ ảo làm trầm trọng thêm các vấn đề tiềm ẩn với phần mềm. Các trục trặc nếu có sẽ xảy ra nhanh hơn vì các luồng bị mắc kẹt trong bộ nhớ ảo trên đĩa cứng chạy chậm hơn nhiều so với các luồng khác. Nếu chương trình viết tốt, kết quả chỉ chậm đi mà thôi. Còn không, các lỗi sẽ nhanh chóng trở thành thảm họa. Đây chỉ là một ví dụ nhỏ.
Đây là một thách thức thật sự cho các lập trình viên phải làm việc với lượng lớn dữ liệu. Ai mà cẩu thả xây dựng cấu trúc dữ liệu lãng phí thì kết cục chương trình sẽ chạy chậm như rùa bò. Nó có thể chạy ổn với một vài tình huống thử nghiệm, nhưng khi làm việc thực sự thì “chết đứng”.
NP-đầy đủ
Bất kỳ ai học ngành máy tính đều biết NP-complete (viết tắt của “nondeterministic polynomial complete) hay NP-đầy đủ, một loại bài toán quyết định. Để tìm hiểu tường tận NP-đầy đủ thường phải mất cả học kỳ, và nhiều sinh viên máy tính sau đó chỉ có ý niệm mơ hồ rằng không ai có thể giải những bài toán này bởi vì chúng quá khó.
Các bài toán NP-đầy đủ thường rất khó, nếu bạn chỉ đơn giản tấn công theo lối “vũ phu” với kỹ thuật “brute force”. Ví dụ như "bài toán người bán hàng", có thể phải mất thời gian theo cấp số nhân khi con đường bán hàng đi qua càng nhiều thành phố. Giải "bài toán xếp ba lô" tìm tập hợp con của các số sao cho gần nhất với một giá trị N nào đó được giải bằng cách thử tất cả các tập con có thể, đó là một số rất lớn. Mọi người sợ hãi những bài toán này bởi vì chúng là ví dụ hoàn hảo của một trong những thứ hù doạ khiếp nhất ở Silicon Valley: thuật toán không thể mở rộng.
Mẹo đó là một số bài toàn NP-đầy đủ có thể giải bằng phương pháp xấp xỉ. Các thuật toán này không hứa hẹn cho giải pháp chính xác, nhưng đến khá gần. Chúng có thể không tìm thấy đường đi tối ưu cho người bán hàng, nhưng có thể cho lời giải chỉ lệch vài phần trăm.
Sự hiện diện của các giải pháp khá tốt này làm cho những con “quái vật” càng huyền bí hơn. Không ai có thể biết chắc là các vấn đề thực sự khó hay dễ nếu bạn sẵn sàng thỏa mãn bằng một câu trả lời đủ tốt.
Bảo mật
Donald Rumsfeld, Bộ trưởng Quốc phòng Mỹ dưới thời chính quyền Bush con, từng nói: "Có những thứ chúng ta biết mình biết. Chúng ta cũng biết những thứ mà mình không biết. Nhưng cũng có những thứ mà chúng ta không biết là mình không biết".
Rumsfeld nói về cuộc chiến tranh ở Iraq, nhưng cũng đúng cho bảo mật máy tính. Những vấn đề lớn nhất là các lỗ hổng mà chúng ta thậm chí không biết là có thể có. Mọi người đều hiểu lý do nên tạo mật khẩu khó đoán, đó là thứ đã biết được biết đến. Nhưng có ai từng được cho biết rằng phần cứng mạng có lớp phần mềm riêng nhúng bên trong? Các khả năng ai đó có thể bỏ qua việc đột nhập hệ điều hành và thay vào đó nhằm vào lớp bí mật này là một ẩn số chưa biết.
Có khả năng loại tấn công đó có lẽ hiện giờ bạn không biết, nhưng còn những loại khác thì sao? Chúng ta chẳng biết được mình có thể gia cố hay không những lỗ hổng mà chúng ta thậm chí không biết về sự tồn tại chúng. Bạn có thể làm cho mật khẩu an toàn, nhưng có những kẽ hở thậm chí bạn không thể hình dung. Đó chính là niềm vui khi làm bảo mật máy tính. Và đối với lập trình, tư duy bảo mật đang ngày càng trở nên quan trọng hơn. Bạn không thể giao phó đống code của mình cho các chuyên gia bảo mật dọn dẹp.
Mã hoá
Mã hóa nghe có vẻ ghê gớm khi đang được đề nghị vào luật tại Mỹ và nhiều nước khác trên thế giới. Vấn đề là đa phần mã hóa được xây dựng trên một mớ bòng bong không chắc chắn. Các căn cứ toán học dựa trên những giả định không chắc chắn, như khó tìm ước số của những số thật lớn hay tính toán chuỗi rời rạc.
Những vấn đề đó có thực sự khó? Không có ai công khai bất kỳ thuật toán nào giải quyết chúng, nhưng điều đó không có nghĩa là không có giải pháp. Nếu bạn tìm ra cách để nghe trộm mọi cuộc đàm thoại hay đột nhập vào bất kỳ ngân hàng nào thì liệu bạn có nhanh nhẩu nói cho cả thế giới biết hay giúp họ bít các lỗ hổng? Hoặc bạn sẽ giữ im lặng?
Thách thức thực sự đó là sử dụng mã hóa trong chương trình của chính chúng ta. Thậm chí nếu chúng ta tin tưởng rằng các thuật toán mã hoá cơ bản là an toàn, thì vẫn còn nhiều việc phải làm với mật khẩu, khóa, và các kết nối. Chỉ cần một sai lầm không bảo vệ mật khẩu, tất cả mọi thứ sẽ mở toang.
Quản lý thông tin định danh
Có một bức biếm hoạ nổi tiếng của The New Yorker ghi: "Trên Internet, không ai biết bạn là một con chó". Nó thậm chí có cả trang Wikipedia riêng.
Tin tốt là việc giấu tên (nặc danh) là tuỳ ý và có thể có ích. Tin xấu là chúng ta chẳng biết làm gì khác ngoài giao tiếp ẩn danh. Một số lập trình viên nói về "xác thực hai yếu tố", nhưng những người thông minh thì nói "xác thực N yếu tố".
Sau mật khẩu và tin nhắn gửi đến điện thoại di động, chưa có thêm cách nào khác thật ổn. Bộ đọc dấu vân tay có vẻ ấn tượng, nhưng trên mạng đầy các hướng dẫn “bẻ khoá”.
Vấn đề này không có ý nghĩa lắm với giới buôn chuyện trên Snapchat hay Reddit, nhưng trang Facebook bị “hack” thì có chút phiền toái. Không có cách nào dễ dàng để kiểm soát thông tin quan trọng như tài sản, tiền bạc, chăm sóc sức khỏe, hoặc khá nhiều thứ khác trong cuộc sống. Những người mê muội Bitcoin thích lảm nhảm về việc blockchain an toàn ra sao, vậy sao đồng tiền này liên tục bị đánh cắp. Chưa có phương pháp nào thật sự kiểm soát được thông tin định danh.
Đo lường độ khó
Trong lập trình, có cách nào đo lường độ khó của một vấn đề? Không ai biết. Chúng ta biết một số vấn đề dễ giải quyết, nhưng để “nói có sách” một vấn đề là khó thì không dễ. NP-đầy đủ chỉ là một phần của nỗ lực nhằm hệ thống hóa độ phức tạp của thuật toán và phân tích dữ liệu. Lý thuyết này hữu ích, nhưng không đủ. Người ta đùa rằng thật khó để biết một vấn đề có khó hay không.
Nguồn: PC World VN