1 điểm bởi GN⁺ 2024-06-10 | 2 bình luận | Chia sẻ qua WhatsApp
  • Một startup vừa bật tính năng kiếm tiền đã gặp sự cố thanh toán đăng ký, nhưng nội bộ không tái hiện được nên việc xác định nguyên nhân bị trì hoãn 5 ngày
  • Sự cố bắt đầu khi sao chép định dạng chuyển đổi Prisma/TypeScript→Python/SQLAlchemy do ChatGPT tạo ra, khiến một chuỗi ID được hard-code được đưa vào như giá trị mặc định thay vì hàm tạo UUID
  • Do cấu trúc gồm 8 task AWS ECS và mỗi task có 5 instance, người dùng có thể gặp một trong tối đa 40 nhóm ID duy nhất, còn ban ngày vấn đề bị che khuất bởi việc triển khai thường xuyên
  • Vào ban đêm, khi việc triển khai dừng lại, ID duy nhất của từng server bị dùng hết, và các lần thử đăng ký mới sau đó thất bại vì xung đột ID unique
  • Ước tính thiệt hại dựa trên 50 khiếu nại mỗi ngày, 5 ngày, phí đăng ký $40/tháng là $10.000 doanh thu hằng tháng; việc thiếu test, logging, cảnh báo và sao chép code đã khiến ứng phó sự cố trở nên nghiêm trọng hơn

Sự cố đăng ký lộ ra ngay sau khi bật kiếm tiền

  • Startup lần đầu bật kiếm tiền vào tháng 5 và có khách hàng đầu tiên trong vòng 1 giờ sau khi ra mắt
  • Sáng hôm sau, Gmail đã chất đống hơn 40 phản hồi phàn nàn của người dùng
    • Người dùng không thể hoàn tất đăng ký
    • Họ báo rằng khi bấm nút đăng ký, một spinner tải vô hạn xuất hiện
  • Nhóm tự tạo tài khoản mới để kiểm tra, nhưng nội bộ đăng ký vẫn hoạt động bình thường nên không thể tái hiện nguyên nhân
  • Trong giờ làm việc hầu như không có phàn nàn, sự cố chủ yếu tích tụ qua đêm

Triển khai kiếm tiền dưới áp lực thời gian

  • Tháng 5 là thời điểm batch YC S23 bắt đầu, và nhóm chưa chắc chắn sau khi ra mắt thì hướng đi nào là tối ưu
  • Group partner của YC, Dalton, khuyên lấy người đăng ký trả phí làm chỉ số định hướng và tăng gấp đôi mức giá hằng tháng đã nghĩ tới
  • Mức giá cuối cùng được đặt là $40/tháng
  • Dự án ban đầu là full-stack NextJS, nhưng trước và sau công việc kiếm tiền, nhóm tiến hành chuyển sang Python/FastAPI
    • Quá trình chuyển đổi có sử dụng ChatGPT
    • Tích hợp Stripe cũng được hoàn tất
  • Sau đó trong 5 ngày, thời gian ngủ giảm mạnh và mỗi ngày phải xử lý 30–50 email phàn nàn

Định dạng chuyển đổi model do ChatGPT tạo

  • Trong quá trình migration backend, các database model được chuyển từ Prisma/TypeScript sang Python/SQLAlchemy
  • Việc chuyển đổi model khá tẻ nhạt, và nhóm đánh giá ChatGPT làm tốt nên đã dùng nó cho gần như toàn bộ migration
  • Code được tạo ra được copy-paste rồi kiểm tra hoạt động, và trên production cũng trông như bình thường nên tiếp tục sử dụng
  • Khi đó việc insert vào database vẫn do Next API đảm nhiệm, còn backend Python chỉ đọc database
  • Khi triển khai tính năng đăng ký, Python lần đầu bắt đầu insert record vào DB
    • Model SQLAlchemy mới được tự viết, nhưng định dạng do ChatGPT tạo ở các model hiện có được sao chép nguyên xi
    • Cùng một vấn đề đã đi vào cách tạo ID của mọi model

