17 điểm bởi GN⁺ 2024-08-04 | 3 bình luận | Chia sẻ qua WhatsApp

"Bảng merchants2 à? Vâng, vì bảng merchants không đủ cột nên chúng tôi tạo merchants2."

  • Khi mới bắt đầu học lập trình, tôi không hề biết rằng người ta có thể kiếm tiền bằng lập trình
  • Tôi đã học được rất nhiều ở công việc phần mềm đầu tiên, và codebase ở đó vừa là thứ tệ nhất vừa là thứ tuyệt nhất

Cơ sở dữ liệu sống mãi

  • Trong các hệ thống legacy, cơ sở dữ liệu không chỉ là nơi lưu dữ liệu đơn thuần. Nó đặt ra các ràng buộc cho toàn bộ hệ thống và là điểm mà mọi đoạn code cùng gặp nhau
  • SQL Server có giới hạn số cột trong một bảng. Khi đó là 1024, hiện nay là 4096. Bảng Merchants thiếu cột nên họ tạo thêm bảng Merchants2 (chứa hơn 500 cột)
  • Merchants và Merchants2 là trung tâm của cả hệ thống. Mọi thứ đều nối về Merchants. Cũng có các bảng đã được chuẩn hóa khác, nhưng chúng có quan hệ khóa ngoại với Merchants

SequenceKey

  • SequenceKey là một bảng đơn giản chỉ có một cột và một giá trị
  • Nó được dùng để tạo ID. Có lẽ nó được tạo ra vì SQL Server khi đó chưa hỗ trợ ID tự tăng
  • Trong mọi stored procedure, hệ thống lấy khóa từ SequenceKey rồi tăng nó lên, sau đó dùng làm ID cho nhiều bảng khác nhau. Nó đóng vai trò như một phép join ngầm

Calendar

  • Calendar là một bảng lịch được nhập thủ công. Nếu Calendar hết hạn thì không thể đăng nhập vào hệ thống
  • Vài năm trước điều đó đã từng xảy ra, và một thực tập sinh đã điền thêm dữ liệu cho 5 năm nữa. Không ai biết hệ thống nào đang dùng nó

Employees

  • Mỗi sáng lúc 7 giờ 15, bảng employees bị xóa rồi được nạp lại từ file CSV nhận từ ADP
  • Trong lúc chạy tác vụ này, không thể đăng nhập vào hệ thống. Đôi khi tác vụ còn bị lỗi
  • Vì dữ liệu phải được sao chép về trụ sở chính, nó được gửi email cho một người để mỗi ngày người đó bấm nút sao chép dữ liệu

Cơ sở dữ liệu thay thế

  • Bạn có thể nghĩ rằng chắc hẳn phải dọn dẹp được cơ sở dữ liệu này. Công ty cũng từng nghĩ vậy
  • Có một bản sao của cơ sở dữ liệu, nhưng dữ liệu bị trễ khoảng 10 phút. Việc đồng bộ chỉ diễn ra theo một chiều
  • Cơ sở dữ liệu này đã được chuẩn hóa, nên để tìm số điện thoại trong merchants phải cần tới 7 phép join

Thành tích bán hàng

  • Mỗi nhân viên kinh doanh đều có một chỉ tiêu hàng tháng gọi là "win" phải đạt được
  • Bảng quản lý việc này cực kỳ phức tạp. Mỗi ngày có một tác vụ chạy để tìm các dòng được thêm/sửa rồi đồng bộ với hệ thống ở trụ sở chính
  • Rắc rối bắt đầu khi một nhân viên kinh doanh yêu cầu chỉnh sửa thủ công một bản ghi
  • Người này đã đạt chỉ tiêu win, rồi có thêm một thương vụ lớn trong tháng đó và muốn chuyển nó sang tháng sau
  • Một thực tập sinh được giao xử lý việc này, rồi tin tức lan ra, khiến số lượng yêu cầu tăng theo cấp số nhân suốt 3 năm
  • Có thời điểm 3 thực tập sinh chỉ ngồi viết câu lệnh SQL cả ngày. Việc làm hẳn một ứng dụng cho việc này bị xem là quá khó

