Tư duy lập trình
Tôi viết bài này từ kinh nghiệm trong một lần review code. khi reviews, tôi thấy đoạn code như sau:
Và tôi thắc mắc rằng lookupEnv là gì? tại sao trong code của một component lại phải suy tư, quan tâm về env, thứ vốn được cung cấp và quản lý bởi buildtime. Với thắc mắc đó, tôi đi tìm nơi mà function này được implement:
Hãy cố hiểu function đơn giản này. Nó nhận vào hai tham số string|undefined
. Và check xem tham số env
có thỏa truthy
hay không.
Nếu thỏa thì bạn trả về env
, nếu không thỏa thì bạn console.warn. Nhưng bạn cũng biết rằng bạn không được phép làm như vậy, nên đã nhanh trí disable eslint ngay.
Rất may cho bạn là người review đoạn code đó của bạn đã không để ý.
Nhìn nó khá là bình thường, nhưng mà tôi biết rằng, process.env.[ENV_NAME]
sẽ bị replace bởi bundler. Vậy nên tôi đoán code thực tế sẽ trông rất kỳ, và đây là code sau khi buld:
Quả là nó kỳ như tôi nghĩ, thay vì truyền vào một variable, thì thực tế nó truyền vào một string, và function cố kiểm tra xem string đó có phải là string hay không. Đến đây, tôi biết đây là một function vô nghĩa, vậy nó phải có dụng ý gì khác.
Tôi trình bày suy nghĩ của mình và yêu cầu bạn remove nó. Đồng thời nhận được lý do mà bạn implement nó.
Như vậy, bài toán mà developer này muốn giải quyết là gì? và cách họ giải quyết thể hiện điều gì về tư duy lập trình?
Sau khi trao đổi với developer này, tôi nhận được lý do mà họ implement như vậy là có hai:
- Để phục vụ type check. Bởi vì
process.env.[env_name]
trả về kiểustring | undefined
. Nên bạn không thể dùng nó trực tiếp mà không check. - Để developer khi run app thì biết được env nào chưa config bằng cách xem trong console.
Hãy đi qua từng lý do một để xem tinh thỏa đáng cũng như lỗ hổng của nó.
Đối với lý do thứ nhất. Vì process.env.[ENV_NAME]
trả về kiểu string | undefined
. Vậy nên developer không thể sử dụng nó mà không check nó trước, TypeScript sẽ báo lỗi. Và để tránh điều đó, developer đã bọc nó vào một function để có thể reuse nó. Mới nghe thì có vẻ hợp lý đấy, nhưng vấn đề là process.env.[ENV_NAME]
không phải là một variable, mà nó là một placeholder, sẽ bị thay thế bởi bundler. Nó không hề tồn tại ở runtime thì làm sao bạn lại cần check nó?
Vậy ở vị trí là developer phải maintain nó, tôi có dễ dàng biết được env nào chưa config hay không? Câu trả lời là không. Bởi vì chỉ khi nào function lookupEnv được gọi, vậy nên nếu tôi chưa test tới function đó, tôi không thể nào biết được nó thiếu.
Nhưng tồi tệ hơn là khi tôi là devops, tôi vận hành website này, nhưng không có nơi nào cho tôi biết project này cần những env nào. Tôi phải đi search trong toàn bộ source code chữ process.env
, đó là nếu tôi biết code, và nếu tôi có quyền truy cập source code. Nếu chỉ có quyền truy cập vào builtcode thì sẽ còn cực hơn nhiều.
Nếu có bỏ sót một env nào đó, sẽ chẳng ai biết được, ngoại trừ khách hàng. Không một lỗi buid-time, vậy không phải lỗi của devops.
Như vậy, để giải quyết một vấn đề đơn giản, người developer này đã đánh một vòng rất thừa thải và vụng về để tạo ra thêm vấn đề mớiơi
Tôi cho rằng nguyên nhân nằm ở tư duy lập trình của developer này, họ không xác định được vấn đề cốt lõi là gì.
Theo tôi, cái cốt lõi của vấn đề là sự thiếu tính tường minh. Chỉ cần bạn làm cho nó tường minh, thì sẽ config cho typescript hoạt động tốt, cũng như người vận hành, người maintain biết rõ về các env cần có.
Làm sao để cho nó tường minh? Nếu hỏi nghĩa là không biết cách sử dụng typescript. Ta chỉ cần declare một file, mà ở đó mọi ENV được define một cách rõ ràng như file env.d.ts
sau:
declare namespace NodeJS {
interface ProcessEnv {
NODE_ENV: string
VUE_ROUTER_MODE: 'hash' | 'history' | 'abstract'
VUE_ROUTER_BASE: string
VUE_APP_CURRENCY_RATES_ENDPOINT: string
}
}
Người vận hành sẽ nhìn vào file này, và check xem trong build environment đã thỏa hết các env chưa, còn người developer thì giờ chỉ cần sử dụng process.env.VUE_ROUTER_MODE
mà không bị lỗi type check nữa.
Để phản biện, ta có thể lập luận rằng, với config như vậy, nếu lúc runtime bị thiếu env, sẽ không cách nào biết được lúc buld time. Không đúng, vì khi bạn nhìn vào file env.d.ts
bạn sẽ thấy mọi env mà bạn cần, nếu thấy thiếu thì nghĩa là thiếu.
Vậy mỗi lần pull code về tôi phải đi check file env hay sao? Vậy theo bạn, việc check một file duy nhất, và việc phải run code, phải mở web và xem console thì đâu là cách đơn giản hơn? Và hãy nghĩ lại, để vận hành code, bạn phải setup đầy đủ những yêu cầu của nó, nếu bạn check file env của bạn và env trong code không khớp, thì đó là lỗi của bạn. Mọi thứ đã được trình bày, nhưng bạn không check, bạn không setup đúng, đó là một yêu cầu tối thiểu.
Nhưng để đẩy mọi chuyện đi xa hơn, ta có cách nào để giải quyết không? Tất nhiên là có. Hãy nhớ rằng code của ta đi qua bundler, nghĩa là ta có quyền can thiệp vào những script dùng để build. Vậy hãy đọc file env.d.ts
và check xem các env khai báo trong đó có tồn tại hay không.
Vậy?
Tư duy lập trình là quan trọng, nó không phải là bạn giải được bao nhiêu thuật toán, mà thông qua việc giải các thuật toán này, bạn rèn luyện tư duy lập trình, khả năng tìm ra được vấn đề, sắp xếp suy nghĩ để giải quyết các vấn đề đó.
Update
Tôi cũng gặp những người làm thuật toán rất tốt, nhưng lại không thể giải quyết các vấn đề khi hiện thực business logic. Học gặp nhiều rắc rối khi cố giải quyết các vấn đề, và loay hoay rất lâu với bất kỳ một yêu cầu nào, và dành rất nhiều thời gian để tiếp nhận những hướng dẫn.