Nguyên nhân thực sự và vì sao ban ngày không thấy

  • Lỗi cốt lõi là không truyền vào hàm hoặc lambda để tạo UUID, mà truyền một chuỗi ID duy nhất được hard-code
  • Khi một người dùng hoàn tất đăng ký bằng ID đó trên một backend instance cụ thể, các lần thử đăng ký sau đó trên cùng instance sẽ gây xung đột ID duy nhất
  • Cấu hình backend đã che giấu vấn đề lâu hơn
    • Chạy 8 ECS task trên AWS
    • Mỗi task chạy 5 backend instance
    • Người dùng về mặt tiềm năng có thể đi tới một trong 40 ID khác nhau
  • Ban ngày, nhóm commit trực tiếp vào nhánh main 10–20 lần mỗi ngày, và mỗi lần đều kích hoạt triển khai backend mới
    • Mỗi lần triển khai diễn ra, lại có 40 ID mới mà khách hàng có thể sử dụng
  • Ban đêm, khi commit và triển khai dừng lại, ID duy nhất của từng server nhanh chóng bị dùng hết
    • Ban đầu còn gần 40 server có thể đăng ký, nhưng theo thời gian con số giảm gần về 0

Quy mô thiệt hại và các biện pháp sau đó

  • Thiệt hại được tính là 50 emails/day x 5 days x $40/month, ước tính mất $10.000 doanh thu hằng tháng
    • Cách tính này chỉ dựa trên những người dùng đã gửi phàn nàn
  • Để tìm ra nguyên nhân cần 5 ngày, vô số email, hàng trăm log Sentry, một cuộc trò chuyện Discord dài với kỹ sư Stripe, và rà soát năm file cốt lõi
  • Sau khi phát hiện nguyên nhân, Adam nhanh chóng đưa bản sửa lên
  • Sau đó nhóm bổ sung unit/integration test mạnh hơn, cảnh báo và logging
  • Sự kiện này cho thấy khi sai sót của con người, test thiếu, sao chép code và push trực tiếp lên main chồng lên nhau, chỉ một dòng nhỏ cũng có thể dẫn tới thiệt hại doanh thu lớn

2 bình luận

 
znjadong 2024-06-11