Codebase

  • Codebase đầu tiên tôi tiếp xúc nằm trên Team Foundation Server, một hệ thống quản lý phiên bản tập trung
  • Codebase tôi làm chủ yếu thì một nửa là VB, một nửa là C#. Nó chạy trên IIS và dùng session state ở khắp mọi nơi
  • Điều đó thực sự có nghĩa là nếu bạn vào một trang theo Path A hay Path B thì nội dung bạn thấy trên trang đó có thể rất khác nhau
  • Mọi JavaScript framework từng tồn tại vào thời đó đều được check in vào repository này, thường kèm theo các chỉnh sửa tùy biến mà tác giả cho là cần thiết. Nổi bật nhất là knockout, backbone, marionette, ngoài ra còn có jquery và các plugin của jquery
  • Ngoài codebase này ra còn có khoảng 12 dịch vụ SOAP và nhiều ứng dụng Windows native khác

Ổ cứng của Gilfoyle

  • Gilfoyle nổi tiếng là một lập trình viên nhanh khủng khiếp. Tôi chưa từng gặp anh ấy, nhưng tôi biết anh ấy qua code và qua những đoạn code còn nằm trên ổ cứng của anh ấy
  • Munch vẫn để ổ cứng của Gilfoyle trên bàn trong cấu hình RAID, dù đã nhiều năm kể từ khi anh ấy rời công ty
  • Lý do là vì Gilfoyle nổi tiếng với việc không check in code, mà thay vào đó làm ra những ứng dụng Windows dùng một lần ngẫu nhiên cho một người dùng duy nhất
  • Chuyện người dùng mang đến báo cáo lỗi cho một ứng dụng chỉ tồn tại trên ổ cứng của Gilfoyle không phải là hiếm

Bug vận chuyển

  • Phần lớn công việc của tôi là lần theo các bug mà cả nhóm không muốn phân công cho ai xử lý
  • Có một bug đặc biệt khó chịu xảy ra vài tháng một lần: có những đơn hàng bị kẹt trong hàng đợi vận chuyển sau khi đã giao đi, và hiển thị như thể chưa được giao dù thực tế đã giao rồi
  • Để xử lý, chúng tôi thử nhiều cách khác nhau (script SQL, ứng dụng Windows, v.v.). Tôi được khuyên là đừng truy đến tận gốc nguyên nhân, nhưng tôi không chịu nổi
  • Trong quá trình đó tôi học được cách Gilfoyle suy nghĩ. Ứng dụng vận chuyển sẽ lấy toàn bộ cơ sở dữ liệu, sau đó lọc theo ngày để giữ lại mọi đơn hàng kể từ ngày bắt đầu của ứng dụng
  • Ứng dụng phụ thuộc vào một dịch vụ SOAP, nhưng không phải để làm những việc kiểu service; nó là một pure function. Client mới là nơi tạo ra mọi side effect
  • Trong client đó, tôi phát hiện ra một hệ thống phân cấp class khổng lồ, gồm 120 class với đủ loại method khác nhau, mức kế thừa đi sâu tới 10 tầng
  • Vấn đề duy nhất là mọi method đều rỗng
  • Mục đích của nó là tạo ra một cấu trúc có thể dùng reflection. Reflection đó tạo ra một chuỗi phân tách bằng dấu pipe (cấu trúc dựa trên cơ sở dữ liệu nhưng hoàn toàn tĩnh) rồi gửi nó qua socket
  • Cuối cùng chuỗi này được gửi tới Kewill, một dịch vụ giao tiếp với hãng vận chuyển. Lý do bug xảy ra là vì Kewill tái sử dụng các số có 9 chữ số mỗi tháng, còn ai đó thì đã vô hiệu hóa cron job xóa các đơn hàng cũ

Sự hỗn loạn đẹp đẽ

  • Còn rất nhiều điều để kể về codebase này: như đội ngũ senior developer dành 5 năm để viết lại toàn bộ mà không phát hành dòng code nào, hay các tư vấn viên Red Hat xây một cơ sở dữ liệu duy nhất để kiểm soát mọi thứ
  • Codebase này có rất nhiều góc điên rồ, và có quá nhiều lý do khiến người ta nghĩ phải có cả một đội chuyên trách chỉ để bắt đầu lại một tính năng từ đầu
  • Nhưng câu chuyện quan trọng nhất là Justin đã cải thiện trang Merchants Search
  • Đây là điểm vào của toàn bộ ứng dụng
  • Mọi nhân viên chăm sóc khách hàng đều vừa nói chuyện điện thoại với merchant vừa nhập ID hoặc tên để tìm thông tin. Sau đó họ được đưa đến một trang khổng lồ chứa mọi thông tin
  • Trang này dày đặc thông tin theo cách tốt nhất có thể, chứa mọi thứ cần thiết và mọi liên kết người dùng muốn truy cập. Nhưng nó cực kỳ chậm
  • Justin là senior developer duy nhất trong nhóm của tôi. Anh ấy thông minh, châm biếm, và không mấy quan tâm đến business
  • Anh ấy nói thẳng, không nương tay, và luôn có thể một mình giải quyết vấn đề nhanh hơn các nhóm xung quanh
  • Một ngày nọ, Justin chán ngấy việc phải nghe than phiền rằng trang tìm kiếm merchant quá chậm, nên anh ấy đi sửa nó
  • Mọi khung trên màn hình đều trở thành endpoint riêng. Khi tải trang, mọi thứ phía trên phần nhìn thấy đầu tiên đều bắt đầu được fetch, và khi một phần tải xong thì sẽ có thêm request khác được gửi đi
  • Thời gian tải trang giảm từ vài phút xuống còn chưa đến 1 giây

Hai cách để decouple

  • Lý do Justin có thể làm như vậy là vì codebase này không có master plan nào cả
  • Không có bản thiết kế tổng thể mà hệ thống phải tuân theo, không có định dạng API được kỳ vọng, không có design system được tài liệu hóa, cũng không có hội đồng review kiến trúc để đảm bảo tính nhất quán
  • Ứng dụng là một mớ hỗn độn hoàn toàn. Không ai có thể sửa nó, nên cũng chẳng ai cố nữa. Thay vào đó, chúng tôi tạo ra những thế giới lý trí nhỏ của riêng mình
  • Ứng dụng monolith này, vì nhu cầu thuần túy, đã phát triển ở phần rìa thành một vũ trụ thu nhỏ gồm các ứng dụng nhỏ gọn và tử tế
  • Mỗi người được giao nhiệm vụ cải thiện một phần của ứng dụng cuối cùng đều bỏ cuộc trong việc gỡ rối mạng nhện đó, rồi tìm một góc nhỏ tử tế để xây thứ mới. Sau đó từ từ cập nhật các liên kết để trỏ sang thứ mới tốt hơn, khiến phần cũ dần bị bỏ rơi
  • Nghe có vẻ lộn xộn. Nhưng làm việc trong đó lại vui đến ngạc nhiên. Mối lo về trùng lặp code biến mất, mối lo về tính nhất quán cũng biến mất, và cả mối lo về khả năng mở rộng cũng vậy
  • Code được viết để phục vụ việc sử dụng, chạm vào ít khu vực xung quanh nhất có thể, và đủ dễ thay thế. Code của chúng tôi được decouple, đơn giản là vì việc couple chúng lại còn khó hơn