Ủa, code được AI tự động tạo ra thì nhất định phải review chứ, sao lại dùng nguyên xi như vậy.

 
GN⁺ 2024-06-10
Ý kiến trên Hacker News
  • Thiếu giám sát mới là thứ làm mất 10.000 đô la. Ứng dụng liên tục phát sinh ngoại lệ cơ sở dữ liệu với số lượng lớn, nhưng không ai nhận được cảnh báo
    Nếu có những cảnh báo như vậy thì cuộc điều tra kéo dài 5 ngày đã kết thúc trong 5 phút. Nếu chưa sửa hệ thống cảnh báo thì thực ra vẫn chưa sửa được gì cả

    • Đúng vậy. Thông báo log hẳn đã nói rằng ID không phải là duy nhất, và như thế thời gian debug đã giảm đi rất nhiều
      Lập trình thì dễ khi mọi thứ chạy tốt; phần khó là xử lý khi có vấn đề
    • Những chuyện như thế này ngày càng phổ biến. Các công ty và nhà sáng lập không nghĩ đến hạ tầng, vì tin rằng nhà cung cấp cloud mà họ chọn sẽ làm mọi thứ một cách kỳ diệu
      Ngay từ lúc có khách hàng trả tiền, cần có người có kiến thức và kinh nghiệm xử lý logging, monitoring, cảnh báo, bảo mật, v.v. Không thể làm DevOps kiểu nghiệp dư
    • Không có test cũng tệ, và dùng thứ AI tạo ra mà không kiểm tra ba lần cũng nguy hiểm
      Nhưng việc cơ sở dữ liệu không có ghi log lỗi và cảnh báo mới là phần thật sự điên rồ. Đây không phải code legacy 20 năm tuổi, mà là một sản phẩm mới; cũng không phải code từ thời người ta dùng lỗi DB như một kiểu xác thực dữ liệu
    • Việc deploy xong rồi đi ngủ ngay trông như một tín hiệu nguy hiểm ở đây. Lẽ ra nên deploy khoảng 9 giờ sáng và theo dõi sự cố trong giờ làm việc
    • Có vẻ ChatGPT đã không nhắc rằng cần monitoring
  • Bài blog bị 404 nên để lại link Web Archive
    https://web.archive.org/web/20240610032818/https://asim.bear...
    Tác giả đã thêm một chỉnh sửa quan trọng: các thực hành ở đây rất tệ và đáng xấu hổ, và sau đó họ đã bổ sung unit/integration test vững chắc cùng cảnh báo/logging. Rốt cuộc đây là lỗi của con người, và nhìn lại thì rõ ràng hoàn toàn có thể tránh được
    Tác giả cũng nói thêm rằng chuyện này xảy ra trong vài tuần đầu của công ty dưới áp lực thời gian rất lớn, và mong mọi người xem nó như một câu chuyện hài hước về việc một bug có tính tái hiện rất kỳ lạ trong production

  • Lỗi đã hiện ra ngay. Dù tôn trọng đội ngũ, chuyện này không liên quan mấy đến ChatGPT mà liên quan nhiều hơn đến việc đội đã dùng một mô hình lập trình mà họ chưa đủ quen thuộc
    Ngay cả khi đã qua review code, chỉ cần có những công cụ giám sát có thể thiết lập trong vòng 5 phút thì khả năng cao cũng đã bắt được lỗi

    • Công bằng mà nói, nếu không chủ động nhìn để tìm lỗi này thì có lẽ tôi cũng không thấy. Dù vậy, nói rằng chỉ cần có monitoring hoặc một bài kiểm thử thủ công cơ bản nhất là đã bắt được ngay thì đúng
    • Có vẻ đội này còn không xử lý sự cố cơ bản được từ log cơ sở dữ liệu hay log ứng dụng. Đây là một lỗi đơn giản, và tôi lo không biết họ sẽ làm gì nếu gặp các lỗi tạm thời như khóa ngầm trên bảng
    • Đây không phải một sai sót ngây thơ, mà tiêu đề cố ý clickbait và tối ưu từ khóa. Nó gợi ý như thể ChatGPT đã mắc lỗi để kích thích những cú nhấp vì lo lắng
      Một tiêu đề kiểu “Đã mắc lỗi lập trình khi dùng LLM, và vì không đảm bảo chất lượng nên tốn 10 nghìn đô” sẽ không tạo ra phản ứng kiểu “Nếu ChatGPT phá hỏng thì mức phơi nhiễm của chúng ta là bao nhiêu?” từ ban điều hành. Sẽ có rất nhiều quản lý cấp trung và cấp cao đăng bài này lên LinkedIn
      LLM không thể “mắc lỗi”. Nó không có tính quyết định, không suy luận, không suy nghĩ, cũng không thực hiện logic. Nó là một trình tạo word salad rất hào nhoáng dùng xác suất thống kê, và vì không có gì đảm bảo đầu ra đúng hay chính xác, nên theo định nghĩa dùng từ sai lầm cũng không đúng
      Sửa: Sau khi bài viết bị downvote mạnh vì những lý do hiển nhiên, thứ hạng của nó đột ngột vọt lên, có vẻ nghĩa là moderator đã boost: https://hnrankings.info/40627558/
      Thật buồn cười khi một bài clickbait đáng ra theo quy định phải bị đổi tiêu đề lại được moderator boost. Hơn nữa, việc tác giả có vẻ thuộc một công ty của Y Combinator chắc hẳn cũng hoàn toàn là trùng hợp: https://news.ycombinator.com/item?id=40629998
    • Điều thú vị là một thực thể khác đã tìm ra lỗi này chính là ChatGPT-4o. Không thể chia sẻ đoạn chat có hình ảnh, nhưng khi dán ảnh đoạn code sai vào và hỏi “code này có vấn đề gì”, nó đã cho biết như sau
      Với việc tạo UUID cho khóa chính, không nên dùng str(uuid.uuid4()) mà nên dùng trực tiếp callable uuid.uuid4, và SQLAlchemy sẽ gọi hàm khi tạo giá trị. Nó cũng nói giá trị mặc định cho ngày tháng server_default=text("(now())") có thể không hoạt động như mong đợi nên hãy dùng func.now(), đồng thời kiểm tra import của uuidtext trong SQLAlchemy, và cân nhắc DateTime(timezone=True) để xử lý múi giờ
      Sau đó nó đề xuất code đã sửa là id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4()), unique=True, nullable=False), trong đó việc thêm lambda: đã khắc phục vấn đề
    • Nếu gần như không có kinh nghiệm Python, có lẽ họ đã nghĩ uuid.uuid4() giống như định nghĩa schema ở nơi nào đó như Prisma. Vì vậy bản thân bug này không làm tôi ngạc nhiên, và tôi cũng có thể đã mắc lỗi tương tự
      Dù vậy, chỉ một lần chạy kubectl logs là đã bắt được ngay. Hơn nữa, đang từ Next.js và Prisma mà chuyển sang Python ư? Tại sao?
  • Bản thân sai sót thì có thể hiểu được. Ngay cả khi viết code không dùng ChatGPT, lỗi này trông cũng tương đối dễ bị bỏ qua
    Nhưng tôi không hiểu vì sao sau lần thất bại đầu tiên lại không bị phát hiện. Công ty này không có logging à? Việc backend đang cố tái sử dụng UUID lẽ ra phải lộ rõ ngay khi nhìn lỗi

    • Họ thậm chí còn không biết đã có lỗi cho đến khi khách hàng phàn nàn. Cần biết có lỗi gì xảy ra trước khách hàng, và logging, cảnh báo, hay bất kỳ dạng monitoring nào cũng đều sẽ giúp ích
    • Đây mới là vấn đề thật sự. Với một dự án di chuyển nhanh, khả năng cao sớm muộn cũng sẽ có bug production kiểu này nữa. Chỉ mong lần sau họ không mất 5 ngày để nhận diện
    • Mất 5 ngày để hiểu bug này thì khá là điên rồ
    • Việc các trường hợp đặc biệt như nhiều subscription Stripe bị lọt khỏi unit test thông thường là hoàn toàn có thể xảy ra. Như trong bài nói, có lẽ cũng không dễ tái hiện bằng acceptance test, và cả tác giả lẫn những người khác đều hơi quá mức khi tập trung vào ChatGPT
      Việc vô tình truyền một chuỗi vào một hàm cần một đối tượng callable thay vì String là chuyện thường gặp. Nếu không dùng ORM thì có lẽ đã tránh được vấn đề cụ thể này, nhưng đó có thể chỉ là thiên kiến cá nhân của tôi về ORM. Những bug tương tự hoàn toàn có thể xảy ra cả trong ngữ cảnh không phải cơ sở dữ liệu
      Những người lớn tiếng nói rằng chắc chắn họ đã bắt được bug này hoặc là kỹ sư giỏi hơn tôi rất nhiều, hoặc nhiều khả năng hơn là đang hơi ảo tưởng về năng lực của mình
      Tuy nhiên, việc không có log hoặc không xem log thì thật sự khó hiểu. Nếu là ECS, ngoại lệ Duplicate Key có lẽ đã được đẩy sang CloudWatch mà không cần cấu hình riêng; tôi thắc mắc là chuyện đó không xảy ra, hay có xảy ra nhưng chẳng ai kiểm tra xem qua đêm đã có ngoại lệ gì
    • Vì những thất bại kiểu quy trình này mà Amazon có quy trình sửa lỗi, tức quy trình phân tích hậu sự cố: https://aws.amazon.com/blogs/mt/why-you-should-develop-a-cor...
      Trong tình huống như vậy, việc hỏi vì sao phát hiện chậm và vì sao chẩn đoán mất nhiều thời gian là rất hữu ích
  • Tôi đã thấy cùng một lỗi này nhiều lần ngay cả trong code do con người viết. Đặc biệt trong React / TypeScript / JavaScript, việc ai đó quên lambda xảy ra khá thường xuyên
    Bài blog có cảm giác chưa giải thích đúng nguyên nhân gốc rễ của vấn đề mà đã chuyển ngay sang đổ lỗi cho ChatGPT. Nếu làm gấp, đưa các commit có thay đổi lớn hoặc không được đồng nghiệp review vào main thì chuyện này sẽ xảy ra
    Vấn đề thật sự là: nếu vội vàng, chọn đường tắt, không kiểm thử đầy đủ và không review code với đồng nghiệp thì lỗi sẽ phát sinh. Chỉ cần có test thử nhiều tùy chọn đăng ký khác nhau là có lẽ đã phát hiện ngay

    • Mô hình tư duy của tôi về ChatGPT là một kỹ sư junior vĩnh viễn không được thăng chức. Một người rồi sẽ bị sa thải, nhưng đổi lại có thể gõ nhanh vô hạn, nên nếu dùng thật cẩn thận thì vẫn có ích
      Nếu để một người như vậy ở gần code quan trọng về tài chính, các vấn đề tương tự sẽ xảy ra, và tôi sẽ nghi ngờ phán đoán của người đã quyết định deploy đoạn code đó với gần như không có test
    • Những vấn đề như vậy rất phổ biến ở nhiều nơi. Ví dụ, thuộc tính component của Vue có thể có giá trị mặc định, nhưng nếu dùng object hoặc array literal làm giá trị mặc định thay vì một hàm trả về object hoặc array thì sẽ rất tai hại
      Thật ngạc nhiên là không có lint rule cho trường hợp này
    • ChatGPT chỉ là yếu tố làm phân tâm. Điều quan trọng không phải là thứ gì đã tạo ra code, mà là bạn làm gì với đoạn code đó
    • Cấu trúc là code do ChatGPT viết, push, rồi sau đó cũng do ChatGPT review. /s
      Hy vọng không phải vậy
  • Phần “dự án ban đầu là full-stack NextJS, nhưng trước tiên tôi muốn migration mọi thứ sang Python/FastAPI” khiến tôi mở mắt
    Không hiểu một startup chưa có khách hàng thì biện minh thế nào cho việc viết lại

    • Tôi cũng nghĩ vậy nên tưởng mình điên. Tôi nghĩ việc viết lại sớm như thế là vô lý, nhưng cứ tưởng không ai trong phần bình luận chỉ ra
      Dù có khách hàng hay không, tôi không hiểu vì sao ở giai đoạn sớm như vậy lại làm một chuyển dịch ngang về bản chất từ Node sang Python. Nếu đã có hàng trăm khách hàng và định chuyển sang thứ như Go thì còn có thể hiểu phần nào, nhưng như vậy vẫn đáng nghi
    • Hơn nữa, họ còn đang viết lại bằng một ngôn ngữ mà họ thiếu kinh nghiệm đến mức không nhận ra cả lỗi có vẻ rất hiển nhiên
    • Với tôi, nếu là một dự án thực tế đang làm hiện nay, lý do có thể là C# là một ngôn ngữ tốt, runtime và web framework cũng ổn, nhưng nó làm giảm tốc độ phát triển và các điểm đau cứ tiếp tục tích tụ
      Ví dụ phải tạo cả đống object DTO, nhưng AutoMapper không hoạt động với tổ hợp phiên bản và thiết lập dự án tôi dùng; Entity Framework và việc serialize/deserialize JSON cũng gây đau nhiều hơn lợi
      Tất nhiên có thể giải quyết dần dần. Có thể đào sâu tài liệu, pha thêm hack, nâng cấp package và viết lại cấu hình. Nhưng nếu là con người, bạn sẽ muốn cầm một can xăng ẩn dụ, đốt hết mọi thứ rồi làm hệ thống thứ hai tốt hơn. Tất nhiên thực tế không phải nó sẽ tốt hơn, mà chỉ phát sinh những điểm đau khác, và có khi còn không làm được đầy đủ hoặc làm đúng những việc hệ thống đầu tiên từng làm
      Ở chỗ làm cũng vậy, mỗi khi nhìn thấy legacy hoặc một hệ thống phiền phức, tôi lại có cùng thôi thúc đó. Cần nỗ lực chủ động và liên tục để thắng bộ não đang gào lên đòi viết lại. Đôi khi các thay đổi kiến trúc như viết lại hoặc đưa container vào có kết quả tốt, nhưng thường thì hoặc lao vào biển lửa, hoặc kéo theo công việc bất tận
      Trừ khi có mức độ tin tưởng cao rằng nó sẽ cải thiện việc vận hành hệ thống hoặc trải nghiệm phát triển của các developer khác, thật may là không chiều theo thôi thúc đó
    • Thất bại duy nhất của ChatGPT trong chuyện này là, vì năng lực của nó, nó khiến người ta nhầm tưởng rằng việc viết lại trước khi ra mắt là một việc hợp lý và dễ dàng để tiêu tốn runway
  • ChatGPT thực ra là phía đã giúp ứng dụng kiếm được tiền. Vì nếu không có ChatGPT thì họ không có khả năng triển khai
    Việc thiếu năng lực coding, debugging, logging và monitoring mới là thứ làm bay 10 nghìn đô la; còn trong câu chuyện này, tác động ròng của ChatGPT là dương

    • Nhìn các project của đội này trên github.com/reworkd là thấy ngay độ trưởng thành của sản phẩm và team. Đó là phát triển dẫn dắt bằng emoji
      Mọi commit message đều có emoji. Khỉ, chuối, tên lửa, pháo hoa, thứ gì cũng có
    • Nhất là nếu xét chi phí 20 đô la mỗi tháng thì càng đúng
    • 10 nghìn đô la chỉ là tiền lẻ. Elon có thể mất 500 tỷ đô la vì lỗi xAI hôm nay
      https://grook.ai/share?id=e269e88a7b1a71eff4f176c864b30161&x...
    • Có vẻ họ vẫn có năng lực, vì phần triển khai đã có sẵn rồi
      Nghe nói ban đầu là full-stack NextJS, và trong lúc migration backend sang Python/FastAPI, họ đang dịch mô hình database của Prisma/Typescript sang Python/SQLAlchemy. Nội dung là công việc này nhàm chán, và thấy ChatGPT làm khá tốt nên đã dùng nó cho gần như toàn bộ migration
      Nếu ngay từ đầu không có ChatGPT thì có lẽ họ đã không thử làm migration trước này, nên khó nói tác động ròng là dương. Stack cũ có thể đã có logging lỗi tốt hơn, cũng có thể không; và vì là code tự viết nên họ có thể hiểu cấu trúc rõ hơn, khiến nhu cầu ít hơn
      Bản thân quyết định “viết lại toàn bộ code lần thứ hai” trước khi bật kiếm tiền cũng khá thú vị
  • Có câu: “Trước hết tôi muốn nói rằng các thực hành ở đây là tệ và lẽ ra có thể tránh được. Chuyện này xảy ra trong một giai đoạn khác, khi có áp lực thời gian lớn. Xin hãy đọc với điều đó trong đầu”
    Chính những ràng buộc như thế này khiến subscription phần mềm trở nên đáng sợ

    • Tôi rất tôn trọng việc tác giả công khai câu chuyện này, đặc biệt là việc đặt lời mở đầu như vậy. Biết người khác mắc những sai lầm nào thật sự hữu ích, nhưng công khai lỗi do chính mình gây ra có thể khá xấu hổ
    • Dù có ghét subscription, thế giới ngày xưa khi phải mua license vài trăm đô la cho mỗi user seat cũng không phải tuyệt vời
    • Tôi từng xử lý code subscription legacy, và nó có thể khá bừa bộn
      Có lần do race condition mà user bị tính phí hai lần. Vì vậy khi thấy timeout hoặc lỗi liên quan đến tiền, tôi trở nên hoang tưởng đến mức trước tiên giả định rằng thanh toán đã được thực hiện, rồi sau đó kiểm tra lại
    • Phương án thay thế là tự viết, nhưng như vậy thì mọi thứ khác đều bị ràng buộc
    • Việc viết lại trong lúc chịu áp lực thời gian “lớn” cũng là điều bất thường
  • Code viết bằng TypeScript và Python, framework như Next.js, chạy 5 instance cho mỗi trong 8 task AWS mà doanh thu chỉ 40 USD, thời gian phát triển chỉ vài tuần thôi sao?
    Thật không hiểu chuyện gì đang xảy ra. Họ sửa lại rằng do hạn chế thời gian nên code rất lộn xộn, nhưng tệ hơn là họ lại dành thời gian cho việc refactor giữa các ngôn ngữ và dựng hệ thống phân tán chẳng vì lý do gì
    Đây là kiểu độ phức tạp tự hại mình, vừa phải tung hứng tính năng vừa phải gánh độ phức tạp kỹ thuật vô lý. Không hiểu họ đã nghĩ gì
    Sửa: Đây là công ty YC mùa hè 2023, nhưng đến mùa hè 2024 có vẻ sản phẩm vẫn nằm sau danh sách chờ. Có lẽ vì họ đang viết lại bằng Rust

    • Vì họ có 500.000 USD vốn ban đầu, thêm 1,2 triệu USD nữa ở trên đó, và cả AWS credit miễn phí để đốt
  • Có vẻ người này chưa viết nổi 1000 dòng Python trong đời, nhưng đã chỉ ra đúng vấn đề
    Python có một khiếm khuyết là không sao chép đúng chiến lược đánh giá của Common Lisp. Nếu trong biểu thức giá trị mặc định của tham số hàm tùy chọn có đối số kiểu foo=obj.whatever(), thì obj.whatever() được đánh giá tại thời điểm phần định nghĩa hàm được xử lý, chứ không phải lúc gọi hàm
    Tôi nghi là họ cố ý làm vậy vì hiệu năng. Python còn một khiếm khuyết khác: không có cú pháp literal thật sự cho các đối tượng phổ biến như list. [1, 2, 3] không phải literal mà gần với constructor hơn; mỗi lần được đánh giá, nó phải tạo list mới và điền giá trị vào
    Nhà thiết kế hẳn không muốn một tham số như list=[] tạo một list rỗng mới mỗi lần đối số bị bỏ qua. Trong Lisp, '(1 2 3)'() là literal thật sự, và mỗi lần được tham chiếu đều trỏ tới cùng một đối tượng. Lập trình viên có thể chọn dùng (list 1 2 3) hay '(1 2 3) làm biểu thức giá trị mặc định
    Cái trước tạo một đối tượng mới có thể thay đổi mỗi lần, giống [1, 2, 3]; cái sau gần như chắc chắn trả về cùng một đối tượng và không thể sửa đổi một cách đáng tin cậy, có tính di động. Các ngôn ngữ phổ biến hiện đại đã có hầu hết tính năng của Lisp, nên nghe như câu đùa rằng chẳng có gì để mất

    • Nói thì đúng, nhưng chuyện xảy ra trong bài blog không phải vậy. Vấn đề nằm trong định nghĩa lớp, không phải định nghĩa hàm
    • Có vẻ không thể đúng khi nói rằng trong foo=obj.whatever(), obj.whatever() được đánh giá tại thời điểm xử lý định nghĩa hàm chứ không phải lúc gọi hàm
      Không rõ sẽ thế nào nếu .whatever() phụ thuộc vào trạng thái nội bộ thay đổi sau khi khởi tạo đối tượng