Sau đó

  • Trong phần còn lại của sự nghiệp, tôi chưa từng có lại đặc quyền được làm việc với một codebase xấu xí mà tuyệt vời đến thế
  • Mọi codebase xấu xí tôi gặp sau này đều không vượt lên được nhu cầu về tính nhất quán
  • Có lẽ vì những developer "nghiêm túc" đã rời bỏ codebase đó từ rất lâu. Những người còn lại chỉ là các thực tập sinh hỗn loạn và developer junior
  • Hoặc có lẽ là vì không có tầng trung gian nào giữa developer và người dùng. Không có phiên dịch, không có thu thập yêu cầu, không có card. Chỉ đơn giản là bạn đứng trước bàn của nhân viên chăm sóc khách hàng và hỏi làm sao để cuộc sống của họ tốt hơn
  • Tôi nhớ sự kết nối trực tiếp đó. Phản hồi nhanh, không cần phải lập những kế hoạch hoành tráng, sự gắn kết giữa bài toán đơn giản và code
  • Có lẽ đó chỉ là nỗi hoài niệm ngây thơ. Nhưng cũng như việc đôi khi tôi muốn quay lại những năm tháng tồi tệ nhất của tuổi trẻ, mỗi khi đối mặt với "enterprise design pattern", tâm trí tôi lại quay về với codebase đẹp đẽ và kinh khủng ấy

Ý kiến của GN⁺

  • Bài viết này có thể mang lại sự đồng cảm và an ủi cho các developer đang phải xử lý hệ thống legacy. Nó cho thấy rằng ngay cả một codebase không hoàn hảo cũng có giá trị và có rất nhiều điều để học
  • Tuy nhiên, không nên lãng mạn hóa các vấn đề của codebase này. Khi technical debt tích tụ, nó làm chậm đáng kể tốc độ phát triển và khiến việc bảo trì trở nên khó khăn hơn. Về lâu dài, chi phí sẽ lớn hơn nhiều
  • Từ bỏ nỗ lực cải thiện codebase rồi tiếp tục chồng thêm các giải pháp tạm thời không phải là đáp án đúng. Cần có chiến lược để cải thiện dần hoặc migrate hệ thống legacy
  • Văn hóa và quy trình phát triển cũng rất quan trọng. Tuân thủ tốt các thực hành kỹ thuật như code review, thiết kế kiến trúc, tài liệu hóa... sẽ giúp tạo ra codebase tốt hơn
  • Việc giao tiếp chặt chẽ với người dùng và nhận phản hồi nhanh là điểm tích cực. Lý tưởng nhất là thúc đẩy điều đó thông qua các phương pháp như Agile, đồng thời vẫn kiểm soát được chất lượng code
  • Cuối cùng, mọi thứ đều là vấn đề cân bằng. Thay vì theo đuổi sự hoàn hảo, điều quan trọng là đáp ứng nhu cầu người dùng theo cách bền vững và quản lý technical debt tốt

3 bình luận

 
bus710 2024-08-07

Ở công việc đầu tiên, nhiệm vụ tôi được giao khi làm lập trình viên firmware là phân tích mã assembly trích xuất từ file hex của MCU 8051 của một sản phẩm không còn lập trình viên lẫn mã nguồn, rồi hiện thực lại bằng C…
May mà vẫn có sản phẩm đang chạy, nên tôi vừa xem mã vừa thử trên sản phẩm và xoay xở kiểu gì đó cũng làm được…
Tôi cũng từng nghe những lời đe dọa kiểu либо phải sửa xong rồi hẵng về, либо chặt ngón tay mà đi
Cũng có lần một lỗi không rõ nguyên nhân hóa ra lại là do thang máy phía sau bức tường nơi thiết bị được lắp đặt
Tôi cũng nhớ đã từng vào hội trường APEC ở đảo Dongbaek, Busan trước khi khai trương chính thức để lắp đặt đủ thứ nữa haha

 
GN⁺ 2024-08-04
Ý kiến Hacker News
  • Ở công ty đầu tiên, đã quản lý một ứng dụng VB phức tạp

    • Có rất nhiều biến toàn cục được tùy biến theo yêu cầu của từng khách hàng
    • Lỗi chỉ không xuất hiện ở chế độ debug, nên đã phải cài Visual Studio cho khách hàng và hướng dẫn họ chạy ở chế độ debug
    • Không có quản lý phiên bản, mã nguồn bị sao chép vào nhiều thư mục nên rất hỗn loạn
    • Khi phát sinh sự cố phía khách hàng thì sửa mã trực tiếp tại hiện trường
    • Không có sự thống nhất về phiên bản cuối cùng, nên mỗi khách hàng dùng một phiên bản khác nhau
  • Ở công việc đầu tiên, đã bảo trì một sản phẩm legacy được viết bằng COBOL và Java

    • Làm việc bằng cách checkout từng file riêng lẻ từ hệ thống quản lý mã nguồn
    • Với mỗi khách hàng đều có một file jar 'master' đại diện cho sản phẩm đã được biên dịch cuối cùng
    • Sau khi sửa mã, chạy script để cập nhật file jar master
    • Toàn bộ codebase không được biên dịch như một khối thống nhất mà bị vá thủ công
    • Codebase phát sinh rất nhiều điểm không nhất quán
    • Mất 2 năm để chuyển sang git
  • Đã refactor một script Perl dài hơn 12.000 dòng

    • Tác giả không biết về mảng nên đã dùng chuỗi để tự triển khai mảng
    • Sau khi refactor, mã giảm xuống còn 200 dòng
  • Đã cảm nhận được sự khác biệt giữa lý thuyết và thực tế

    • Nhiều công ty và dự án trải qua quy trình tương tự
    • Mọi người bàn về phương pháp lý tưởng, nhưng trên thực tế thì làm theo cách nào chạy được là được
  • Codebase của client Android Telegram rất phức tạp

    • File quá lớn đến mức GitHub từ bỏ việc render
    • Toàn bộ việc render và tương tác tin nhắn được xử lý trong một lớp duy nhất
    • Được trao quyền refactor nhưng cuối cùng không thể thực hiện
  • Ở công việc đầu tiên, đã giải quyết vấn đề bộ nhớ trong tác vụ báo cáo

    • Trưởng nhóm và sếp đều rất ngạc nhiên
    • Các lập trình viên khác ghét Linux và muốn chuyển sang .NET
  • Trải nghiệm trực tiếp trao đổi với khách hàng để giải quyết vấn đề là rất tốt

    • Tạo prototype rất nhanh rồi để khách hàng kiểm thử
    • Codebase bừa bộn nhưng hoạt động tốt
  • Đã xây dựng một hệ thống hỗ trợ nhiều thị trường

    • Sao chép hệ thống gốc để tạo phiên bản quốc tế
    • Sau 5 năm, các hệ thống không tách rời mà quấn vào nhau
    • Hệ thống mới và hệ thống cũ bị kết dính chặt chẽ
  • Ở công việc đầu tiên có nhiều người còn thiếu kinh nghiệm

    • Sau khi tích lũy kinh nghiệm thì chuyển sang nơi làm việc tốt hơn
  • Đã giải thích cách xử lý vấn đề trong SQL Server hiện đại

    • Tùy biến theo khách hàng, ID dùng chung, bảng lịch thủ công, phân vùng bảng, replica báo cáo bị trễ, v.v.
    • Codebase pha trộn VB và C# là chuyện phổ biến
    • Có thể sử dụng công cụ chuyển đổi tự động
 
reddiana 2024-08-05

Bảng đánh số (SequenceKey) và bảng ngày làm việc (Calendar)
Nghe mà bồi hồi thật. Giờ thì không biết người ta làm thế nào, nhưng ngày xưa đây là những bảng được dùng rất phổ biến. Làm SI thì phía phần dùng chung cho nghiệp vụ sẽ triển khai các chức năng liên quan.